using VoiceCat.Core; using UIKit; namespace VoiceCat.iOS; internal sealed class ServerListController : UITableViewController { private readonly AppModel model; internal ServerListController(AppModel model) { this.model = model; Title = "Servers"; TabBarItem = new(UITabBarSystemItem.Favorites, 0); } public override void ViewDidLoad() { base.ViewDidLoad(); TableView.RegisterClassForCellReuse(typeof(UITableViewCell), "server"); NavigationItem.RightBarButtonItem = new(UIBarButtonSystemItem.Add, (_, _) => PresentEditor(null)); model.Changed += Reload; } public override nint RowsInSection(UITableView tableView, nint section) => model.Profiles.Count; public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath) { UITableViewCell cell = tableView.DequeueReusableCell("server", indexPath); ServerProfile p = model.Profiles[indexPath.Row]; var content = cell.DefaultContentConfiguration; content.Text = p.DisplayName; content.SecondaryText = p.Authentication.ToString(); cell.ContentConfiguration = content; cell.Accessory = UITableViewCellAccessory.DisclosureIndicator; return cell; } public override async void RowSelected(UITableView tableView, NSIndexPath indexPath) { tableView.DeselectRow(indexPath, true); try { await model.ConnectAsync(model.Profiles[indexPath.Row]); } catch (Exception e) { UiHelpers.ShowError(this, e); } } public override UISwipeActionsConfiguration GetTrailingSwipeActionsConfiguration(UITableView tableView, NSIndexPath indexPath) { ServerProfile profile = model.Profiles[indexPath.Row]; UIContextualAction edit = UIContextualAction.FromContextualActionStyle(UIContextualActionStyle.Normal, "Edit", (_, _, done) => { PresentEditor(profile); done(true); }); UIContextualAction delete = UIContextualAction.FromContextualActionStyle(UIContextualActionStyle.Destructive, "Delete", (_, _, done) => { model.RemoveProfile(profile); done(true); }); return UISwipeActionsConfiguration.FromActions([delete, edit]); } private void PresentEditor(ServerProfile? profile) => PresentViewController(new UINavigationController(new ServerEditorController(model, profile)), true, null); private void Reload() { TableView.ReloadData(); NavigationItem.Prompt = model.IsConnecting ? model.Status : null; } } internal sealed class ServerEditorController : UIViewController { private readonly AppModel model; private readonly ServerProfile? existing; private readonly UITextField host = UiHelpers.Field("Hostname or IP address"); private readonly UITextField port = UiHelpers.Field("Port"); private readonly UISegmentedControl mode = new(["Guest", "Account"]); private readonly UITextField name = UiHelpers.Field("Nickname or username"); private readonly UITextField password = UiHelpers.Field("Password (optional)", true); internal ServerEditorController(AppModel model, ServerProfile? existing) { this.model = model; this.existing = existing; Title = existing is null ? "Add Server" : "Edit Server"; } public override void ViewDidLoad() { base.ViewDidLoad(); View!.BackgroundColor = UIColor.SystemGroupedBackground; port.KeyboardType = UIKeyboardType.NumberPad; UIStackView stack = new([host, port, mode, name, password]) { Axis = UILayoutConstraintAxis.Vertical, Spacing = 12, TranslatesAutoresizingMaskIntoConstraints = false }; View.AddSubview(stack); NSLayoutConstraint.ActivateConstraints([stack.TopAnchor.ConstraintEqualTo(View.SafeAreaLayoutGuide.TopAnchor, 24), stack.LeadingAnchor.ConstraintEqualTo(View.LayoutMarginsGuide.LeadingAnchor), stack.TrailingAnchor.ConstraintEqualTo(View.LayoutMarginsGuide.TrailingAnchor)]); mode.SelectedSegment = existing?.Authentication == ServerAuthentication.Account ? 1 : 0; host.Text = existing?.Host; port.Text = (existing?.Port ?? 8384).ToString(); name.Text = existing?.Username ?? existing?.Nickname; NavigationItem.LeftBarButtonItem = new(UIBarButtonSystemItem.Cancel, (_, _) => DismissViewController(true, null)); NavigationItem.RightBarButtonItem = new(UIBarButtonSystemItem.Save, (_, _) => Save()); } private void Save() { try { if (!ushort.TryParse(port.Text, out ushort number)) throw new ArgumentException("Enter a valid port."); ServerAuthentication auth = mode.SelectedSegment == 1 ? ServerAuthentication.Account : ServerAuthentication.Guest; ServerProfile p = ServerProfile.Create(host.Text ?? "", number, auth, auth == ServerAuthentication.Account ? name.Text : null, auth == ServerAuthentication.Guest ? name.Text : null, existing?.Id); model.UpsertProfile(p, password.Text); DismissViewController(true, null); } catch (Exception e) { UiHelpers.ShowError(this, e); } } }