-
Notifications
You must be signed in to change notification settings - Fork 104
feat: standardized stream pages #7113
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ElectricalBoy
wants to merge
35
commits into
main
Choose a base branch
from
streampage
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
35 commits
Select commit
Hold shift + click to select a range
f9af617
add skeleton impl
ElectricalBoy 9a7e5e5
add recent matches
ElectricalBoy e1d8eb3
cleanup
ElectricalBoy 8b714f1
move
ElectricalBoy 9a75277
css
ElectricalBoy d96273b
type annotation
ElectricalBoy 8b75999
add team version
ElectricalBoy 55cd42c
remove unused imports
ElectricalBoy aa7f61e
annotations
ElectricalBoy 7cd1c26
basic starcraft impl
ElectricalBoy c657f40
copy from live
ElectricalBoy 5353742
type annotations
ElectricalBoy 14011b0
use widget
ElectricalBoy f8ce06f
backport css change to team version
ElectricalBoy 52f6256
add custom to sc(2)
ElectricalBoy 6544e96
add custom to LoL
ElectricalBoy 924c4fe
lint
ElectricalBoy a31fab0
feedback from review
ElectricalBoy e33e985
remove unused import
ElectricalBoy df87f4c
add gap
ElectricalBoy 5ae5057
use table2
ElectricalBoy 334dd77
remove unused import
ElectricalBoy 33c05cd
feedback from review
ElectricalBoy 5946589
simplify faction check
ElectricalBoy 0da922b
use formatPercentage
ElectricalBoy 93c83da
use title param
ElectricalBoy 3a57090
remove font-size css
ElectricalBoy 701573a
suppress name display for solo opponent
ElectricalBoy 72d1809
add option for suppressing map table
ElectricalBoy b757cf9
rename as suggested
ElectricalBoy fba0b30
tabs
ElectricalBoy 5f95ab8
adjust display
ElectricalBoy 8de7534
use compact date
ElectricalBoy 58c2176
code conciseness
ElectricalBoy 32e6313
remove unused import
ElectricalBoy File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,306 @@ | ||
| --- | ||
| -- @Liquipedia | ||
| -- page=Module:StreamPage/Base | ||
| -- | ||
| -- Please see https://github.com/Liquipedia/Lua-Modules to contribute | ||
| -- | ||
|
|
||
| local Lua = require('Module:Lua') | ||
|
|
||
| local Arguments = Lua.import('Module:Arguments') | ||
| local Array = Lua.import('Module:Array') | ||
| local Class = Lua.import('Module:Class') | ||
| local Countdown = Lua.import('Module:Countdown') | ||
| local DateExt = Lua.import('Module:Date/Ext') | ||
| local HighlightConditions = Lua.import('Module:HighlightConditions') | ||
| local Logic = Lua.import('Module:Logic') | ||
| local Lpdb = Lua.import('Module:Lpdb') | ||
| local MatchGroupUtil = Lua.import('Module:MatchGroup/Util/Custom') | ||
| local MatchPage = Lua.requireIfExists('Module:MatchPage') | ||
| local MatchTable = Lua.import('Module:MatchTable') | ||
| local MatchTicker = Lua.import('Module:MatchTicker') | ||
| local Opponent = Lua.import('Module:Opponent/Custom') | ||
| local OpponentDisplay = Lua.import('Module:OpponentDisplay/Custom') | ||
| local Table = Lua.import('Module:Table') | ||
| local TeamTemplate = Lua.import('Module:TeamTemplate') | ||
| local Tournament = Lua.import('Module:Tournament') | ||
|
|
||
| local Condition = Lua.import('Module:Condition') | ||
| local ConditionTree = Condition.Tree | ||
| local ConditionNode = Condition.Node | ||
| local Comparator = Condition.Comparator | ||
| local BooleanOperator = Condition.BooleanOperator | ||
| local ColumnName = Condition.ColumnName | ||
|
|
||
| local GridWidgets = Lua.import('Module:Widget/Grid') | ||
| local HtmlWidgets = Lua.import('Module:Widget/Html/All') | ||
| local IconImage = Lua.import('Module:Widget/Image/Icon/Image') | ||
| local MatchPageAdditionalSection = Lua.import('Module:Widget/Match/Page/AdditionalSection') | ||
| local MatchPageHeader = Lua.import('Module:Widget/Match/Page/Header') | ||
| local WidgetUtil = Lua.import('Module:Widget/Util') | ||
|
|
||
| ---@class BaseStreamPage: BaseClass | ||
| ---@operator call(table): BaseStreamPage | ||
| ---@field channel string | ||
| ---@field provider string | ||
| ---@field suppressBottomContent boolean | ||
| ---@field matches MatchGroupUtilMatch[] | ||
| local StreamPage = Class.new(function (self, args) | ||
| self.channel = assert(Logic.nilIfEmpty(args.channel)) | ||
| self.provider = assert(Logic.nilIfEmpty(args.provider)) | ||
| self.suppressBottomContent = Logic.readBool(args.suppressBottomContent) | ||
| self.matches = {} | ||
|
|
||
| self:_fetchMatches() | ||
| end) | ||
|
|
||
| ---@param frame Frame | ||
| ---@return Widget? | ||
| function StreamPage.run(frame) | ||
| local args = Arguments.getArgs(frame) | ||
| return StreamPage(args):create() | ||
| end | ||
|
|
||
| ---@private | ||
| ---@return ConditionTree | ||
| function StreamPage:_createMatchQueryCondition() | ||
| local conditions = ConditionTree(BooleanOperator.all):add{ | ||
| ConditionTree(BooleanOperator.any):add{ | ||
| ConditionNode(ColumnName(self.provider, 'stream'), Comparator.eq, self.channel), | ||
| ConditionNode(ColumnName(self.provider .. '_en_1', 'stream'), Comparator.eq, self.channel) | ||
| }, | ||
| ConditionNode(ColumnName('finished'), Comparator.eq, 0), | ||
| ConditionNode(ColumnName('date'), Comparator.neq, DateExt.defaultDateTime), | ||
| ConditionNode(ColumnName('dateexact'), Comparator.eq, 1) | ||
| } | ||
|
|
||
| conditions:add(StreamPage:addMatchConditions()) | ||
| return conditions | ||
| end | ||
|
|
||
| ---@private | ||
| function StreamPage:_fetchMatches() | ||
| local conditions = self:_createMatchQueryCondition() | ||
|
|
||
| Lpdb.executeMassQuery('match2', { | ||
| conditions = tostring(conditions), | ||
| order = 'date asc', | ||
| limit = 25, | ||
| }, function (record) | ||
| local match = MatchGroupUtil.matchFromRecord(record) | ||
| if Array.any(match.opponents, Opponent.isBye) then | ||
| return | ||
| elseif #self.matches == 5 then | ||
| return false | ||
| end | ||
| Array.appendWith(self.matches, match) | ||
| end) | ||
| end | ||
|
|
||
| ---@protected | ||
| ---@return AbstractConditionNode|AbstractConditionNode[]? | ||
| function StreamPage:addMatchConditions() | ||
| end | ||
|
|
||
| ---@private | ||
| ---@return Widget | ||
| function StreamPage:_header() | ||
| local match = self.matches[1] | ||
| local countdownBlock = HtmlWidgets.Div{ | ||
| css = { | ||
| display = 'block', | ||
| ['text-align'] = 'center' | ||
| }, | ||
| children = Countdown.create{ | ||
| date = DateExt.toCountdownArg(match.timestamp, match.timezoneId, match.dateIsExact), | ||
| finished = match.finished, | ||
| rawdatetime = Logic.readBool(match.finished), | ||
| } | ||
| } or nil | ||
| local tournament = Tournament.partialTournamentFromMatch(match) | ||
| Array.forEach(match.opponents, function (opponent, opponentIndex) | ||
| if opponent.type ~= Opponent.team then | ||
| return | ||
| end | ||
| ---@cast opponent MatchPageOpponent | ||
| opponent.iconDisplay = OpponentDisplay.InlineTeamContainer{ | ||
| style = 'icon', | ||
| template = opponent.template, | ||
| } | ||
| opponent.teamTemplateData = TeamTemplate.getRaw(opponent.template) | ||
| opponent.seriesDots = {} | ||
| end) | ||
|
|
||
| return MatchPageHeader{ | ||
| countdownBlock = countdownBlock, | ||
| isBestOfOne = match.bestof == 1, | ||
| mvp = match.extradata.mvp, | ||
| opponent1 = match.opponents[1], | ||
| opponent2 = match.opponents[2], | ||
| parent = tournament.pageName, | ||
| phase = MatchGroupUtil.computeMatchPhase(match), | ||
| stream = {}, | ||
| tournamentName = tournament.fullName, | ||
| highlighted = HighlightConditions.tournament(tournament) | ||
| } | ||
| end | ||
|
|
||
| ---@return Widget? | ||
| function StreamPage:create() | ||
| if Logic.isEmpty(self.matches) then | ||
| return | ||
| elseif self.matches[1].bracketData.matchPage then | ||
| return HtmlWidgets.Fragment{children = { | ||
| '__NOTOC__', | ||
| MatchPage(self.matches[1]):render(), | ||
| HtmlWidgets.H3{children = 'Channel Schedule'}, | ||
| self:_createMatchTicker():query():create() | ||
| }} | ||
| end | ||
|
|
||
| return HtmlWidgets.Fragment{children = WidgetUtil.collect( | ||
| '__NOTOC__', | ||
| self:_header(), | ||
| GridWidgets.Container{gridCells = { | ||
| GridWidgets.Cell{ | ||
| cellContent = self:render(), | ||
| xs = 'ignore', | ||
| sm = 'ignore', | ||
| lg = 8, | ||
| xl = 8, | ||
| xxl = 9, | ||
| xxxl = 9, | ||
| }, | ||
| GridWidgets.Cell{ | ||
| cellContent = MatchPageAdditionalSection{ | ||
| header = 'Channel Schedule', | ||
| children = self:_createMatchTicker():query():create():css('width', '100%') | ||
| }, | ||
| xs = 'ignore', | ||
| sm = 'ignore', | ||
| lg = 4, | ||
| xl = 4, | ||
| xxl = 3, | ||
| xxxl = 3, | ||
| } | ||
| }}, | ||
| not self.suppressBottomContent and self:createBottomContent() or nil | ||
| )} | ||
| end | ||
|
|
||
| ---@private | ||
| ---@return MatchTicker | ||
| function StreamPage:_createMatchTicker() | ||
| return MatchTicker{ | ||
| additionalConditions = 'AND (' .. tostring(self:_createMatchQueryCondition()) .. ')', | ||
| limit = 5, | ||
| newStyle = true, | ||
| ongoing = true, | ||
| upcoming = true, | ||
| } | ||
| end | ||
|
|
||
| ---@protected | ||
| ---@return string|Widget|Html|(string|Widget|Html)[]? | ||
| function StreamPage:render() | ||
| end | ||
|
|
||
| ---@protected | ||
| ---@return Widget[]? | ||
| function StreamPage:createBottomContent() | ||
| local match = self.matches[1] | ||
|
|
||
| if Array.all(match.opponents, Opponent.isTbd) then | ||
| return | ||
| end | ||
|
|
||
| local headToHead = self:_buildHeadToHeadMatchTable() | ||
|
|
||
| return WidgetUtil.collect( | ||
| not self.suppressBottomContent and HtmlWidgets.H3{children = 'Match History'} or nil, | ||
| HtmlWidgets.Div{ | ||
| classes = {'match-bm-match-additional'}, | ||
| children = WidgetUtil.collect( | ||
| headToHead and MatchPageAdditionalSection{ | ||
| css = {flex = '2 0 100%'}, | ||
| header = 'Head to Head', | ||
| bodyClasses = {'match-table-wrapper'}, | ||
| children = headToHead, | ||
| } or nil, | ||
| Array.map(match.opponents, function (opponent) | ||
| local matchTable = self:_buildMatchTable(opponent) | ||
| return MatchPageAdditionalSection{ | ||
| header = OpponentDisplay.InlineOpponent{opponent = opponent, teamStyle = 'hybrid'}, | ||
| bodyClasses = matchTable and {'match-table-wrapper'} or nil, | ||
| children = matchTable or IconImage{ | ||
| imageLight = match.icon, | ||
| imageDark = match.iconDark, | ||
| size = '50x32px', | ||
| } | ||
| } | ||
| end) | ||
| ) | ||
| } | ||
| ) | ||
| end | ||
|
|
||
| ---@private | ||
| ---@param props table | ||
| ---@return Html | ||
| function StreamPage:_createMatchTable(props) | ||
| local match = self.matches[1] | ||
| return MatchTable(Table.mergeInto({ | ||
| addCategory = false, | ||
| dateFormat = 'compact', | ||
| edate = match.timestamp - DateExt.daysToSeconds(1) --[[ MatchTable adds 1-day offset to make edate | ||
| inclusive, and we don't want that here ]], | ||
| limit = 5, | ||
| stats = false, | ||
| vod = false, | ||
| matchPageButtonText = 'short', | ||
| }, props)):readConfig():query():buildDisplay() | ||
| end | ||
|
|
||
| ---@private | ||
| ---@param opponent standardOpponent | ||
| ---@return Html? | ||
| function StreamPage:_buildMatchTable(opponent) | ||
| if opponent.type ~= Opponent.solo and opponent.type ~= Opponent.team then | ||
| return | ||
| end | ||
| local base = opponent.type == Opponent.team and Opponent.team or 'player' | ||
| return self:_createMatchTable{ | ||
| ['hide_tier'] = true, | ||
| limit = 5, | ||
| stats = false, | ||
| tableMode = opponent.type, | ||
| [base] = Opponent.toName(opponent), | ||
| useTickerName = true, | ||
| } | ||
| end | ||
|
|
||
| ---@private | ||
| ---@return Html? | ||
| function StreamPage:_buildHeadToHeadMatchTable() | ||
| local match = self.matches[1] | ||
| if Array.any(match.opponents, Opponent.isTbd) then | ||
| return | ||
| elseif match.opponents[1].type ~= match.opponents[2].type then | ||
| return | ||
| elseif match.opponents[1].type ~= Opponent.solo and match.opponents[1].type ~= Opponent.team then | ||
| return | ||
| end | ||
|
|
||
| local base = match.opponents[1].type == Opponent.team and Opponent.team or 'player' | ||
|
|
||
| return self:_createMatchTable{ | ||
| [base] = Opponent.toName(match.opponents[1]), | ||
| ['vs' .. base] = Opponent.toName(match.opponents[2]), | ||
| tableMode = match.opponents[1].type, | ||
| showOpponent = true, | ||
| teamStyle = 'hybrid', | ||
| useTickerName = true, | ||
| } | ||
| end | ||
|
|
||
| return StreamPage | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| --- | ||
| -- @Liquipedia | ||
| -- page=Module:StreamPage/Custom | ||
| -- | ||
| -- Please see https://github.com/Liquipedia/Lua-Modules to contribute | ||
| -- | ||
|
|
||
| local Lua = require('Module:Lua') | ||
|
|
||
| return Lua.import('Module:StreamPage') |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.