Skip to content

Commit 6c89b19

Browse files
committed
Add a status-message to referee message and UI
1 parent 79ee37a commit 6c89b19

19 files changed

+627
-344
lines changed

frontend/src/components/common/TextInput.vue

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type {Shortcuts} from "@/providers/shortcuts";
66
defineProps<{
77
modelValue?: string,
88
label?: string,
9+
clearable?: boolean,
910
}>()
1011
1112
const emit = defineEmits<{
@@ -38,8 +39,8 @@ const updateValue = (v: string | number | null) => {
3839
@focusin="onFocusin"
3940
@focusout="onFocusout"
4041
>
41-
<template v-slot:prepend>
42-
<q-icon name="close" @click="updateValue(null)"/>
42+
<template v-slot:prepend v-if="clearable">
43+
<q-icon name="clear" @click="updateValue(null)"/>
4344
</template>
4445
</q-input>
4546
</template>

frontend/src/components/game-events/common/TextItem.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const updateValue = (v: string | undefined) => {
2424
:label="label"
2525
:model-value="modelValue"
2626
@update:model-value="updateValue"
27+
:clearable="true"
2728
/>
2829
</q-item-section>
2930
</q-item>
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<script setup lang="ts">
2+
import {computed, inject, ref} from 'vue'
3+
import {useMatchStateStore} from "@/store/matchState";
4+
import type {ControlApi} from "@/providers/controlApi";
5+
import TextInput from "@/components/common/TextInput.vue";
6+
7+
const store = useMatchStateStore()
8+
const control = inject<ControlApi>('control-api')!
9+
10+
const statusMessage = computed(() => store.matchState.statusMessage)
11+
const currentStatusMessage = ref(statusMessage.value)
12+
const setCurrentStatusMessage = (value?: string) => {
13+
currentStatusMessage.value = value
14+
}
15+
const applyCurrentStatusMessage = () => {
16+
if (currentStatusMessage.value) {
17+
sendStatusMessage(currentStatusMessage.value)
18+
}
19+
}
20+
21+
const sendStatusMessage = (value: string) => {
22+
if (value === statusMessage.value) {
23+
return
24+
}
25+
control.SubmitChange({
26+
origin: "UI",
27+
revertible: true,
28+
change: {
29+
$case: "setStatusMessageChange",
30+
setStatusMessageChange: {
31+
statusMessage: value,
32+
}
33+
}
34+
})
35+
}
36+
37+
const dialogOpen = ref(false)
38+
39+
</script>
40+
41+
<template>
42+
<q-btn
43+
label="Status Message"
44+
color="primary"
45+
@click="dialogOpen = true"
46+
/>
47+
<q-dialog v-model="dialogOpen">
48+
<q-card class="q-px-sm q-pb-md">
49+
<q-card-section class="row items-center q-pb-none">
50+
<div class="text-h6">Status Message</div>
51+
<q-space/>
52+
<q-btn icon="close" flat round dense v-close-popup/>
53+
</q-card-section>
54+
55+
<q-card-section>
56+
The status message will be broadcast and displayed on the status-board.
57+
</q-card-section>
58+
59+
<q-separator/>
60+
61+
<q-card-actions vertical>
62+
<q-btn
63+
flat
64+
label="Vision Problems"
65+
@click="sendStatusMessage('Vision Problems')"
66+
/>
67+
<q-btn
68+
flat
69+
label="Rule Discussions"
70+
@click="sendStatusMessage('Rule Discussions')"
71+
/>
72+
<TextInput
73+
label="Custom Status Message"
74+
:modelValue="statusMessage"
75+
@update:model-value="setCurrentStatusMessage"
76+
@focusout="applyCurrentStatusMessage"
77+
:clearable="false"
78+
/>
79+
</q-card-actions>
80+
81+
<q-card-actions>
82+
<q-btn
83+
v-close-popup
84+
label="Close"
85+
color="primary"
86+
/>
87+
<q-btn
88+
label="Clear"
89+
color="primary"
90+
@click="sendStatusMessage('')"
91+
/>
92+
</q-card-actions>
93+
</q-card>
94+
</q-dialog>
95+
</template>
96+
97+
<style scoped>
98+
99+
</style>

frontend/src/proto/ssl_gc_change.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ export interface Change {
4848
| { $case: "switchColorsChange"; switchColorsChange: Change_SwitchColors }
4949
| { $case: "revertChange"; revertChange: Change_Revert }
5050
| { $case: "newGameStateChange"; newGameStateChange: Change_NewGameState }
51-
| { $case: "acceptProposalGroupChange"; acceptProposalGroupChange: Change_AcceptProposalGroup };
51+
| { $case: "acceptProposalGroupChange"; acceptProposalGroupChange: Change_AcceptProposalGroup }
52+
| { $case: "setStatusMessageChange"; setStatusMessageChange: Change_SetStatusMessage };
5253
}
5354

5455
/** New referee command */
@@ -187,6 +188,11 @@ export interface Change_NewGameState {
187188
gameState?: GameState;
188189
}
189190

191+
export interface Change_SetStatusMessage {
192+
/** The new status message */
193+
statusMessage?: string;
194+
}
195+
190196
export const StateChange = {
191197
fromJSON(object: any): StateChange {
192198
return {
@@ -262,6 +268,11 @@ export const Change = {
262268
$case: "acceptProposalGroupChange",
263269
acceptProposalGroupChange: Change_AcceptProposalGroup.fromJSON(object.acceptProposalGroupChange),
264270
}
271+
: isSet(object.setStatusMessageChange)
272+
? {
273+
$case: "setStatusMessageChange",
274+
setStatusMessageChange: Change_SetStatusMessage.fromJSON(object.setStatusMessageChange),
275+
}
265276
: undefined,
266277
};
267278
},
@@ -320,6 +331,10 @@ export const Change = {
320331
(obj.acceptProposalGroupChange = message.change?.acceptProposalGroupChange
321332
? Change_AcceptProposalGroup.toJSON(message.change?.acceptProposalGroupChange)
322333
: undefined);
334+
message.change?.$case === "setStatusMessageChange" &&
335+
(obj.setStatusMessageChange = message.change?.setStatusMessageChange
336+
? Change_SetStatusMessage.toJSON(message.change?.setStatusMessageChange)
337+
: undefined);
323338
return obj;
324339
},
325340
};
@@ -571,6 +586,18 @@ export const Change_NewGameState = {
571586
},
572587
};
573588

589+
export const Change_SetStatusMessage = {
590+
fromJSON(object: any): Change_SetStatusMessage {
591+
return { statusMessage: isSet(object.statusMessage) ? String(object.statusMessage) : "" };
592+
},
593+
594+
toJSON(message: Change_SetStatusMessage): unknown {
595+
const obj: any = {};
596+
message.statusMessage !== undefined && (obj.statusMessage = message.statusMessage);
597+
return obj;
598+
},
599+
};
600+
574601
function fromTimestamp(t: Timestamp): Date {
575602
let millis = t.seconds * 1_000;
576603
millis += t.nanos / 1_000_000;

frontend/src/proto/ssl_gc_referee_message.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ export interface Referee {
114114
* * ball placement
115115
*/
116116
currentActionTimeRemaining?: number;
117+
/** A message that can be displayed to the spectators, like a reason for a stoppage. */
118+
statusMessage?: string;
117119
}
118120

119121
/** These are the "coarse" stages of the game. */
@@ -511,6 +513,7 @@ export const Referee = {
511513
currentActionTimeRemaining: isSet(object.currentActionTimeRemaining)
512514
? Number(object.currentActionTimeRemaining)
513515
: 0,
516+
statusMessage: isSet(object.statusMessage) ? String(object.statusMessage) : "",
514517
};
515518
},
516519

@@ -543,6 +546,7 @@ export const Referee = {
543546
}
544547
message.currentActionTimeRemaining !== undefined &&
545548
(obj.currentActionTimeRemaining = Math.round(message.currentActionTimeRemaining));
549+
message.statusMessage !== undefined && (obj.statusMessage = message.statusMessage);
546550
return obj;
547551
},
548552
};

