5QDSYIEJCF7PJL3AIDWTG4AW7BM73KCVD6CAEWGZKUONHZ2TUGLAC let now_utc = Utc::now();let tomorrow_midnight_utc = (now_utc + Days::new(1)).date_naive().and_hms_opt(0, 0, 0).unwrap_or_else(|| {error!("and_hms_opt failed");exit(1)}).and_local_timezone(Utc).single().unwrap_or_else(|| {error!("single failed");exit(1)});let duration_until_midnight = tomorrow_midnight_utc.signed_duration_since(now_utc);debug!("seconds until midnight UTC: {}",duration_until_midnight.num_seconds());let std_duration = duration_until_midnight.to_std().unwrap_or_else(|error| {error!("to_std failed: {error}");exit(1)});
let now = Zoned::now().with_time_zone(jiff::tz::TimeZone::UTC);match Zoned::now().with_time_zone(jiff::tz::TimeZone::UTC).end_of_day(){Ok(midnight) => {let duration = now.duration_until(&midnight);debug!("midnight: {midnight}");debug!("seconds until midnight: {}", duration.as_secs());
sleep(std_duration);sleep(Duration::from_secs(1));
match duration.try_into() {Ok(mut duration) => {duration += Duration::from_secs(2);sleep(duration);}Err(error) => {error!("new_tournament (1): {error}");exit(1);}}}Err(error) => {error!("new_tournament (2): {error}");exit(1);}}
// This file is part of hnefatafl-copenhagen.//// hnefatafl-copenhagen is free software: you can redistribute it and/or modify// it under the terms of the GNU Affero General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// hnefatafl-copenhagen is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU Affero General Public License for more details.//// You should have received a copy of the GNU Affero General Public License// along with this program. If not, see <https://www.gnu.org/licenses/>.//// SPDX-License-Identifier: AGPL-3.0-or-later// SPDX-FileCopyrightText: 2026 David Campbell <david@hnefatafl.org>use std::fmt;#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]pub(crate) enum TabId {AccountSettings,#[default]Games,GameNew,Tournament,Users,}impl fmt::Display for TabId {fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {match self {Self::AccountSettings => f.write_str("Account Settings"),Self::Games => f.write_str("Games"),Self::GameNew => f.write_str("Create Game"),Self::Tournament => f.write_str("Tournament"),Self::Users => f.write_str("Users"),}}}
fn add_infinity(&self, column: Column<'a, Message>) -> Column<'a, Message> {let infinity = radio(format!("{} (9)", TimeEnum::Infinity),TimeEnum::Infinity,self.game_settings.time,Message::Time,
fn account_settings_view(&self) -> Column<'_, Message> {let mut columns = column![text!("{} {} {} TCP",t!("connected to"),&self.connected_to,t!("via")),text!("{}: {}", t!("username"), &self.username),].padding(PADDING).spacing(SPACING);if let Some(email) = &self.email {let mut row = Row::new();if email.verified {row = row.push(text!("{} [{}]: {} ",t!("email address"),t!("verified"),email.address,));columns = columns.push(row);} else {row = row.push(text!("{} [{}]: {} ",t!("email address"),t!("unverified"),email.address,));columns = columns.push(row);let mut row = Row::new();row = row.push(text!("{}: ", t!("email code")));row = row.push(widget::text_input("", &self.email_input).on_input(Message::EmailChanged).on_paste(Message::EmailChanged).on_submit(Message::TextSendEmailCode),);columns = columns.push(row);}} else {let mut row = Row::new();row = row.push(text!("{}: ", t!("email address")));row = row.push(widget::text_input("", &self.email_input).on_input(Message::EmailChanged).on_paste(Message::EmailChanged).on_submit(Message::TextSendEmail),);columns = columns.push(row);columns = columns.push(row![text!("{}: ", t!("email code"))]);}columns = columns.push(row![button(text!("{} (6)", self.strings["Reset Email"].as_str())).on_press(Message::EmailReset)]);if let Some(error) = &self.error_email {columns = columns.push(row![text!("error: {error}").style(text::danger)]);}let mut change_password_button =button(text!("{} (7)", self.strings["Change Password"].as_str()));if !self.password_ends_with_whitespace {change_password_button = change_password_button.on_press(Message::TextSend);}columns = columns.push(row![change_password_button,widget::text_input("", &self.password).secure(!self.password_show).on_input(Message::PasswordChanged).on_paste(Message::PasswordChanged),].spacing(SPACING),);columns = columns.push(row![checkbox(self.password_show).on_toggle(Message::PasswordShow),text!("{} (8)", t!("show password")),].spacing(SPACING),
let row = row![infinity].padding(PADDING).spacing(SPACING);column.push(row)
if self.delete_account {columns = columns.push(button(text!("{} (9)",self.strings["REALLY DELETE ACCOUNT"].as_str())).on_press(Message::DeleteAccount),);} else {columns = columns.push(button(text!("{} (9)", self.strings["Delete Account"].as_str())).on_press(Message::DeleteAccount),);}columns = columns.push(button(text!("{} (Esc)", self.strings["Quit"].as_str())).on_press(Message::Leave),);columns
fn game_new(&mut self) {self.game_settings = NewGameSettings::default();self.screen = Screen::GameNew;
fn game_new_view(&self) -> Column<'_, Message> {let attacker = radio(format!("{} (7)", t!("attacker")),Role::Attacker,self.game_settings.role_selected,Message::RoleSelected,);let defender = radio(format!("{} (8)", t!("defender")),Role::Defender,self.game_settings.role_selected,Message::RoleSelected,);let rated = row![text!("{} (6):", t!("rated")),checkbox(self.game_settings.rated.into()).on_toggle(Message::RatedSelected)].padding(PADDING).spacing(SPACING);let mut new_game = button(text!("{} (Enter)", self.strings["New Game"].as_str()));if self.game_settings.role_selected.is_some() && self.game_settings.time.is_some() {new_game = new_game.on_press(Message::GameSubmit);}let leave =button(text!("{} (Esc)", self.strings["Quit"].as_str())).on_press(Message::Leave);let size_11x11 = radio("11x11 (9)",BoardSize::_11,Some(self.game_settings.board_size),Message::BoardSizeSelected,);let size_13x13 = radio("13x13 (0)",BoardSize::_13,Some(self.game_settings.board_size),Message::BoardSizeSelected,);let row_role = iced_aw::widget::LabeledFrame::new(text(t!("role")),row![attacker, defender].padding(PADDING).spacing(SPACING),);let row_board_size = iced_aw::widget::LabeledFrame::new(text(t!("board size")),row![size_11x11, size_13x13].padding(PADDING).spacing(SPACING),);let rapid = radio(format!("{} (a)", TimeEnum::Rapid),TimeEnum::Rapid,self.game_settings.time,Message::Time,);let classical = radio(format!("{} (b)", TimeEnum::Classical),TimeEnum::Classical,self.game_settings.time,Message::Time,);let long = radio(format!("{} (c)", TimeEnum::Long),TimeEnum::Long,self.game_settings.time,Message::Time,);let very_long = radio(format!("{} (d)", TimeEnum::VeryLong),TimeEnum::VeryLong,self.game_settings.time,Message::Time,);let infinity = radio(format!("{} (e)", TimeEnum::Infinity),TimeEnum::Infinity,self.game_settings.time,Message::Time,);let row_1 = row![rapid, classical].spacing(SPACING);let row_2 = row![long, very_long].spacing(SPACING);let row_3 = row![infinity].spacing(SPACING);let row_time = iced_aw::widget::LabeledFrame::new(text(t!("time")),column![row_1, row_2, row_3].padding(PADDING).spacing(3),);let leave = row![new_game, leave].padding(PADDING).spacing(SPACING);column![rated, row_role, row_board_size, row_time, leave]
Screen::AccountSettings| Screen::EmailEveryone| Screen::Game| Screen::Games| Screen::GameReview| Screen::Tournament| Screen::Users => {}Screen::GameNew => self.game_submit(),
Screen::Games if self.active_tab == TabId::GameNew => self.game_submit(),
Screen::Games => self.join_game_press(1, shift),
Screen::Games if self.active_tab == TabId::Games => self.join_game_press(1, shift),Screen::Games if self.active_tab == TabId::GameNew => {self.game_settings.time = Some(TimeEnum::Classical);}Screen::EmailEveryone | Screen::Games | Screen::Login => {}
Screen::Games => self.join_game_press(2, shift),
Screen::Games if self.active_tab == TabId::Games => self.join_game_press(2, shift),Screen::Games if self.active_tab == TabId::GameNew => {self.game_settings.time = Some(TimeEnum::Long);}Screen::EmailEveryone | Screen::Games | Screen::Login => {}
Screen::Games => self.join_game_press(3, shift),
Screen::Games if self.active_tab == TabId::Games => self.join_game_press(3, shift),Screen::Games if self.active_tab == TabId::GameNew => {self.game_settings.time = Some(TimeEnum::VeryLong);}Screen::EmailEveryone | Screen::Games | Screen::Login => {}
Screen::Games => self.join_game_press(4, shift),
Screen::Games if self.active_tab == TabId::Games => self.join_game_press(4, shift),Screen::Games if self.active_tab == TabId::GameNew => {self.game_settings.time = Some(TimeEnum::Infinity);}Screen::EmailEveryone | Screen::Games | Screen::Login => {}
Screen::AccountSettings| Screen::EmailEveryone| Screen::Game| Screen::GameNew| Screen::GameReview| Screen::Login| Screen::Tournament| Screen::Users => {}
Screen::EmailEveryone | Screen::Game | Screen::GameReview | Screen::Login => {}
Screen::AccountSettings| Screen::EmailEveryone| Screen::Game| Screen::GameNew| Screen::GameReview| Screen::Login| Screen::Tournament| Screen::Users => {}
Screen::EmailEveryone | Screen::Game | Screen::GameReview | Screen::Login => {}
Screen::AccountSettings| Screen::EmailEveryone| Screen::Game| Screen::GameNew| Screen::GameReview| Screen::Login| Screen::Tournament| Screen::Users => {}
Screen::EmailEveryone | Screen::Game | Screen::GameReview | Screen::Login => {}
Screen::AccountSettings| Screen::EmailEveryone| Screen::Game| Screen::GameNew| Screen::GameReview| Screen::Login| Screen::Tournament| Screen::Users => {}
Screen::EmailEveryone | Screen::Game | Screen::GameReview | Screen::Login => {}
Screen::AccountSettings| Screen::EmailEveryone| Screen::Game| Screen::GameNew| Screen::GameReview| Screen::Login| Screen::Tournament| Screen::Users => {}
Screen::EmailEveryone | Screen::Game | Screen::GameReview | Screen::Login => {}
Screen::AccountSettings| Screen::EmailEveryone| Screen::Game| Screen::GameNew| Screen::GameReview| Screen::Login| Screen::Tournament| Screen::Users => {}
Screen::EmailEveryone | Screen::Game | Screen::GameReview | Screen::Login => {}
Screen::AccountSettings| Screen::EmailEveryone| Screen::Game| Screen::GameNew| Screen::GameReview| Screen::Login| Screen::Tournament| Screen::Users => {}
Screen::EmailEveryone | Screen::Game | Screen::GameReview | Screen::Login => {}
Screen::AccountSettings| Screen::EmailEveryone| Screen::Game| Screen::GameNew| Screen::GameReview| Screen::Login| Screen::Tournament| Screen::Users => {}
Screen::EmailEveryone | Screen::Game | Screen::GameReview | Screen::Login => {}
Screen::AccountSettings | Screen::Login => self.reset_email(),Screen::EmailEveryone | Screen::Tournament | Screen::Users => {}Screen::Games => self.send("archived_games\n"),Screen::GameNew => self.game_settings.role_selected = Some(Role::Attacker),
Screen::Login => self.toggle_show_password(),Screen::EmailEveryone => {}Screen::Games => self.active_tab = TabId::Games,
Screen::AccountSettings => {self.send(&format!("change_password {}\n", self.password));}Screen::EmailEveryone | Screen::Tournament | Screen::Users => {}Screen::Games => self.my_games_only(),Screen::GameNew => self.game_settings.role_selected = Some(Role::Defender),
Screen::EmailEveryone => {}Screen::Games => self.active_tab = TabId::GameNew,
Screen::AccountSettings => self.toggle_show_password(),Screen::EmailEveryone | Screen::Tournament | Screen::Users => {}Screen::Games => self.game_new(),Screen::GameNew => self.game_settings.board_size = BoardSize::_11,
Screen::EmailEveryone => {}Screen::Games => self.active_tab = TabId::Tournament,
Screen::AccountSettings => self.delete_account(),Screen::EmailEveryone | Screen::Tournament | Screen::Users => {}Screen::Games => self.screen = Screen::Users,Screen::GameNew => self.game_settings.board_size = BoardSize::_13,
Screen::EmailEveryone => {}Screen::Games => self.active_tab = TabId::AccountSettings,
Screen::AccountSettings| Screen::EmailEveryone| Screen::Tournament| Screen::Users => {}Screen::Games => self.screen = Screen::AccountSettings,Screen::GameNew => self.game_settings.time = Some(TimeEnum::Rapid),
Screen::EmailEveryone => {}Screen::Games => self.active_tab = TabId::Users,// Fixme: Screen::GameNew => self.game_settings.time = Some(TimeEnum::Rapid),
Screen::AccountSettings| Screen::EmailEveryone| Screen::Tournament| Screen::Users => {}Screen::GameNew => self.game_settings.time = Some(TimeEnum::Classical),Screen::Games => open_url("https://hnefatafl.org/rules.html"),
Screen::EmailEveryone => {}// Fixme: Screen::GameNew => self.game_settings.time = Some(TimeEnum::Classical),Screen::Games => match self.active_tab {TabId::AccountSettings => self.reset_email(),TabId::GameNew => self.game_settings.rated = !self.game_settings.rated,TabId::Games => self.send("archived_games\n"),TabId::Tournament => open_url("https://hnefatafl.org/tournaments.html"),TabId::Users => {}},
Screen::AccountSettings | Screen::EmailEveryone | Screen::Tournament => {}Screen::Games | Screen::Users => self.users_sort_by = SortBy::Rating,Screen::GameNew => self.game_settings.time = Some(TimeEnum::Long),
Screen::EmailEveryone => {}Screen::Games => match self.active_tab {// Fixme!TabId::AccountSettings => {self.send(&format!("change_password {}\n", self.password));}TabId::GameNew => self.game_settings.role_selected = Some(Role::Attacker),TabId::Games => open_url("https://hnefatafl.org/rules.html"),TabId::Tournament => self.send("join_tournament\n"),TabId::Users => {}},// Fixme: Screen::GameNew => self.game_settings.time = Some(TimeEnum::Long),
Screen::AccountSettings | Screen::EmailEveryone | Screen::Tournament => {}Screen::Games | Screen::Users => self.users_sort_by = SortBy::Name,Screen::GameNew => self.game_settings.time = Some(TimeEnum::VeryLong),
Screen::EmailEveryone => {}Screen::Games => match self.active_tab {TabId::AccountSettings => self.toggle_show_password(),TabId::GameNew => self.game_settings.role_selected = Some(Role::Defender),TabId::Games => self.my_games_only(),TabId::Tournament => self.send("leave_tournament\n"),TabId::Users => {}},// Fixme: Screen::GameNew => self.game_settings.time = Some(TimeEnum::VeryLong),
Screen::AccountSettings| Screen::EmailEveryone| Screen::Games| Screen::Tournament| Screen::Users => {}Screen::GameNew => self.game_settings.time = Some(TimeEnum::Infinity),
Screen::EmailEveryone => {}// Fixme: Screen::GameNew => self.game_settings.time = Some(TimeEnum::Infinity),Screen::Games => match self.active_tab {TabId::AccountSettings => self.delete_account(),TabId::GameNew => self.game_settings.board_size = BoardSize::_11,TabId::Games | TabId::Users => self.users_sort_by = SortBy::Rating,TabId::Tournament => {}},
Screen::AccountSettings| Screen::EmailEveryone| Screen::Games| Screen::Tournament| Screen::Users => {}Screen::GameNew => self.game_settings.rated = !self.game_settings.rated,
Screen::EmailEveryone => {}Screen::Games => match self.active_tab {TabId::AccountSettings | TabId::Tournament => {}TabId::GameNew => self.game_settings.board_size = BoardSize::_13,TabId::Games | TabId::Users => self.users_sort_by = SortBy::Name,},
fn games_view(&self) -> Column<'_, Message> {let username = row![text!("{}: {}", t!("username"), &self.username)].spacing(SPACING);let username = container(username).padding(PADDING / 2).style(container::bordered_box);let my_games_text = text!("{} (8)", t!("My Games Only")).center();let my_games = checkbox(self.my_games_only).on_toggle(Message::MyGamesOnly);let get_archived_games =button(text!("{} (6)", self.strings["Get Archived Games"].as_str())).on_press(Message::ArchivedGamesGet);let website = button(text!("{} (7)", self.strings["Rules"].as_str())).on_press(Message::OpenUrl("https://hnefatafl.org/rules.html".to_string()),);let quit =button(text!("{} (Esc)", self.strings["Quit"].as_str())).on_press(Message::Leave);let mut middle = row![get_archived_games, website, quit].spacing(SPACING);if self.admin {middle = middle.push(button("Email Everyone").on_press(Message::EmailEveryone));}
emails_sent =if let Some(email_sent) = DateTime::from_timestamp(account.email_sent, 0) {emails_sent.push(text(email_sent.format("%Y-%m-%d").to_string()))} else {emails_sent.push(text(""))};
if let Ok(timestamp) = Timestamp::from_second(account.email_sent) {emails_sent =emails_sent.push(text(timestamp.strftime("%Y-%m-%d").to_string()));} else {emails_sent = emails_sent.push(text(""));}
Screen::AccountSettings => {let mut rating = String::new();let mut wins = String::new();let mut draws = String::new();let mut losses = String::new();for user in self.users.values() {if self.username == user.name {rating = user.rating.to_string_rounded();wins.clone_from(&user.wins);losses.clone_from(&user.losses);draws.clone_from(&user.draws);}}let mut columns = column![text!("{} {} {} TCP",t!("connected to"),&self.connected_to,t!("via")),text!("{}: {}", t!("username"), &self.username),text!("{}: {rating}", t!("rating")),text!("{}: {wins}", t!("wins")),text!("{}: {losses}", t!("losses")),text!("{}: {draws}", t!("draws")),].padding(PADDING).spacing(SPACING);if let Some(email) = &self.email {let mut row = Row::new();if email.verified {row = row.push(text!("{}: [{}] {} ",t!("email address"),t!("verified"),email.address,));columns = columns.push(row);} else {row = row.push(text!("{}: [{}] {} ",t!("email address"),t!("unverified"),email.address,));columns = columns.push(row);let mut row = Row::new();row = row.push(text!("{}: ", t!("email code")));row = row.push(widget::text_input("", &self.text_input).on_input(Message::TextChanged).on_paste(Message::TextChanged).on_submit(Message::TextSendEmailCode),);columns = columns.push(row);}} else {let mut row = Row::new();row = row.push(text!("{}: ", t!("email address")));row = row.push(widget::text_input("", &self.text_input).on_input(Message::TextChanged).on_paste(Message::TextChanged).on_submit(Message::TextSendEmail),);columns = columns.push(row);columns = columns.push(row![text!("{}: ", t!("email code"))]);}columns = columns.push(row![button(text!("{} (1)", self.strings["Reset Email"].as_str())).on_press(Message::EmailReset)]);if let Some(error) = &self.error_email {columns = columns.push(row![text!("error: {error}").style(text::danger)]);}let mut change_password_button =button(text!("{} (2)", self.strings["Change Password"].as_str()));if !self.password_ends_with_whitespace {change_password_button = change_password_button.on_press(Message::TextSend);}columns = columns.push(row![change_password_button,widget::text_input("", &self.password).secure(!self.password_show).on_input(Message::PasswordChanged).on_paste(Message::PasswordChanged),].spacing(SPACING),);columns = columns.push(row![checkbox(self.password_show).on_toggle(Message::PasswordShow),text!("{} (3)", t!("show password")),].spacing(SPACING),);if self.delete_account {columns = columns.push(button(text!("{} (4)",self.strings["REALLY DELETE ACCOUNT"].as_str())).on_press(Message::DeleteAccount),);} else {columns = columns.push(button(text!("{} (4)", self.strings["Delete Account"].as_str())).on_press(Message::DeleteAccount),);}columns = columns.push(button(text!("{} (Esc)", self.strings["Leave"].as_str())).on_press(Message::Leave),);columns.into()}
Screen::GameNew => {let attacker = radio(format!("{} (1)", t!("attacker")),Role::Attacker,self.game_settings.role_selected,Message::RoleSelected,);let defender = radio(format!("{} (2)", t!("defender")),Role::Defender,self.game_settings.role_selected,Message::RoleSelected,);let rated = row![text!("{} (0):", t!("rated")),checkbox(self.game_settings.rated.into()).on_toggle(Message::RatedSelected)].padding(PADDING).spacing(SPACING);let mut new_game = button(text!("{} (Enter)", self.strings["New Game"].as_str()));if self.game_settings.role_selected.is_some() && self.game_settings.time.is_some() {new_game = new_game.on_press(Message::GameSubmit);}let leave = button(text!("{} (Esc)", self.strings["Leave"].as_str())).on_press(Message::Leave);let size_11x11 = radio("11x11 (3)",BoardSize::_11,Some(self.game_settings.board_size),Message::BoardSizeSelected,);let size_13x13 = radio("13x13 (4)",BoardSize::_13,Some(self.game_settings.board_size),Message::BoardSizeSelected,);let row_1 = row![text!("{}:", t!("role")), attacker, defender]
Screen::Games => Tabs::new(Message::TabSelected).push(TabId::Games,iced_aw::TabLabel::Text(format!("{} (1)", t!("Games"))),self.games_view(),).push(TabId::GameNew,iced_aw::TabLabel::Text(format!("{} (2)",self.strings["Create Game"].as_str())),self.game_new_view(),).push(TabId::Tournament,iced_aw::TabLabel::Text(format!("{} (3)", self.strings["Tournament"].as_str())),self.tournament_view(),).push(TabId::AccountSettings,iced_aw::TabLabel::Text(format!("{} (4)", self.strings["Account Settings"])),self.account_settings_view(),).push(TabId::Users,iced_aw::TabLabel::Text(format!("{} (5)", self.strings["Users"])),column![button(text!("{} (Esc)", self.strings["Quit"].as_str())).on_press(Message::Leave),self.users(true)]
.spacing(SPACING);let row_2 = row![text!("{}:", t!("board size")), size_11x11, size_13x13].padding(PADDING).spacing(SPACING);let rapid = radio(format!("{} (5)", TimeEnum::Rapid),TimeEnum::Rapid,self.game_settings.time,Message::Time,);let classical = radio(format!("{} (6)", TimeEnum::Classical),TimeEnum::Classical,self.game_settings.time,Message::Time,);let long = radio(format!("{} (7)", TimeEnum::Long),TimeEnum::Long,self.game_settings.time,Message::Time,);let very_long = radio(format!("{} (8)", TimeEnum::VeryLong),TimeEnum::VeryLong,self.game_settings.time,Message::Time,);let row_3 = row![text!("{}:", t!("time"))].padding(PADDING).spacing(SPACING);let row_4 = row![rapid, classical].padding(PADDING).spacing(SPACING);let row_5 = row![long, very_long].padding(PADDING).spacing(SPACING);let mut column = column![rated, row_1, row_2, row_3, row_4, row_5];column = self.add_infinity(column);let row_6 = row![new_game, leave].padding(PADDING).spacing(SPACING);column.push(row_6).into()}Screen::Games => {let mut column = Column::new().padding(PADDING).spacing(SPACING);if self.admin {column = column.push(button("Email Everyone").on_press(Message::EmailEveryone));}let username =row![text!("{}: {}", t!("username"), &self.username)].spacing(SPACING);let username = container(username).padding(PADDING / 2).style(container::bordered_box);let tournament = button(text!("{} (0)", self.strings["Tournament"].as_str())).on_press(Message::Tournament);let my_games_text = text!("{} (2)", t!("My Games Only")).center();let my_games = checkbox(self.my_games_only).on_toggle(Message::MyGamesOnly).size(32);let get_archived_games =button(text!("{} (1)", self.strings["Get Archived Games"].as_str())).on_press(Message::ArchivedGamesGet);let username = row![username,tournament,get_archived_games,my_games,my_games_text].spacing(SPACING);let create_game = button(text!("{} (3)", self.strings["Create Game"].as_str())).on_press(Message::GameNew);let users = button(text!("{} (4)", self.strings["Users"].as_str())).on_press(Message::Users);let account_setting =button(text!("{} (5)", self.strings["Account Settings"].as_str())).on_press(Message::AccountSettings);let website = button(text!("{} (6)", self.strings["Rules"].as_str())).on_press(Message::OpenUrl("https://hnefatafl.org/rules.html".to_string()),);let quit = button(text!("{} (Esc)", self.strings["Leave"].as_str())).on_press(Message::Leave);let top = row![create_game, users, account_setting, website, quit].spacing(SPACING);let user_area = self.user_area(false);column = column.push(username);column = column.push(top);column = column.push(user_area);column.into()}
.spacing(SPACING),).height(Length::Shrink).width(Length::Fill).set_active_tab(&self.active_tab).into(),
Screen::Tournament => {let mut column = Column::new().padding(PADDING).spacing(SPACING);if self.admin_tournament {let date_button =Button::new(text("Tournament Date")).on_press(Message::DateChoose);let date_picker = date_picker(self.tournament_date_show_picker,self.tournament_date,date_button,Message::DateCancel,Message::DateSubmit,);let mut delete_button = button("Delete Tournament");if self.tournament.is_some() {delete_button = delete_button.on_press(Message::TournamentDelete);}let row = row![date_picker, delete_button].spacing(SPACING);column = column.push(row);}let Some(tournament) = &self.tournament else {column = column.push(text(t!("There is no tournament.")));column = column.push(button(text!("{} (Esc)", self.strings["Leave"].as_str())).on_press(Message::Leave),);return column.into();};let mut date = Row::new().spacing(SPACING);let start_date = t!("Tournament Start Date");date = date.push(text!("{start_date}: {}", tournament.date.to_rfc2822()));let button_0 = button(text!("{} (0)",self.strings["Tournaments Described"].as_str())).on_press(Message::Tournaments);let mut button_1 =button(text!("{} (1)", self.strings["Join Tournament"].as_str()));let mut button_2 =button(text!("{} (2)", self.strings["Leave Tournament"].as_str()));if tournament.players.contains(&self.username) {button_2 = button_2.on_press(Message::TournamentLeave);} else {button_1 = button_1.on_press(Message::TournamentJoin);}let buttons = row![button_0,button_1,button_2,button(text!("{} (Esc)", self.strings["Leave"].as_str())).on_press(Message::Leave),].spacing(SPACING);let title = t!("Players");let dashes = text("-".repeat(title.len())).font(Font::MONOSPACE);let title = text(title);let title = column![title, dashes];let mut players = Column::new();let mut player_names: Vec<_> = tournament.players.iter().collect();player_names.sort();for player in &player_names {players = players.push(text(*player));}column = column.push(date);column = column.push(buttons);column = column.push(title);column = column.push(players);if self.admin_tournament {let mut delete_button = button("Delete Tournament Tree");if let Some(tournament) = &self.tournament&& tournament.groups.is_some(){delete_button = delete_button.on_press(Message::TournamentTreeDelete);}let mut start_tournament = button("Start Tournament");if let Some(tournament) = &self.tournament {if tournament.groups.is_none() {start_tournament = start_tournament.on_press(Message::TournamentStart);}} else {start_tournament = start_tournament.on_press(Message::TournamentStart);}column = column.push(row![start_tournament, delete_button].spacing(SPACING));}column = column.push(self.display_tournament());scrollable(column).spacing(SPACING).into()}Screen::Users => column![button(text!("{} (Esc)", self.strings["Leave"].as_str())).on_press(Message::Leave),self.users(true),].padding(PADDING).spacing(SPACING).into(),
if self.admin_tournament {let date_button = Button::new(text("Tournament Date")).on_press(Message::DateChoose);let date_picker = date_picker(self.tournament_date_show_picker,self.tournament_date,date_button,Message::DateCancel,Message::DateSubmit,);let mut delete_button = button("Delete Tournament");if self.tournament.is_some() {delete_button = delete_button.on_press(Message::TournamentDelete);}let row = row![date_picker, delete_button].spacing(SPACING);column = column.push(row);}let Some(tournament) = &self.tournament else {column = column.push(text(t!("There is no tournament.")));column = column.push(button(text!("{} (Esc)", self.strings["Quit"].as_str())).on_press(Message::Leave),);return scrollable(column).spacing(SPACING);};let mut date = Row::new().spacing(SPACING);let start_date = t!("Tournament Start Date");date = date.push(text!("{start_date}: {}",tournament.date.strftime("%F %T UTC")));let button_0 = button(text!("{} (6)",self.strings["Tournaments Described"].as_str())).on_press(Message::Tournaments);let mut button_1 = button(text!("{} (7)", self.strings["Join Tournament"].as_str()));let mut button_2 = button(text!("{} (8)", self.strings["Leave Tournament"].as_str()));if tournament.players.contains(&self.username) {button_2 = button_2.on_press(Message::TournamentLeave);} else {button_1 = button_1.on_press(Message::TournamentJoin);}let buttons = row![button_0,button_1,button_2,button(text!("{} (Esc)", self.strings["Quit"].as_str())).on_press(Message::Leave),].spacing(SPACING);let title = t!("Players");let dashes = text("-".repeat(title.len())).font(Font::MONOSPACE);let title = text(title);let title = column![title, dashes];let mut players = Column::new();let mut player_names: Vec<_> = tournament.players.iter().collect();player_names.sort();for player in &player_names {players = players.push(text(*player));}column = column.push(date);column = column.push(buttons);column = column.push(title);column = column.push(players);if self.admin_tournament {let mut delete_button = button("Delete Tournament Tree");if let Some(tournament) = &self.tournament&& tournament.groups.is_some(){delete_button = delete_button.on_press(Message::TournamentTreeDelete);}let mut start_tournament = button("Start Tournament");if let Some(tournament) = &self.tournament {if tournament.groups.is_none() {start_tournament = start_tournament.on_press(Message::TournamentStart);}} else {start_tournament = start_tournament.on_press(Message::TournamentStart);}column = column.push(row![start_tournament, delete_button].spacing(SPACING));}column = column.push(self.display_tournament());scrollable(column).spacing(SPACING)}
"anstyle-parse",
"anstyle-parse 0.2.7","anstyle-query","anstyle-wincon","colorchoice","is_terminal_polyfill","utf8parse",][[package]]name = "anstream"version = "1.0.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"dependencies = ["anstyle","anstyle-parse 1.0.0",
][[package]]name = "jiff-tzdb"version = "0.1.6"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "c900ef84826f1338a557697dc8fc601df9ca9af4ac137c7fb61d4c6f2dfd3076"[[package]]name = "jiff-tzdb-platform"version = "0.1.3"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8"dependencies = ["jiff-tzdb",