From 029d9828ff468e4d8f3bba2fb596fb77465e9470 Mon Sep 17 00:00:00 2001 From: Kostya Bats Date: Sat, 23 Mar 2024 21:04:24 +0300 Subject: [PATCH] horizontal queue experiment --- .run/overlay2-start-local.run.xml | 1 + config/widget_positions.horqueue.json | 1 + .../main/kotlin/org/icpclive/api/Settings.kt | 2 +- src/frontend/generated/api.ts | 1 + .../components/organisms/widgets/Queue.tsx | 62 ++++++++++++++----- src/frontend/overlay/src/config.ts | 3 + 6 files changed, 54 insertions(+), 16 deletions(-) create mode 100644 config/widget_positions.horqueue.json diff --git a/.run/overlay2-start-local.run.xml b/.run/overlay2-start-local.run.xml index 6f57f37d..71751525 100644 --- a/.run/overlay2-start-local.run.xml +++ b/.run/overlay2-start-local.run.xml @@ -8,6 +8,7 @@ + diff --git a/config/widget_positions.horqueue.json b/config/widget_positions.horqueue.json new file mode 100644 index 00000000..b0a656c8 --- /dev/null +++ b/config/widget_positions.horqueue.json @@ -0,0 +1 @@ +{ "queue": { "positionX": 16, "positionY": 832, "sizeX": 1888, "sizeY": 168 } } \ No newline at end of file diff --git a/src/backend-api/src/main/kotlin/org/icpclive/api/Settings.kt b/src/backend-api/src/main/kotlin/org/icpclive/api/Settings.kt index ddc728a8..8cb76081 100644 --- a/src/backend-api/src/main/kotlin/org/icpclive/api/Settings.kt +++ b/src/backend-api/src/main/kotlin/org/icpclive/api/Settings.kt @@ -20,7 +20,7 @@ data class TitleSettings(val preset: String, val data: Map) : Ob data class PictureSettings(val url: String, val name: String) : ObjectSettings @Serializable -class QueueSettings : ObjectSettings +data class QueueSettings(val horizontal: Boolean = false) : ObjectSettings @Serializable data class ScoreboardSettings( diff --git a/src/frontend/generated/api.ts b/src/frontend/generated/api.ts index 1bd8fd42..f7d0128e 100644 --- a/src/frontend/generated/api.ts +++ b/src/frontend/generated/api.ts @@ -523,6 +523,7 @@ export interface PictureSettings { } export interface QueueSettings { + horizontal?: boolean; } export interface ScoreboardSettings { diff --git a/src/frontend/overlay/src/components/organisms/widgets/Queue.tsx b/src/frontend/overlay/src/components/organisms/widgets/Queue.tsx index 3c204376..d9d274f2 100644 --- a/src/frontend/overlay/src/components/organisms/widgets/Queue.tsx +++ b/src/frontend/overlay/src/components/organisms/widgets/Queue.tsx @@ -1,5 +1,5 @@ // import PropTypes from "prop-types"; -import { useState } from "react"; +import { useCallback, useMemo, useState } from "react"; import { Transition, TransitionGroup } from "react-transition-group"; import styled, { css, CSSObject, Keyframes, keyframes } from "styled-components"; import c from "../../../config"; @@ -13,7 +13,7 @@ import star from "../../../assets/icons/star.svg"; import star_mask from "../../../assets/icons/star_mask.svg"; import { formatScore } from "@/services/displayUtils"; import { useAppSelector } from "@/redux/hooks"; -import { RunInfo } from "@shared/api"; +import { RunInfo, Widget } from "@shared/api"; import { isFTS } from "@/utils/statusInfo"; // const MAX_QUEUE_ROWS_COUNT = 20; @@ -21,23 +21,27 @@ import { isFTS } from "@/utils/statusInfo"; // Needed just for positioning and transitions. Don't use for anything else interface QueueRowAnimatorProps { bottom: number, + right: number, + horizontal?: boolean, zIndex: number, animation: Keyframes, fts: boolean } -const QueueRowAnimator = styled.div.attrs(({ bottom, zIndex }) => { +const QueueRowAnimator = styled.div.attrs(({ bottom, right, zIndex }) => { return ({ style: { bottom: bottom + "px", + right: right + "px", zIndex: zIndex, } }); })` overflow: hidden; - width: 100%; + width: ${({ horizontal }) => horizontal ? (c.QUEUE_ROW_WIDTH + "px") : "100%"}; position: absolute; - transition: bottom linear ${({ fts }) => fts ? c.QUEUE_ROW_FTS_TRANSITION_TIME : c.QUEUE_ROW_TRANSITION_TIME}ms; + transition-property: ${({ horizontal }) => horizontal ? "right" : "bottom"}; + transition: linear ${({ fts }) => fts ? c.QUEUE_ROW_FTS_TRANSITION_TIME : c.QUEUE_ROW_TRANSITION_TIME}ms; animation: ${({ animation }) => animation} ${c.QUEUE_ROW_APPEAR_TIME}ms linear; /* dissapear is also linear for now. FIXME */ animation-fill-mode: forwards; `; @@ -115,6 +119,7 @@ interface QueueRow extends RunInfo { isEven: boolean, zIndex: number, bottom: number, + right: number, isFeatured: boolean, isFeaturedRunMediaLoaded: boolean, isFts: boolean, @@ -122,15 +127,29 @@ interface QueueRow extends RunInfo { } const useQueueRowsData = ({ - // width, + width, height, basicZIndex = c.QUEUE_BASIC_ZINDEX, + horizontal = true }: { + width: number, height: number, - basicZIndex?: number + basicZIndex?: number, + horizontal?: boolean, }): [QueueRow | null, QueueRow[]] => { const shouldShow = useDelayedBoolean(300); + const bottomPosition = useCallback((index: number) => { + const actual = horizontal ? Math.floor(index / c.QUEUE_HORISONTAL_HEIGHT_NUM) : index; + return (c.QUEUE_ROW_HEIGHT + c.QUEUE_ROW_PADDING) * actual; + }, [horizontal]); + const rightPosition = useCallback((index: number) => { + return (c.QUEUE_ROW_WIDTH + c.QUEUE_ROW_HORIZONTAL_PADDING) * (horizontal ? (index % c.QUEUE_HORISONTAL_HEIGHT_NUM) : 0); + }, [horizontal]); + const allowedMaxRows = useMemo(() => { + return Math.min((width / c.QUEUE_ROW_WIDTH) * (height / c.QUEUE_ROW_HEIGHT), c.QUEUE_MAX_ROWS); + }, [width, height]); + const { queue, totalQueueItems } = useAppSelector(state => state.queue); const [loadedMediaRun, setLoadedMediaRun] = useState(null); @@ -144,6 +163,7 @@ const useQueueRowsData = ({ isEven: (totalQueueItems - runIndex) % 2 === 0, zIndex: basicZIndex - runIndex + totalQueueItems, bottom: 0, + right: 0, isFeatured: false, isFeaturedRunMediaLoaded: false, isFts: isFTS(run), @@ -171,14 +191,21 @@ const useQueueRowsData = ({ let regularRowCount = 0; rows.forEach((row) => { if (row.isFts) { - row.bottom = (height - (c.QUEUE_ROW_HEIGHT + c.QUEUE_ROW_PADDING) * (totalFts - ftsRowCount)) + 3; + if (horizontal) { + row.bottom = height - c.QUEUE_ROW_HEIGHT - 3 - bottomPosition(totalFts - ftsRowCount - 1); + row.right = width - c.QUEUE_ROW_WIDTH - rightPosition(totalFts - ftsRowCount - 1); + } else { + row.bottom = height - bottomPosition(totalFts - ftsRowCount) + 3; + row.right = 0; + } ftsRowCount++; - } else { - row.bottom = (c.QUEUE_ROW_HEIGHT + c.QUEUE_ROW_PADDING) * regularRowCount; + } else { + row.bottom = bottomPosition(regularRowCount); + row.right = rightPosition(regularRowCount); regularRowCount++; } }); - const allowedRegular = c.QUEUE_MAX_ROWS - ftsRowCount; + const allowedRegular = allowedMaxRows - ftsRowCount; rows = rows.filter((row, index) => { return row.isFts || index < allowedRegular; }); @@ -293,7 +320,7 @@ const QueueWrap = styled.div` background-color: ${c.QUEUE_BACKGROUND_COLOR}; background-repeat: no-repeat; border-radius: ${c.GLOBAL_BORDER_RADIUS}; - padding: 8px; + padding: ${c.QUEUE_ROW_HORIZONTAL_PADDING}px; box-sizing: border-box; display: flex; flex-direction: column; @@ -365,10 +392,13 @@ export const Featured = ({ runInfo }) => { ; }; -export const Queue = () => { - // const [width, setWidth] = useState(null); +type QueueProps = { + widgetData: Widget.QueueWidget, +}; +export const Queue = ({ widgetData: { settings: { horizontal }, location } }): QueueProps => { const [height, setHeight] = useState(null); - const [featured, queueRows] = useQueueRowsData({ height }); + const width =location.sizeX - c.QUEUE_ROW_HORIZONTAL_PADDING * 2; + const [featured, queueRows] = useQueueRowsData({ height, width, horizontal }); return <> @@ -394,8 +424,10 @@ export const Queue = () => { return state !== "exited" && ( {/*