frontend/src/proto/ssl_gc_state.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ export interface State {
251251
matchType?: MatchType;
252252
readyContinueTime?: Date;
253253
shootoutState?: ShootoutState;
254+
statusMessage?: string;
254255
}
255256

256257
export interface State_TeamStateEntry {
@@ -502,6 +503,7 @@ export const State = {
502503
matchType: isSet(object.matchType) ? matchTypeFromJSON(object.matchType) : MatchType.UNKNOWN_MATCH,
503504
readyContinueTime: isSet(object.readyContinueTime) ? fromJsonTimestamp(object.readyContinueTime) : undefined,
504505
shootoutState: isSet(object.shootoutState) ? ShootoutState.fromJSON(object.shootoutState) : undefined,
506+
statusMessage: isSet(object.statusMessage) ? String(object.statusMessage) : "",
505507
};
506508
},
507509

@@ -546,6 +548,7 @@ export const State = {
546548
message.readyContinueTime !== undefined && (obj.readyContinueTime = message.readyContinueTime.toISOString());
547549
message.shootoutState !== undefined &&
548550
(obj.shootoutState = message.shootoutState ? ShootoutState.toJSON(message.shootoutState) : undefined);
551+
message.statusMessage !== undefined && (obj.statusMessage = message.statusMessage);
549552
return obj;
550553
},
551554
};

