mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-30 08:37:43 +01:00
tgui: Vote Panel (#57775)
This upgrades the voting panel to TGUI which makes it much more user friendly and adds some new functionality: - Users can now change votes - The screen no longer scrolls to top when someone else votes - Admins can see ckeys of users with the voting window open - Fancy icons and vote tallies in a much better format - Map list is now shuffled. - All the old functionality remains for custom votes, etc. Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com> Co-authored-by: Mothblocks <35135081+Mothblocks@users.noreply.github.com> Co-authored-by: Aleksej Komarov <stylemistake@gmail.com>
This commit is contained in:
co-authored by
LemonInTheDark
Mothblocks
Aleksej Komarov
parent
f2e5285535
commit
90ed4672a6
+116
-145
@@ -6,42 +6,37 @@ SUBSYSTEM_DEF(vote)
|
||||
|
||||
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
|
||||
|
||||
var/initiator = null
|
||||
var/started_time = null
|
||||
var/time_remaining = 0
|
||||
var/mode = null
|
||||
var/question = null
|
||||
var/list/choices = list()
|
||||
var/list/choice_by_ckey = list()
|
||||
var/list/generated_actions = list()
|
||||
var/initiator
|
||||
var/mode
|
||||
var/question
|
||||
var/started_time
|
||||
var/time_remaining
|
||||
var/list/voted = list()
|
||||
var/list/voting = list()
|
||||
var/list/generated_actions = list()
|
||||
|
||||
/datum/controller/subsystem/vote/fire() //called by master_controller
|
||||
if(mode)
|
||||
time_remaining = round((started_time + CONFIG_GET(number/vote_period) - world.time)/10)
|
||||
|
||||
if(time_remaining < 0)
|
||||
result()
|
||||
for(var/client/C in voting)
|
||||
C << browse(null, "window=vote;can_close=0")
|
||||
reset()
|
||||
else
|
||||
var/datum/browser/client_popup
|
||||
for(var/client/C in voting)
|
||||
client_popup = new(C, "vote", "Voting Panel")
|
||||
client_popup.set_window_options("can_close=0")
|
||||
client_popup.set_content(interface(C))
|
||||
client_popup.open(FALSE)
|
||||
|
||||
// Called by master_controller
|
||||
/datum/controller/subsystem/vote/fire()
|
||||
if(!mode)
|
||||
return
|
||||
time_remaining = round((started_time + CONFIG_GET(number/vote_period) - world.time)/10)
|
||||
if(time_remaining < 0)
|
||||
result()
|
||||
SStgui.close_uis(src)
|
||||
reset()
|
||||
|
||||
/datum/controller/subsystem/vote/proc/reset()
|
||||
choices.Cut()
|
||||
choice_by_ckey.Cut()
|
||||
initiator = null
|
||||
time_remaining = 0
|
||||
mode = null
|
||||
question = null
|
||||
choices.Cut()
|
||||
time_remaining = 0
|
||||
voted.Cut()
|
||||
voting.Cut()
|
||||
|
||||
remove_action_buttons()
|
||||
|
||||
/datum/controller/subsystem/vote/proc/get_result()
|
||||
@@ -74,17 +69,13 @@ SUBSYSTEM_DEF(vote)
|
||||
else if(mode == "map")
|
||||
for (var/non_voter_ckey in non_voters)
|
||||
var/client/C = non_voters[non_voter_ckey]
|
||||
if(C.prefs.preferred_map)
|
||||
if(choices[C.prefs.preferred_map]) //No votes if the map isn't in the vote.
|
||||
var/preferred_map = C.prefs.preferred_map
|
||||
choices[preferred_map] += 1
|
||||
greatest_votes = max(greatest_votes, choices[preferred_map])
|
||||
else if(config.defaultmap)
|
||||
if(choices[config.defaultmap]) //No votes if the map isn't in the vote.
|
||||
var/default_map = config.defaultmap.map_name
|
||||
choices[default_map] += 1
|
||||
greatest_votes = max(greatest_votes, choices[default_map])
|
||||
//get all options with that many votes and return them in a list
|
||||
var/preferred_map = C.prefs.preferred_map
|
||||
if(isnull(global.config.defaultmap))
|
||||
continue
|
||||
if(!preferred_map)
|
||||
preferred_map = global.config.defaultmap.map_name
|
||||
choices[preferred_map] += 1
|
||||
greatest_votes = max(greatest_votes, choices[preferred_map])
|
||||
. = list()
|
||||
if(greatest_votes)
|
||||
for(var/option in choices)
|
||||
@@ -141,12 +132,13 @@ SUBSYSTEM_DEF(vote)
|
||||
SSmapping.map_voted = TRUE
|
||||
if(restart)
|
||||
var/active_admins = FALSE
|
||||
for(var/client/C in GLOB.admins)
|
||||
for(var/client/C in GLOB.admins + GLOB.deadmins)
|
||||
if(!C.is_afk() && check_rights_for(C, R_SERVER))
|
||||
active_admins = TRUE
|
||||
break
|
||||
if(!active_admins)
|
||||
SSticker.Reboot("Restart vote successful.", "restart vote", 1) //no delay in case the restart is due to lag
|
||||
// No delay in case the restart is due to lag
|
||||
SSticker.Reboot("Restart vote successful.", "restart vote", 1)
|
||||
else
|
||||
to_chat(world, "<span style='boldannounce'>Notice:Restart vote will not restart the server automatically because there are active admins on.</span>")
|
||||
message_admins("A restart vote has passed, but there are active admins on with +server, so it has been canceled. If you wish, you may restart the server.")
|
||||
@@ -154,24 +146,30 @@ SUBSYSTEM_DEF(vote)
|
||||
return .
|
||||
|
||||
/datum/controller/subsystem/vote/proc/submit_vote(vote)
|
||||
if(mode)
|
||||
if(CONFIG_GET(flag/no_dead_vote) && usr.stat == DEAD && !usr.client.holder)
|
||||
return FALSE
|
||||
if(!(usr.ckey in voted))
|
||||
if(vote && 1<=vote && vote<=choices.len)
|
||||
voted += usr.ckey
|
||||
choices[choices[vote]]++ //check this
|
||||
return vote
|
||||
return FALSE
|
||||
if(!mode)
|
||||
return FALSE
|
||||
if(CONFIG_GET(flag/no_dead_vote) && usr.stat == DEAD && !usr.client.holder)
|
||||
return FALSE
|
||||
if(!vote || vote < 1 || vote > choices.len)
|
||||
return FALSE
|
||||
// If user has already voted, remove their specific vote
|
||||
if(usr.ckey in voted)
|
||||
choices[choices[choice_by_ckey[usr.ckey]]]--
|
||||
else
|
||||
voted += usr.ckey
|
||||
choice_by_ckey[usr.ckey] = vote
|
||||
choices[choices[vote]]++
|
||||
return vote
|
||||
|
||||
/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key)
|
||||
if(!Master.current_runlevel) //Server is still intializing.
|
||||
//Server is still intializing.
|
||||
if(!Master.current_runlevel)
|
||||
to_chat(usr, "<span class='warning'>Cannot start vote, server is not done initializing.</span>")
|
||||
return FALSE
|
||||
var/admin = FALSE
|
||||
var/lower_admin = FALSE
|
||||
var/ckey = ckey(initiator_key)
|
||||
if(GLOB.admin_datums[ckey])
|
||||
admin = TRUE
|
||||
lower_admin = TRUE
|
||||
|
||||
if(!mode)
|
||||
if(started_time)
|
||||
@@ -179,9 +177,7 @@ SUBSYSTEM_DEF(vote)
|
||||
if(mode)
|
||||
to_chat(usr, "<span class='warning'>There is already a vote in progress! please wait for it to finish.</span>")
|
||||
return FALSE
|
||||
|
||||
|
||||
if(next_allowed_time > world.time && !admin)
|
||||
if(next_allowed_time > world.time && !lower_admin)
|
||||
to_chat(usr, "<span class='warning'>A vote was initiated recently, you must wait [DisplayTimeText(next_allowed_time-world.time)] before a new vote can be started!</span>")
|
||||
return FALSE
|
||||
|
||||
@@ -192,14 +188,19 @@ SUBSYSTEM_DEF(vote)
|
||||
if("gamemode")
|
||||
choices.Add(config.votable_modes)
|
||||
if("map")
|
||||
if(!admin && SSmapping.map_voted)
|
||||
if(!lower_admin && SSmapping.map_voted)
|
||||
to_chat(usr, "<span class='warning'>The next map has already been selected.</span>")
|
||||
return FALSE
|
||||
for(var/map in config.maplist)
|
||||
// Randomizes the list so it isn't always METASTATION
|
||||
var/list/maps = list()
|
||||
for(var/map in global.config.maplist)
|
||||
var/datum/map_config/VM = config.maplist[map]
|
||||
if(!VM.votable || (VM.map_name in SSpersistence.blocked_maps))
|
||||
continue
|
||||
choices.Add(VM.map_name)
|
||||
maps += VM.map_name
|
||||
shuffle_inplace(maps)
|
||||
for(var/valid_map in maps)
|
||||
choices.Add(valid_map)
|
||||
if("custom")
|
||||
question = stripped_input(usr,"What is the vote for?")
|
||||
if(!question)
|
||||
@@ -214,12 +215,12 @@ SUBSYSTEM_DEF(vote)
|
||||
mode = vote_type
|
||||
initiator = initiator_key
|
||||
started_time = world.time
|
||||
var/text = "[capitalize(mode)] vote started by [initiator]."
|
||||
var/text = "[capitalize(mode)] vote started by [initiator || "CentCom"]."
|
||||
if(mode == "custom")
|
||||
text += "\n[question]"
|
||||
log_vote(text)
|
||||
var/vp = CONFIG_GET(number/vote_period)
|
||||
to_chat(world, "\n<font color='purple'><b>[text]</b>\nType <b>vote</b> or click <a href='?src=[REF(src)]'>here</a> to place your votes.\nYou have [DisplayTimeText(vp)] to vote.</font>")
|
||||
to_chat(world, "\n<font color='purple'><b>[text]</b>\nType <b>vote</b> or click <a href='byond://winset?command=vote'>here</a> to place your votes.\nYou have [DisplayTimeText(vp)] to vote.</font>")
|
||||
time_remaining = round(vp/10)
|
||||
for(var/c in GLOB.clients)
|
||||
var/client/C = c
|
||||
@@ -234,97 +235,73 @@ SUBSYSTEM_DEF(vote)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/controller/subsystem/vote/proc/interface(client/C)
|
||||
if(!C)
|
||||
/mob/verb/vote()
|
||||
set category = "OOC"
|
||||
set name = "Vote"
|
||||
SSvote.ui_interact(usr)
|
||||
|
||||
/datum/controller/subsystem/vote/ui_state()
|
||||
return GLOB.always_state
|
||||
|
||||
/datum/controller/subsystem/vote/ui_interact(mob/user, datum/tgui/ui)
|
||||
// Tracks who is voting
|
||||
if(!(user.client?.ckey in voting))
|
||||
voting += user.client?.ckey
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "Vote")
|
||||
ui.open()
|
||||
|
||||
/datum/controller/subsystem/vote/ui_data(mob/user)
|
||||
var/list/data = list(
|
||||
"allow_vote_map" = CONFIG_GET(flag/allow_vote_map),
|
||||
"allow_vote_mode" = CONFIG_GET(flag/allow_vote_mode),
|
||||
"allow_vote_restart" = CONFIG_GET(flag/allow_vote_restart),
|
||||
"choices" = list(),
|
||||
"lower_admin" = !!user.client?.holder,
|
||||
"mode" = mode,
|
||||
"question" = question,
|
||||
"selected_choice" = choice_by_ckey[user.client?.ckey],
|
||||
"time_remaining" = time_remaining,
|
||||
"upper_admin" = check_rights_for(user.client, R_ADMIN),
|
||||
"voting" = list(),
|
||||
)
|
||||
|
||||
if(!!user.client?.holder)
|
||||
data["voting"] += list(voting)
|
||||
|
||||
for(var/key in choices)
|
||||
data["choices"] += list(list(
|
||||
"name" = key,
|
||||
"votes" = choices[key] || 0
|
||||
))
|
||||
|
||||
return data
|
||||
|
||||
/datum/controller/subsystem/vote/ui_act(action, params)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
var/admin = FALSE
|
||||
var/trialmin = FALSE
|
||||
if(C.holder)
|
||||
admin = TRUE
|
||||
if(check_rights_for(C, R_ADMIN))
|
||||
trialmin = TRUE
|
||||
voting |= C
|
||||
|
||||
if(mode)
|
||||
if(question)
|
||||
. += "<h2>Vote: '[question]'</h2>"
|
||||
else
|
||||
. += "<h2>Vote: [capitalize(mode)]</h2>"
|
||||
. += "Time Left: [time_remaining] s<hr><ul>"
|
||||
for(var/i=1,i<=choices.len,i++)
|
||||
var/votes = choices[choices[i]]
|
||||
if(!votes)
|
||||
votes = 0
|
||||
. += "<li><a href='?src=[REF(src)];vote=[i]'>[choices[i]]</a> ([votes] votes)</li>"
|
||||
. += "</ul><hr>"
|
||||
if(admin)
|
||||
. += "(<a href='?src=[REF(src)];vote=cancel'>Cancel Vote</a>) "
|
||||
else
|
||||
. += "<h2>Start a vote:</h2><hr><ul><li>"
|
||||
//restart
|
||||
var/avr = CONFIG_GET(flag/allow_vote_restart)
|
||||
if(trialmin || avr)
|
||||
. += "<a href='?src=[REF(src)];vote=restart'>Restart</a>"
|
||||
else
|
||||
. += "<font color='grey'>Restart (Disallowed)</font>"
|
||||
if(trialmin)
|
||||
. += "\t(<a href='?src=[REF(src)];vote=toggle_restart'>[avr ? "Allowed" : "Disallowed"]</a>)"
|
||||
. += "</li><li>"
|
||||
//gamemode
|
||||
var/avm = CONFIG_GET(flag/allow_vote_mode)
|
||||
if(trialmin || avm)
|
||||
. += "<a href='?src=[REF(src)];vote=gamemode'>GameMode</a>"
|
||||
else
|
||||
. += "<font color='grey'>GameMode (Disallowed)</font>"
|
||||
if(trialmin)
|
||||
. += "\t(<a href='?src=[REF(src)];vote=toggle_gamemode'>[avm ? "Allowed" : "Disallowed"]</a>)"
|
||||
|
||||
. += "</li>"
|
||||
//map
|
||||
var/avmap = CONFIG_GET(flag/allow_vote_map)
|
||||
if(trialmin || avmap)
|
||||
. += "<a href='?src=[REF(src)];vote=map'>Map</a>"
|
||||
else
|
||||
. += "<font color='grey'>Map (Disallowed)</font>"
|
||||
if(trialmin)
|
||||
. += "\t(<a href='?src=[REF(src)];vote=toggle_map'>[avmap ? "Allowed" : "Disallowed"]</a>)"
|
||||
|
||||
. += "</li>"
|
||||
//custom
|
||||
if(trialmin)
|
||||
. += "<li><a href='?src=[REF(src)];vote=custom'>Custom</a></li>"
|
||||
. += "</ul><hr>"
|
||||
. += "<a href='?src=[REF(src)];vote=close' style='position:absolute;right:50px'>Close</a>"
|
||||
return .
|
||||
|
||||
|
||||
/datum/controller/subsystem/vote/Topic(href,href_list[],hsrc)
|
||||
if(!usr || !usr.client)
|
||||
return //not necessary but meh...just in-case somebody does something stupid
|
||||
|
||||
var/trialmin = FALSE
|
||||
var/upper_admin = FALSE
|
||||
if(usr.client.holder)
|
||||
if(check_rights_for(usr.client, R_ADMIN))
|
||||
trialmin = TRUE
|
||||
upper_admin = TRUE
|
||||
|
||||
switch(href_list["vote"])
|
||||
if("close")
|
||||
voting -= usr.client
|
||||
usr << browse(null, "window=vote")
|
||||
return
|
||||
switch(action)
|
||||
if("cancel")
|
||||
if(usr.client.holder)
|
||||
usr.log_message("[key_name_admin(usr)] cancelled a vote.", LOG_ADMIN)
|
||||
message_admins("[key_name_admin(usr)] has cancelled the current vote.")
|
||||
reset()
|
||||
if("toggle_restart")
|
||||
if(usr.client.holder && trialmin)
|
||||
if(usr.client.holder && upper_admin)
|
||||
CONFIG_SET(flag/allow_vote_restart, !CONFIG_GET(flag/allow_vote_restart))
|
||||
if("toggle_gamemode")
|
||||
if(usr.client.holder && trialmin)
|
||||
if(usr.client.holder && upper_admin)
|
||||
CONFIG_SET(flag/allow_vote_mode, !CONFIG_GET(flag/allow_vote_mode))
|
||||
if("toggle_map")
|
||||
if(usr.client.holder && trialmin)
|
||||
if(usr.client.holder && upper_admin)
|
||||
CONFIG_SET(flag/allow_vote_map, !CONFIG_GET(flag/allow_vote_map))
|
||||
if("restart")
|
||||
if(CONFIG_GET(flag/allow_vote_restart) || usr.client.holder)
|
||||
@@ -338,9 +315,9 @@ SUBSYSTEM_DEF(vote)
|
||||
if("custom")
|
||||
if(usr.client.holder)
|
||||
initiate_vote("custom",usr.key)
|
||||
else
|
||||
submit_vote(round(text2num(href_list["vote"])))
|
||||
usr.vote()
|
||||
if("vote")
|
||||
submit_vote(round(text2num(params["index"])))
|
||||
return TRUE
|
||||
|
||||
/datum/controller/subsystem/vote/proc/remove_action_buttons()
|
||||
for(var/v in generated_actions)
|
||||
@@ -350,14 +327,8 @@ SUBSYSTEM_DEF(vote)
|
||||
V.Remove(V.owner)
|
||||
generated_actions = list()
|
||||
|
||||
/mob/verb/vote()
|
||||
set category = "OOC"
|
||||
set name = "Vote"
|
||||
|
||||
var/datum/browser/popup = new(src, "vote", "Voting Panel")
|
||||
popup.set_window_options("can_close=0")
|
||||
popup.set_content(SSvote.interface(client))
|
||||
popup.open(FALSE)
|
||||
/datum/controller/subsystem/vote/ui_close(mob/user)
|
||||
voting -= user.client?.ckey
|
||||
|
||||
/datum/action/vote
|
||||
name = "Vote!"
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
import { useBackend } from "../backend";
|
||||
import { Box, Icon, Stack, Button, Section, NoticeBox, LabeledList, Collapsible } from "../components";
|
||||
import { Window } from "../layouts";
|
||||
|
||||
export const Vote = (props, context) => {
|
||||
const { data } = useBackend(context);
|
||||
const { mode, question, lower_admin } = data;
|
||||
|
||||
// Adds the voting type to title if there is an ongoing vote
|
||||
let windowTitle = "Vote";
|
||||
if (mode) {
|
||||
windowTitle += ": " + (question || mode).replace(/^\w/, c => c.toUpperCase());
|
||||
}
|
||||
|
||||
return (
|
||||
<Window resizable title={windowTitle} width={400} height={500}>
|
||||
<Window.Content>
|
||||
<Stack fill vertical>
|
||||
{!!lower_admin && (
|
||||
<Section title="Admin Options">
|
||||
<VoteOptions />
|
||||
<VotersList />
|
||||
</Section>
|
||||
)}
|
||||
<ChoicesPanel />
|
||||
<TimePanel />
|
||||
</Stack>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
// Gives access to starting votes
|
||||
const VoteOptions = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
allow_vote_mode,
|
||||
allow_vote_restart,
|
||||
allow_vote_map,
|
||||
upper_admin,
|
||||
} = data;
|
||||
|
||||
return (
|
||||
<Stack.Item>
|
||||
<Collapsible title="Start a Vote">
|
||||
<Stack justify="space-between">
|
||||
<Stack.Item>
|
||||
<Stack vertical>
|
||||
<Stack.Item>
|
||||
{!!upper_admin && (
|
||||
<Button.Checkbox
|
||||
mr={!allow_vote_map ? 1 : 1.6}
|
||||
color="red"
|
||||
checked={!!allow_vote_map}
|
||||
onClick={() => act("toggle_map")}>
|
||||
{allow_vote_map ? "Enabled" : "Disabled"}
|
||||
</Button.Checkbox>
|
||||
)}
|
||||
<Button
|
||||
disabled={!upper_admin || !allow_vote_map}
|
||||
onClick={() => act("map")}>
|
||||
Map
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
{!!upper_admin && (
|
||||
<Button.Checkbox
|
||||
mr={!allow_vote_restart ? 1 : 1.6}
|
||||
color="red"
|
||||
checked={!!allow_vote_restart}
|
||||
onClick={() => act("toggle_restart")}>
|
||||
{allow_vote_restart ? "Enabled" : "Disabled"}
|
||||
</Button.Checkbox>
|
||||
)}
|
||||
<Button
|
||||
disabled={!upper_admin || !allow_vote_restart}
|
||||
onClick={() => act("restart")}>
|
||||
Restart
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
{!!upper_admin && (
|
||||
<Button.Checkbox
|
||||
mr={!allow_vote_mode ? 1 : 1.6}
|
||||
color="red"
|
||||
checked={!!allow_vote_mode}
|
||||
onClick={() => act("toggle_gamemode")}>
|
||||
{allow_vote_mode ? "Enabled" : "Disabled"}
|
||||
</Button.Checkbox>
|
||||
)}
|
||||
<Button
|
||||
disabled={!upper_admin || !allow_vote_mode}
|
||||
onClick={() => act("gamemode")}>
|
||||
Gamemode
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Button disabled={!upper_admin} onClick={() => act("custom")}>
|
||||
Create Custom Vote
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Collapsible>
|
||||
</Stack.Item>
|
||||
);
|
||||
};
|
||||
|
||||
// Table to view voters by ckey
|
||||
const VotersList = (props, context) => {
|
||||
const { data } = useBackend(context);
|
||||
const { voting } = data;
|
||||
|
||||
return (
|
||||
<Stack.Item>
|
||||
<Collapsible title={`View Voters: ${voting.length}`}>
|
||||
<Section height={8} fill scrollable>
|
||||
{voting.map(voter => {
|
||||
return <Box key={voter}>{voter}</Box>;
|
||||
})}
|
||||
</Section>
|
||||
</Collapsible>
|
||||
</Stack.Item>
|
||||
);
|
||||
};
|
||||
|
||||
// Display choices
|
||||
const ChoicesPanel = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const { choices, selected_choice } = data;
|
||||
|
||||
return (
|
||||
<Stack.Item grow>
|
||||
<Section fill scrollable title="Choices">
|
||||
{choices.length !== 0 ? (
|
||||
<LabeledList>
|
||||
{choices.map((choice, i) => (
|
||||
<Box key={choice.id}>
|
||||
<LabeledList.Item
|
||||
label={choice.name.replace(/^\w/, c => c.toUpperCase())}
|
||||
textAlign="right"
|
||||
buttons={
|
||||
<Button
|
||||
disabled={i === selected_choice - 1}
|
||||
onClick={() => {
|
||||
act("vote", { index: i + 1 });
|
||||
}}>
|
||||
Vote
|
||||
</Button>
|
||||
}>
|
||||
{i === selected_choice - 1 && (
|
||||
<Icon
|
||||
alignSelf="right"
|
||||
mr={2}
|
||||
color="green"
|
||||
name="vote-yea" />
|
||||
)}
|
||||
{choice.votes} Votes
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Divider />
|
||||
</Box>
|
||||
))}
|
||||
</LabeledList>
|
||||
) : (
|
||||
<NoticeBox>No choices available!</NoticeBox>
|
||||
)}
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
);
|
||||
};
|
||||
|
||||
// Countdown timer at the bottom. Includes a cancel vote option for admins
|
||||
const TimePanel = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const { upper_admin, time_remaining } = data;
|
||||
|
||||
return (
|
||||
<Stack.Item mt={1}>
|
||||
<Section>
|
||||
<Stack justify="space-between">
|
||||
<Box fontSize={1.5}>
|
||||
Time Remaining: {time_remaining || 0}s
|
||||
</Box>
|
||||
{!!upper_admin && (
|
||||
<Button color="red" onClick={() => act('cancel')}>
|
||||
Cancel Vote
|
||||
</Button>
|
||||
)}
|
||||
</Stack>
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user