Skip to content
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

Scoreboard paggination #197

Merged
merged 4 commits into from
Sep 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions src/backend-api/src/main/kotlin/org/icpclive/api/Settings.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@ data class QueueSettings(val horizontal: Boolean = false) : ObjectSettings

@Serializable
data class ScoreboardSettings(
val isInfinite: Boolean = true,
val numRows: Int? = null,
val startFromRow: Int = 1,
val scrollDirection: ScoreboardScrollDirection = ScoreboardScrollDirection.Forward,
val optimismLevel: OptimismLevel = OptimismLevel.NORMAL,
val group: String = "all"
) : ObjectSettings

enum class ScoreboardScrollDirection{
FirstPage, Back, Pause, Forward, LastPage
}

@Serializable
class StatisticsSettings : ObjectSettings

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ abstract class SingleWidgetController<SettingsType : ObjectSettings, DataType :
}

suspend fun show() = mutex.withLock {
hideImpl()
cancel()
showImpl()
}

suspend fun show(newSettings: SettingsType) = mutex.withLock {
hideImpl()
cancel()
settings = newSettings
showImpl()
}
Expand All @@ -69,14 +69,14 @@ abstract class SingleWidgetController<SettingsType : ObjectSettings, DataType :
createWidgetAndShow(settings)
}

