Redesign main menu
This commit is contained in:
parent
220db8a867
commit
fdfa8d21bf
@ -93,6 +93,10 @@ function Padding:getInnerHeight()
|
|||||||
return self.element:getHeight()
|
return self.element:getHeight()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Padding:getInnerElement()
|
||||||
|
return self.element
|
||||||
|
end
|
||||||
|
|
||||||
function Padding:getWidth()
|
function Padding:getWidth()
|
||||||
return self:getInnerWidth() + self:getPaddingLeft() + self:getPaddingRight()
|
return self:getInnerWidth() + self:getPaddingLeft() + self:getPaddingRight()
|
||||||
end
|
end
|
||||||
|
29
gfx/prop/visibility.lua
Normal file
29
gfx/prop/visibility.lua
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
local Prop = require("gfx.prop")
|
||||||
|
local Visibility = Prop:new{ defaultState = true, uid = "VISIBILITY" }
|
||||||
|
|
||||||
|
Visibility.VISIBLE = true
|
||||||
|
Visibility.INVISIBLE = not Visibility.VISIBLE
|
||||||
|
|
||||||
|
function Visibility:with(elementType)
|
||||||
|
local propSelf = self
|
||||||
|
local defaultDraw = elementType.draw
|
||||||
|
|
||||||
|
function elementType:isVisible()
|
||||||
|
return propSelf:getState(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
function elementType:setVisible(visibility)
|
||||||
|
return propSelf:setState(self, visibility)
|
||||||
|
end
|
||||||
|
|
||||||
|
function elementType:draw()
|
||||||
|
if self:isVisible() then
|
||||||
|
return defaultDraw(self)
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
return elementType
|
||||||
|
end
|
||||||
|
|
||||||
|
return Visibility
|
@ -8,7 +8,9 @@ local Padding = require("gfx.padding")
|
|||||||
local Container = require("gfx.container")
|
local Container = require("gfx.container")
|
||||||
local Element = require("gfx.element")
|
local Element = require("gfx.element")
|
||||||
local Progress = require("gfx.progress")
|
local Progress = require("gfx.progress")
|
||||||
|
local Prop = require("gfx.prop")
|
||||||
local Children = require("gfx.prop.children")
|
local Children = require("gfx.prop.children")
|
||||||
|
local Visibility = require("gfx.prop.visibility")
|
||||||
local Orientation = require("gfx.prop.orientation")
|
local Orientation = require("gfx.prop.orientation")
|
||||||
|
|
||||||
|
|
||||||
@ -260,8 +262,448 @@ end
|
|||||||
|
|
||||||
local PAGES = {
|
local PAGES = {
|
||||||
MAIN = function(state)
|
MAIN = function(state)
|
||||||
local pageState = state:currentPageState({}, state.changingPage)
|
local pageState = state:currentPageState({
|
||||||
|
sortMode = 1,
|
||||||
|
filter = "",
|
||||||
|
displayKeyboard = false,
|
||||||
|
currentPage = 1
|
||||||
|
})
|
||||||
|
|
||||||
|
local function _genSort(func, invert, tiebreaker)
|
||||||
|
return function(a, b)
|
||||||
|
local aRes = func(a)
|
||||||
|
local bRes = func(b)
|
||||||
|
return (aRes == bRes and tiebreaker ~= nil and tiebreaker(a, b)) or ((aRes > bRes) ~= invert)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function sortByCount(invert, tiebreaker)
|
||||||
|
return _genSort(function(v) return v:getItemCount() end, invert, tiebreaker)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function sortByName(invert, tiebreaker)
|
||||||
|
return _genSort(function(v) return v:getSimpleName() end, invert, tiebreaker)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function sortByDamage(invert, tiebreaker)
|
||||||
|
return _genSort(function(v)
|
||||||
|
local damage = v:getDamage()
|
||||||
|
return damage == 0 and 1 or (damage / v:getMaxDamage())
|
||||||
|
end, invert, tiebreaker)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function composeSortFuncs(...)
|
||||||
|
local funcs = {...}
|
||||||
|
|
||||||
|
local compose = nil
|
||||||
|
for i=#funcs,1,-1 do
|
||||||
|
local current = funcs[i]
|
||||||
|
local invert = false
|
||||||
|
if type(current) == "boolean" then
|
||||||
|
invert = current
|
||||||
|
i = i - 1
|
||||||
|
current = funcs[i]
|
||||||
|
end
|
||||||
|
compose = current(invert, compose)
|
||||||
|
end
|
||||||
|
return compose
|
||||||
|
end
|
||||||
|
|
||||||
|
local SORT_MODE = {
|
||||||
|
composeSortFuncs(sortByCount, sortByName, sortByDamage),
|
||||||
|
composeSortFuncs(sortByName, sortByDamage, sortByCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
local function matchFilter(filter, str)
|
||||||
|
if #filter == 0 then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local cur = 1
|
||||||
|
filter = filter:lower()
|
||||||
|
str = str:lower()
|
||||||
|
for i=1,#str do
|
||||||
|
if str:sub(i,1) == filter:sub(cur,1) then
|
||||||
|
cur = cur + 1
|
||||||
|
if cur > #filter then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local storageSatProgress = Progress:new {
|
||||||
|
width = state.width,
|
||||||
|
height = 1
|
||||||
|
-- Percentage of storage slots occupied
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
---- BOTTOM BAR
|
||||||
|
local keyboardButton = Text:new{
|
||||||
|
id = "action_keyboard",
|
||||||
|
text = "KBD"
|
||||||
|
}
|
||||||
|
|
||||||
|
local importButton = Text:new{
|
||||||
|
id = "action_import",
|
||||||
|
text = "IMPORT"
|
||||||
|
}
|
||||||
|
|
||||||
|
local sortButton = Text:new{
|
||||||
|
id = "action_sort"
|
||||||
|
}
|
||||||
|
|
||||||
|
local function tabActionButton(count)
|
||||||
|
local text = count < 0 and ("<"..tostring(-count)) or (tostring(count)..">")
|
||||||
|
return Padding:new{
|
||||||
|
Text:new{ id = tostring(count), text = text }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function tabActionButtonID(index, interval, count, sign)
|
||||||
|
-- | s = 1: i - 1
|
||||||
|
-- k(i, c, s) <|
|
||||||
|
-- | s = -1: c - i
|
||||||
|
|
||||||
|
-- Simple solution (7 operations):
|
||||||
|
-- (c + 1)(1 - s) - 2
|
||||||
|
-- k(i, c, s) = ------------------ + i*s
|
||||||
|
-- 2
|
||||||
|
|
||||||
|
-- (`tabActionButtonID` abbreviated as `tabID`)
|
||||||
|
-- tabID(i, t, c, s) = max(1, k(i, c, s) * t * s)
|
||||||
|
|
||||||
|
-- E.g.
|
||||||
|
-- interval = 5, count = 3, sign = 1
|
||||||
|
-- i=1: tabID(i, interval, count, sign) = max(1, k(1, 2, 1) * 5) * 1 = max(1, (-1 + 1) * 5) = max(1, 0) = 1
|
||||||
|
-- i=2: tabID(i, interval, count, sign) = max(1, k(2, 2, 1) * 5) * 1 = max(1, (-1 + 2) * 5) = max(1, 5) = 5
|
||||||
|
-- i=3: tabID(i, interval, count, sign) = max(1, k(3, 2, 1) * 5) * 1 = max(1, (-1 + 3) * 5) = max(1, 10) = 10
|
||||||
|
|
||||||
|
-- E.g.
|
||||||
|
-- interval = 5, count = 3, sign = -1
|
||||||
|
-- i=1: tabID(i, interval, count, sign) = max(1, k(1, 2, -1) * 5) * (-1) = -max(1, (3 + -1) * 5) = -max(1, 10) = -10
|
||||||
|
-- i=2: tabID(i, interval, count, sign) = max(1, k(2, 2, -1) * 5) * (-1) = -max(1, (3 + -2) * 5) = -max(1, 5) = -5
|
||||||
|
-- i=3: tabID(i, interval, count, sign) = max(1, k(3, 2, -1) * 5) * (-1) = -max(1, (3 + -3) * 5) = -max(1, 0) = -1
|
||||||
|
return math.max(1, (((((count + 1) * (1 - sign)) - 2) / 2) + (sign * index)) * interval) * sign
|
||||||
|
end
|
||||||
|
|
||||||
|
local function tabActionList(interval, count, sign, spacing)
|
||||||
|
local buttons = {}
|
||||||
|
if count > 0 then
|
||||||
|
table.insert(buttons, tabActionButton(tabActionButtonID(1, interval, count, sign)))
|
||||||
|
for i=2,count do
|
||||||
|
table.insert(buttons, Element:new{ width = spacing })
|
||||||
|
table.insert(buttons, tabActionButton(tabActionButtonID(i, interval, count, sign)))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return List:new{
|
||||||
|
[Orientation:getId()] = Orientation.HORIZONTAL,
|
||||||
|
[Children:getId()] = buttons
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local ACTION_COUNT = 3
|
||||||
|
local ACTION_INTERVAL = 5
|
||||||
|
local ACTION_SPACING = 1
|
||||||
|
|
||||||
|
local actions = {
|
||||||
|
tabActionList(ACTION_INTERVAL, ACTION_COUNT, 1, ACTION_SPACING),
|
||||||
|
List:new {
|
||||||
|
[Orientation:getId()] = Orientation.HORIZONTAL,
|
||||||
|
[Children:getId()] = {
|
||||||
|
keyboardButton,
|
||||||
|
Element:new{ width = 1 },
|
||||||
|
importButton,
|
||||||
|
Element:new{ width = 1 },
|
||||||
|
sortButton
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tabActionList(ACTION_INTERVAL, ACTION_COUNT, -1, ACTION_SPACING)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i=#actions+1,1,-1 do
|
||||||
|
table.insert(actions, i, Element:new{ width = 0, height = 0 })
|
||||||
|
end
|
||||||
|
|
||||||
|
local function calculateActionSpaces()
|
||||||
|
local freeSpace = 0
|
||||||
|
for i=2,#actions,2 do
|
||||||
|
freeSpace = freeSpace + actions[i]:getWidth()
|
||||||
|
end
|
||||||
|
local asymmetry = freeSpace % ((#actions - 1) / 2)
|
||||||
|
local part = (freeSpace - asymmetry) / ((#actions + 1) / 2)
|
||||||
|
for i=1,#actions,2 do
|
||||||
|
actions[i]:setWidth((i <= asymmetry and 1 or 0) + part)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local bottomBarList = List:new {
|
||||||
|
[Orientation:getId()] = Orientation.HORIZONTAL,
|
||||||
|
[Children:getId()] = actions
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
local aboveEntries = { storageSatProgress }
|
||||||
|
local belowEntries = { bottomBarList }
|
||||||
|
local entries = {}
|
||||||
|
for _,v in ipairs(aboveEntries) do
|
||||||
|
table.insert(entries, v)
|
||||||
|
end
|
||||||
|
|
||||||
|
local groupEntryListBudget = state.height
|
||||||
|
for _,v in ipairs(aboveEntries) do
|
||||||
|
groupEntryListBudget = groupEntryListBudget - v:getHeight()
|
||||||
|
end
|
||||||
|
for _,v in ipairs(belowEntries) do
|
||||||
|
groupEntryListBudget = groupEntryListBudget - v:getHeight()
|
||||||
|
end
|
||||||
|
|
||||||
|
local GroupEntryID = {
|
||||||
|
PADDING = "padding",
|
||||||
|
NAME = "name",
|
||||||
|
COUNT = "count"
|
||||||
|
}
|
||||||
|
|
||||||
|
for i=1,groupEntryListBudget do
|
||||||
|
table.insert(
|
||||||
|
entries,
|
||||||
|
Prop.attach(
|
||||||
|
List:new{
|
||||||
|
id = tostring(i),
|
||||||
|
[Orientation:getId()] = Orientation.HORIZONTAL,
|
||||||
|
[Children:getId()] = {
|
||||||
|
Padding:new{
|
||||||
|
id = GroupEntryID.PADDING,
|
||||||
|
element = Text:new{ id = GroupEntryID.NAME }
|
||||||
|
},
|
||||||
|
Text:new{
|
||||||
|
id = GroupEntryID.COUNT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Visibility,
|
||||||
|
Visibility.INVISIBLE
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
for _,v in ipairs(belowEntries) do
|
||||||
|
table.insert(entries, v)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
local mainList = List:new{
|
||||||
|
[Orientation:getId()] = Orientation.VERTICAL,
|
||||||
|
[Children:getId()] = entries
|
||||||
|
}
|
||||||
|
|
||||||
|
local KEY_BACKSPACE = "backspace"
|
||||||
|
local ID_FILTER_DISPLAY = "display_filter"
|
||||||
|
local keyboardLines = {
|
||||||
|
lines = {
|
||||||
|
{ backspace = true, "1234567890" },
|
||||||
|
{ "qwertyuiop" },
|
||||||
|
{ "asdfghjkl" },
|
||||||
|
{ "zxcvbnm_:" }
|
||||||
|
},
|
||||||
|
elements = { }
|
||||||
|
}
|
||||||
|
|
||||||
|
local function charInputKeyList(chars, backspace)
|
||||||
|
local keys = {}
|
||||||
|
for i=1,#chars do
|
||||||
|
local key = chars:sub(i, 1)
|
||||||
|
table.insert(keys, Padding:new{ bgColor = colors.black, right = ((not backspace) and i == #keys and 0) or 1, element = Text:new{
|
||||||
|
id = key,
|
||||||
|
text = key,
|
||||||
|
bgColor = colors.gray
|
||||||
|
}})
|
||||||
|
end
|
||||||
|
if backspace then
|
||||||
|
table.insert(keys, Text:new{
|
||||||
|
id = KEY_BACKSPACE,
|
||||||
|
text = "<--",
|
||||||
|
bgColor = colors.gray
|
||||||
|
})
|
||||||
|
end
|
||||||
|
return List:new{
|
||||||
|
[Orientation:getId()] = Orientation.HORIZONTAL,
|
||||||
|
[Children:getId()] = keys
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local keyboardWidth = 0
|
||||||
|
for _,line in ipairs(keyboardLines.lines) do
|
||||||
|
local keyLineList = charInputKeyList(line[1], line.backspace)
|
||||||
|
keyboardWidth = math.max(keyboardWidth, keyLineList:getWidth())
|
||||||
|
table.insert(keyboardLines, keyLineList)
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(keyboardLines, 1, Text:new{ id = ID_FILTER_DISPLAY, text = "" })
|
||||||
|
|
||||||
|
local keyboardList = List:new{
|
||||||
|
[Orientation:getId()] = Orientation.Vertical,
|
||||||
|
[Children:getId()] = keyboardLines
|
||||||
|
}
|
||||||
|
|
||||||
|
local screenContainer = Container:new{
|
||||||
|
[Children:getId()] = {
|
||||||
|
mainList,
|
||||||
|
Prop.attach(keyboardList, Visibility, pageState.displayKeyboard)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
local function reloadState()
|
||||||
|
-- Enumerate inventory stats
|
||||||
|
local emptyCount = 0
|
||||||
|
local totalCount = 0
|
||||||
|
pageState.stacks = ItemGroup.collectStacks(state.controller:find(function(stack)
|
||||||
|
if stack:isEmpty() then
|
||||||
|
emptyCount = emptyCount + 1
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
totalCount = totalCount + 1
|
||||||
|
local name = stack:getSimpleName()
|
||||||
|
return matchFilter(state.filter, name) or (name:find(":") ~= nil and matchFilter(state.filter, stack:getName()))
|
||||||
|
end
|
||||||
|
end))
|
||||||
|
totalCount = emptyCount + totalCount
|
||||||
|
table.sort(pageState.stacks, SORT_MODE[pageState.sortMode])
|
||||||
|
|
||||||
|
pageState.pages = math.ceil(totalCount / groupEntryListBudget)
|
||||||
|
|
||||||
|
|
||||||
|
-- Set dynamic states for elements
|
||||||
|
sortButton:setText("<"..tostring(state.sortMode)..">")
|
||||||
|
storageSatProgress:setProgress(1 - (emptyCount / totalCount))
|
||||||
|
|
||||||
|
local basePageIndex = (pageState.currentPage - 1) * groupEntryListBudget
|
||||||
|
local pageEntryCount = math.min(groupEntryListBudget, totalCount - basePageIndex)
|
||||||
|
for i=1,groupEntryListBudget do
|
||||||
|
local isVisible = i <= pageEntryCount
|
||||||
|
local listEntry = mainList:findById(tostring(i))
|
||||||
|
listEntry:setVisible(isVisible)
|
||||||
|
if isVisible then
|
||||||
|
local group = pageState.stacks[basePageIndex + i]
|
||||||
|
|
||||||
|
local nameText = listEntry:findById(GroupEntryID.NAME)
|
||||||
|
local namePadding = listEntry:findById(GroupEntryID.PADDING)
|
||||||
|
local countText = listEntry:findById(GroupEntryID.COUNT)
|
||||||
|
|
||||||
|
countText:setText(tostring(group:getItemCount()))
|
||||||
|
|
||||||
|
local nameTextBudget = mainList:getWidth() - countText:getWidth() - 1
|
||||||
|
local name = fitText(group:getSimpleName(), nameTextBudget)
|
||||||
|
|
||||||
|
nameText:setText(name)
|
||||||
|
namePadding:setPadding{ right = nameTextBudget - #name }
|
||||||
|
listEntry:adjustPositions()
|
||||||
|
listEntry:setOnClick(function()
|
||||||
|
state:setPage("GROUP_DETAIL", group)
|
||||||
|
return true
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
listEntry:setOnClick(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@diagnostic disable-next-line: redefined-local
|
||||||
|
local keyboardWidth = keyboardList:getWidth()
|
||||||
|
keyboardList:setPos{
|
||||||
|
x = math.floor((screenContainer:getWidth() - keyboardWidth) / 2),
|
||||||
|
y = screenContainer:getHeight() - keyboardList:getHeight()
|
||||||
|
}
|
||||||
|
|
||||||
|
local filterDisplayedText = #pageState.filter < keyboardWidth and pageState.filter or pageState.filter:sub(#pageState.filter - keyboardWidth + 1, keyboardWidth)
|
||||||
|
local filterText = keyboardList:findById(ID_FILTER_DISPLAY)
|
||||||
|
filterText:setText(filterDisplayedText)
|
||||||
|
filterText:setX(math.floor((keyboardWidth - #filterDisplayedText) / 2))
|
||||||
|
|
||||||
|
calculateActionSpaces()
|
||||||
|
screenContainer:setDirty(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
for _,line in ipairs(keyboardLines.lines) do
|
||||||
|
for i=1,#line do
|
||||||
|
local key = line:sub(i, 1)
|
||||||
|
keyboardList:findById(key):setOnClick(function()
|
||||||
|
pageState.filter = pageState.filter..key
|
||||||
|
reloadState()
|
||||||
|
return true
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
keyboardList:findById(KEY_BACKSPACE):setOnClick(function()
|
||||||
|
pageState.filter = fitText(pageState.filter, math.max(0, #pageState.filter - 1))
|
||||||
|
reloadState()
|
||||||
|
return true
|
||||||
|
end)
|
||||||
|
|
||||||
|
local function bindTabActionButtons()
|
||||||
|
local function onClickHandler(change)
|
||||||
|
return function()
|
||||||
|
local newValue = math.max(1, math.min(pageState.currentPage + change, pageState.pages))
|
||||||
|
if newValue ~= pageState.currentPage then
|
||||||
|
pageState.currentPage = newValue
|
||||||
|
reloadState()
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for i=1,ACTION_COUNT do
|
||||||
|
local id = tabActionButtonID(i, ACTION_INTERVAL, ACTION_COUNT, 1)
|
||||||
|
bottomBarList:findById(tostring(id)):setOnClick(onClickHandler(id))
|
||||||
|
end
|
||||||
|
for i=1,ACTION_COUNT do
|
||||||
|
local id = tabActionButtonID(i, ACTION_INTERVAL, ACTION_COUNT, -1)
|
||||||
|
bottomBarList:findById(tostring(id)):setOnClick(onClickHandler(id))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
bindTabActionButtons()
|
||||||
|
|
||||||
|
keyboardButton:setOnClick(function()
|
||||||
|
print("Toggling keyboard...")
|
||||||
|
state.showKeyboard = not state.showKeyboard
|
||||||
|
|
||||||
|
reloadState()
|
||||||
|
return true
|
||||||
|
end)
|
||||||
|
|
||||||
|
importButton:setOnClick(function()
|
||||||
|
print("Importing...")
|
||||||
|
state.node:rescan()
|
||||||
|
-- Safely handle transfers, priming computer for a full reset/rescan in case server stops mid-transaction
|
||||||
|
state:itemTransaction(function()
|
||||||
|
for _,nodeStack in ipairs(state.node:find(function(s) return not s:isEmpty() end)) do
|
||||||
|
if not state.controller:insertStack(nodeStack) then
|
||||||
|
print("Couldn't find a free slot for: "..nodeStack:getSimpleName())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
reloadState()
|
||||||
|
return true
|
||||||
|
end)
|
||||||
|
|
||||||
|
sortButton:setOnClick(function()
|
||||||
|
print("Reorganizing...")
|
||||||
|
state.sortMode = (state.sortMode + 1 + #SORT_MODE) % #SORT_MODE
|
||||||
|
reloadState()
|
||||||
|
return true
|
||||||
|
end)
|
||||||
|
|
||||||
|
reloadState()
|
||||||
|
|
||||||
|
return renderDefault(state, screenContainer)
|
||||||
|
--[[
|
||||||
if pageState.stacks == nil then
|
if pageState.stacks == nil then
|
||||||
pageState.stacks = ItemGroup.collectStacks(state.controller:find(function(stack)
|
pageState.stacks = ItemGroup.collectStacks(state.controller:find(function(stack)
|
||||||
return not stack:isEmpty()
|
return not stack:isEmpty()
|
||||||
@ -305,6 +747,7 @@ local PAGES = {
|
|||||||
pageState.listState = listState
|
pageState.listState = listState
|
||||||
|
|
||||||
return renderDefault(state, listResult)
|
return renderDefault(state, listResult)
|
||||||
|
]]--
|
||||||
end,
|
end,
|
||||||
|
|
||||||
GROUP_DETAIL = function(state)
|
GROUP_DETAIL = function(state)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user