frontend/src/providers/shortcuts/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ export class Shortcuts {
6565
case "NumpadEnter":
6666
this.manualActions.getContinueAction().send()
6767
break
68+
default:
69+
return
6870
}
6971
e.preventDefault()
7072
}

frontend/src/views/MatchView.vue

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import SwitchSidesButton from "@/components/start/SwitchSidesButton.vue";
1313
import GameEvents from "@/components/match/GameEvents.vue";
1414
import {Command_Type} from "@/proto/ssl_gc_state";
1515
import CommandButton from "@/components/control/CommandButton.vue";
16+
import StatusMessageInput from "@/components/match/StatusMessageInput.vue";
1617
1718
const store = useMatchStateStore()
1819
const gcStore = useGcStateStore()
@@ -81,7 +82,12 @@ onUnmounted(() => {
8182
</q-card>
8283
</q-expansion-item>
8384
<div class="row">
84-
<AutoContinueInput/>
85+
<div class="col">
86+
<AutoContinueInput/>
87+
</div>
88+
<div class="col">
89+
<StatusMessageInput/>
90+
</div>
8591
</div>
8692
<div class="row justify-evenly">
8793
Press

frontend/src/views/NewGameEventView.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const createGameEvent = (gameEvent: GameEvent) => {
5252

5353
<div class="row q-gutter-md justify-evenly">
5454
<ToggleInput v-model="propose" label="Propose only"/>
55-
<TextInput v-model="origin" label="Origin"/>
55+
<TextInput v-model="origin" label="Origin" :clearable="true"/>
5656
</div>
5757

5858
<q-tab-panels v-model="tab" animated swipeable>

internal/app/publish/messagegenerator.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ func (g *MessageGenerator) StateToRefereeMessage(matchState *state.State) (r *st
115115
r.NextCommand = mapCommand(matchState.NextCommand)
116116
*r.CurrentActionTimeRemaining = microseconds(matchState.CurrentActionTimeRemaining)
117117
r.MatchType = matchState.MatchType
118+
r.StatusMessage = matchState.StatusMessage
118119

119120
updateTeam(r.Yellow, matchState.TeamInfo(state.Team_YELLOW))
120121
updateTeam(r.Blue, matchState.TeamInfo(state.Team_BLUE))

0 commit comments

Comments
 (0)