private suspend fun hideImpl() {
removeWidget()
private suspend fun cancel() {
widgetShowScope?.cancel()
widgetShowScope = null
}

suspend fun hide() = mutex.withLock {
hideImpl()
removeWidget()
cancel()
}

open suspend fun onDelete() {
Expand Down
6 changes: 3 additions & 3 deletions src/backend/src/main/kotlin/org/icpclive/data/DataBus.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ object DataBus {
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
val adminActionsFlow = MutableSharedFlow<String>(
replay = 500,
extraBufferCapacity = 0,
replay = 0,
extraBufferCapacity = 10,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
val teamSpotlightFlow = CompletableDeferred<Flow<KeyTeam>>()
Expand All @@ -49,4 +49,4 @@ object DataBus {
}

suspend fun DataBus.currentContestInfo() = currentContestInfoFlow().first()
suspend fun DataBus.currentContestInfoFlow() = contestStateFlow.await().mapNotNull { it.infoAfterEvent }.distinctUntilChanged()
suspend fun DataBus.currentContestInfoFlow() = contestStateFlow.await().mapNotNull { it.infoAfterEvent }.distinctUntilChanged()
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Button, ButtonGroup, SvgIconTypeMap } from "@mui/material";
import SkipPreviousIcon from "@mui/icons-material/SkipPrevious";
import PauseIcon from "@mui/icons-material/Pause";
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
import SkipNextIcon from "@mui/icons-material/SkipNext";
import { OverridableComponent } from "@mui/material/OverridableComponent";
import { ScoreboardScrollDirection } from "@shared/api";

export type ScrollDirectionSwitcherProps = {
direction: ScoreboardScrollDirection;
setDirection: (duration: ScoreboardScrollDirection) => void;
};

const availableDirections: [ScoreboardScrollDirection, OverridableComponent<SvgIconTypeMap>][] = [
[ScoreboardScrollDirection.FirstPage, SkipPreviousIcon],
[ScoreboardScrollDirection.Back, PlayArrowIcon],
[ScoreboardScrollDirection.Pause, PauseIcon],
[ScoreboardScrollDirection.Forward, PlayArrowIcon],
[ScoreboardScrollDirection.LastPage, SkipNextIcon]
];

const ScrollDirectionSwitcher = ({ direction, setDirection }: ScrollDirectionSwitcherProps) => {
return (
<ButtonGroup variant="outlined">
{availableDirections.map(([v, C]) => (
<Button
color={direction == v ? "error" : "primary"}
variant={direction == v ? "contained" : "outlined"}
onClick={() => setDirection(v)}
key={v}
>
<C sx={{ transform: v == ScoreboardScrollDirection.Back ? "rotate(180deg)" : undefined }}/>
</Button>
))}
</ButtonGroup>
);
};

export default ScrollDirectionSwitcher;
98 changes: 44 additions & 54 deletions src/frontend/admin/src/components/managers/ScoreboardManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,63 +6,59 @@ import {
Button,
ButtonGroup,
Checkbox,
Switch,
Stack,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
Typography
} from "@mui/material";
import { Dispatch, SetStateAction, useEffect, useState } from "react";
import { Dispatch, SetStateAction, useCallback, useEffect, useState } from "react";
import { SlimTableCell } from "../atoms/Table.tsx";
import NumericField from "../controls/NumericField.tsx";
import { GroupInfo, OptimismLevel, ScoreboardSettings } from "@shared/api.ts";
import { GroupInfo, OptimismLevel, ScoreboardSettings, ScoreboardScrollDirection } from "@shared/api.ts";
import { ScoreboardWidgetService } from "@/services/scoreboardService.ts";
import ScrollDirectionSwitcher from "@/components/controls/ScrollDirectionSwitcher.tsx";


type ScoreboardSettingsTabProps = {
isShown: boolean;
onClickShow: () => void;
onClickHide: () => void;
showWithSettings: (settings: ScoreboardSettings) => void;
hide: () => void;
settings: ScoreboardSettings;
setSettings: Dispatch<SetStateAction<ScoreboardSettings>>;
}

const ScoreboardSettingsTab = ({ isShown, onClickShow, onClickHide, settings, setSettings }: ScoreboardSettingsTabProps) => {
return (<Table align="center" sx={{ my: 0 }} size="small">
<TableHead>
<TableRow>
{["", "Start from row", "Amount of rows", "Infinity"].map(val =>
<SlimTableCell key={val} align={"center"}>
<Typography variant="h6">{val}</Typography>
</SlimTableCell>
)}
</TableRow>
</TableHead>
<TableBody>
<TableRow>
<SlimTableCell align={"center"}>
<ButtonGroup variant="contained" sx={{ m: 2 }}>
<Button color="primary" disabled={isShown} onClick={onClickShow}>Show</Button>
<Button color="error" disabled={!isShown} onClick={onClickHide}>Hide</Button>
</ButtonGroup>
</SlimTableCell>
<SlimTableCell align={"center"}>
<NumericField value={settings.startFromRow} minValue={1} arrowsDelta={settings.startFromRow}
onChange={v => setSettings(s => ({ ...s, startFromRow: v }))}/>
</SlimTableCell>
<SlimTableCell align={"center"}>
<NumericField value={settings.numRows} minValue={0} arrowsDelta={settings.numRows}
onChange={v => setSettings(s => ({ ...s, numRows: v }))}/>
</SlimTableCell>
<SlimTableCell align={"center"}>
<Switch checked={settings.isInfinite}
onChange={t => setSettings(s => ({ ...s, isInfinite: t.target.checked }))}/>
</SlimTableCell>
</TableRow>
</TableBody>
</Table>);
const ScoreboardSettingsTab = ({ isShown, showWithSettings, hide, settings, setSettings }: ScoreboardSettingsTabProps) => {
const setScrollDirection = useCallback((d: ScoreboardScrollDirection) => {
setSettings(s => {
const newSettings: ScoreboardSettings = { ...s, scrollDirection: d };
if (isShown) {
showWithSettings(newSettings);
}
return newSettings;
});
}, [setSettings, isShown, showWithSettings]);

return (
<Stack spacing={4} direction="row" flexWrap="wrap" sx={{ mx: 2 }}>
<ButtonGroup>
<Button
color={isShown ? "success" : "primary"}
onClick={() => showWithSettings(settings)} variant="contained"
>
{isShown ? "Update" : "Show"}
</Button>
<Button color="error" disabled={!isShown} onClick={hide} variant="contained">
Hide
</Button>
</ButtonGroup>
<ScrollDirectionSwitcher
direction={settings.scrollDirection}
setDirection={setScrollDirection}
/>
</Stack>
);
};

type ScoreboardOptLevelCellsProps = {
Expand Down Expand Up @@ -136,11 +132,9 @@ const ScoreboardGroupSetting = ({ settings, setSettings, groupsList }: Scoreboar
};

export const DEFAULT_SCOREBOARD_SETTINGS: ScoreboardSettings = {
isInfinite: true,
optimismLevel: OptimismLevel.normal,
group: "all",
startFromRow: 1,
numRows: 0,
scrollDirection: ScoreboardScrollDirection.Forward,
};

// TODO: create generic type for all managers that has service, settings and setSettings
Expand All @@ -158,19 +152,15 @@ const ScoreboardManager = ({ service, isShown, settings, setSettings }: Scoreboa
service.groups().then((result) => setGroupsList(result));
}, [service]);

const onClickHide = () => {
// TODO: when hide not reload settings from server, because I want set new settings and than hide + show
service.hide();
};

const onClickShow = () => {
service.showWithSettings(settings);
};

return (
<>
<ScoreboardSettingsTab isShown={isShown} onClickShow={onClickShow} onClickHide={onClickHide}
settings={settings} setSettings={setSettings}/>
<ScoreboardSettingsTab
isShown={isShown}
showWithSettings={(s: ScoreboardSettings) => service.showWithSettings(s)}
hide={() => service.hide()}
settings={settings}
setSettings={setSettings}
/>
<ScoreboardGroupSetting groupsList={groupsList} settings={settings} setSettings={setSettings}/>
</>
);
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/admin/src/components/pages/ControlsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ const ControlsPage = () => {
<Container maxWidth="md" sx={{ pt: 2 }} className="Controls">
<ScoreboardWidgetGroup/>
<SimpleWidgetGroup title={"Queue"} apiPath={"/queue"}/>
<SimpleWidgetGroup title={"Statistic"} apiPath={"/statistics"}/>
<SimpleWidgetGroup title={"Statistics"} apiPath={"/statistics"}/>
<SimpleWidgetGroup title={"Ticker"} apiPath={"/ticker"}/>
<FullScreenClockWidgetGroup/>
</Container>
Expand Down
12 changes: 9 additions & 3 deletions src/frontend/generated/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -555,9 +555,7 @@ export interface QueueSettings {
}

export interface ScoreboardSettings {
isInfinite?: boolean;
numRows?: number | null;
startFromRow?: number;
scrollDirection?: ScoreboardScrollDirection;
optimismLevel?: OptimismLevel;
group?: string;
}
Expand All @@ -583,6 +581,14 @@ export interface OverlayTeamViewSettings {
export interface TickerSettings {
}

export enum ScoreboardScrollDirection {
FirstPage = "FirstPage",
Back = "Back",
Pause = "Pause",
Forward = "Forward",
LastPage = "LastPage",
}

export enum OptimismLevel {
normal = "normal",
optimistic = "optimistic",
Expand Down
8 changes: 4 additions & 4 deletions src/frontend/overlay/src/components/layouts/MainLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ type WidgetWrapProps = {
const WidgetWrap = styled.div.attrs<WidgetWrapProps>(
({ left, top, width, height }) => {
return { style: {
left: left+"px",
top: top+"px",
width: width+"px",
height: height+"px",
left: left + "px",
top: top + "px",
width: width + "px",
height: height + "px",
} };
}
)<WidgetWrapProps>`
Expand Down
Loading
Loading