Improve iOS voice stability and user audio controls
This commit is contained in:
@@ -116,14 +116,74 @@ internal sealed class ChatController : UIViewController
|
||||
public override void ViewDidLoad()
|
||||
{
|
||||
base.ViewDidLoad(); View!.BackgroundColor = UIColor.SystemBackground; log.Editable = false; log.Font = UIFont.PreferredBody; log.AccessibilityLabel = "Chat and activity timeline"; log.TranslatesAutoresizingMaskIntoConstraints = false;
|
||||
NavigationItem.RightBarButtonItem = new("Private Chats", UIBarButtonItemStyle.Plain, (_, _) => NavigationController?.PushViewController(new PrivateChatsController(model), true));
|
||||
UIButton send = UIButton.FromType(UIButtonType.System); send.SetTitle("Send", UIControlState.Normal); send.AccessibilityLabel = "Send message"; send.TranslatesAutoresizingMaskIntoConstraints = false; send.TouchUpInside += (_, _) => { model.SendText(compose.Text ?? ""); compose.Text = ""; };
|
||||
compose.TranslatesAutoresizingMaskIntoConstraints = false; View.AddSubviews(log, compose, send);
|
||||
NSLayoutConstraint.ActivateConstraints([log.TopAnchor.ConstraintEqualTo(View.SafeAreaLayoutGuide.TopAnchor), log.LeadingAnchor.ConstraintEqualTo(View.LeadingAnchor, 12), log.TrailingAnchor.ConstraintEqualTo(View.TrailingAnchor, -12), compose.TopAnchor.ConstraintEqualTo(log.BottomAnchor, 8), compose.LeadingAnchor.ConstraintEqualTo(View.LeadingAnchor, 12), compose.BottomAnchor.ConstraintEqualTo(View.KeyboardLayoutGuide.TopAnchor, -8), send.LeadingAnchor.ConstraintEqualTo(compose.TrailingAnchor, 8), send.TrailingAnchor.ConstraintEqualTo(View.TrailingAnchor, -12), send.CenterYAnchor.ConstraintEqualTo(compose.CenterYAnchor), compose.WidthAnchor.ConstraintGreaterThanOrEqualTo(120)]); Refresh();
|
||||
}
|
||||
private void Refresh()
|
||||
{
|
||||
IEnumerable<(DateTime Time, string Text)> chat = model.Messages.Select(message => (message.Timestamp, $"{(message.Private ? "[private] " : "")}{message.Sender}: {message.Text}"));
|
||||
IEnumerable<(DateTime Time, string Text)> chat = model.Messages.Where(message => !message.Private).Select(message => (message.Timestamp, $"{message.Sender}: {message.Text}"));
|
||||
IEnumerable<(DateTime Time, string Text)> activity = model.Activity.Select(value => (value.Timestamp, $"• {value.Text}"));
|
||||
log.Text = string.Join("\n", chat.Concat(activity).OrderBy(value => value.Time).Select(value => $"[{value.Time:t}] {value.Text}")); if (log.Text.Length > 0) log.ScrollRangeToVisible(new(log.Text.Length - 1, 1));
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PrivateChatsController : UITableViewController
|
||||
{
|
||||
private readonly AppModel model;
|
||||
private IReadOnlyList<(uint Id, string Name, ChatEntry? Latest, bool Online)> Peers
|
||||
{
|
||||
get
|
||||
{
|
||||
var online = model.Users.Where(user => user.Id != model.SelfUserId).ToDictionary(user => user.Id);
|
||||
var history = model.Messages.Where(message => message.Private).GroupBy(message => message.PeerUserId).ToDictionary(group => group.Key, group => group.OrderByDescending(message => message.Timestamp).First());
|
||||
return online.Keys.Concat(history.Keys).Distinct().Select(id =>
|
||||
{
|
||||
online.TryGetValue(id, out User? user); history.TryGetValue(id, out ChatEntry? latest);
|
||||
string name = user?.Nickname ?? latest?.Peer ?? $"User {id}"; return (Id: id, Name: name, Latest: latest, Online: user is not null);
|
||||
}).OrderByDescending(peer => peer.Latest?.Timestamp ?? DateTime.MinValue).ThenBy(peer => peer.Name).ToArray();
|
||||
}
|
||||
}
|
||||
internal PrivateChatsController(AppModel model) { this.model = model; Title = "Private Chats"; model.Changed += Reload; }
|
||||
public override void ViewDidLoad() { base.ViewDidLoad(); TableView.RegisterClassForCellReuse(typeof(UITableViewCell), "private-peer"); Reload(); }
|
||||
public override nint RowsInSection(UITableView tableView, nint section) => Peers.Count;
|
||||
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
|
||||
{
|
||||
(uint _, string name, ChatEntry? latest, bool online) = Peers[indexPath.Row]; UITableViewCell cell = tableView.DequeueReusableCell("private-peer", indexPath); var content = cell.DefaultContentConfiguration;
|
||||
content.Text = name; content.SecondaryText = latest?.Text ?? "Start a conversation"; content.Image = UIImage.GetSystemImage(online ? "person.crop.circle.fill" : "person.crop.circle.badge.xmark"); cell.ContentConfiguration = content;
|
||||
cell.Accessory = UITableViewCellAccessory.DisclosureIndicator; cell.AccessibilityLabel = $"{name}, {(online ? "online" : "offline")}, {content.SecondaryText}"; return cell;
|
||||
}
|
||||
public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
|
||||
{
|
||||
(uint id, string name, _, _) = Peers[indexPath.Row]; tableView.DeselectRow(indexPath, true); NavigationController?.PushViewController(new PrivateConversationController(model, id, name), true);
|
||||
}
|
||||
private void Reload() { if (IsViewLoaded) { TableView.ReloadData(); TableView.BackgroundView = Peers.Count == 0 ? new UILabel { Text = "No other users or private conversations", TextAlignment = UITextAlignment.Center, AccessibilityLabel = "No other users or private conversations" } : null; } }
|
||||
}
|
||||
|
||||
internal sealed class PrivateConversationController : UIViewController
|
||||
{
|
||||
private readonly AppModel model; private readonly uint peerUserId; private readonly string fallbackName;
|
||||
private readonly UITextView transcript = new(); private readonly UITextField compose = UiHelpers.Field("Private message"); private readonly UIButton send = UIButton.FromType(UIButtonType.System);
|
||||
private User? Peer => model.Users.FirstOrDefault(user => user.Id == peerUserId);
|
||||
internal PrivateConversationController(AppModel model, uint peerUserId, string fallbackName)
|
||||
{ this.model = model; this.peerUserId = peerUserId; this.fallbackName = fallbackName; Title = fallbackName; model.Changed += Refresh; }
|
||||
public override void ViewDidLoad()
|
||||
{
|
||||
base.ViewDidLoad(); View!.BackgroundColor = UIColor.SystemBackground; transcript.Editable = false; transcript.Font = UIFont.PreferredBody; transcript.AccessibilityLabel = $"Private conversation with {fallbackName}"; transcript.TranslatesAutoresizingMaskIntoConstraints = false;
|
||||
compose.AccessibilityLabel = $"Message to {fallbackName}"; compose.TranslatesAutoresizingMaskIntoConstraints = false;
|
||||
send.SetTitle("Send", UIControlState.Normal); send.AccessibilityLabel = $"Send message to {fallbackName}"; send.TranslatesAutoresizingMaskIntoConstraints = false; send.TouchUpInside += (_, _) => Send();
|
||||
NavigationItem.RightBarButtonItem = new("User", UIBarButtonItemStyle.Plain, (_, _) => NavigationController?.PushViewController(new UserDetailController(model, peerUserId), true)) { AccessibilityLabel = $"Audio and user settings for {fallbackName}" };
|
||||
View.AddSubviews(transcript, compose, send);
|
||||
NSLayoutConstraint.ActivateConstraints([transcript.TopAnchor.ConstraintEqualTo(View.SafeAreaLayoutGuide.TopAnchor), transcript.LeadingAnchor.ConstraintEqualTo(View.LeadingAnchor, 12), transcript.TrailingAnchor.ConstraintEqualTo(View.TrailingAnchor, -12), compose.TopAnchor.ConstraintEqualTo(transcript.BottomAnchor, 8), compose.LeadingAnchor.ConstraintEqualTo(View.LeadingAnchor, 12), compose.BottomAnchor.ConstraintEqualTo(View.KeyboardLayoutGuide.TopAnchor, -8), send.LeadingAnchor.ConstraintEqualTo(compose.TrailingAnchor, 8), send.TrailingAnchor.ConstraintEqualTo(View.TrailingAnchor, -12), send.CenterYAnchor.ConstraintEqualTo(compose.CenterYAnchor), compose.WidthAnchor.ConstraintGreaterThanOrEqualTo(120)]); Refresh();
|
||||
}
|
||||
private void Send() { string text = compose.Text ?? ""; if (string.IsNullOrWhiteSpace(text) || Peer is null) return; model.SendText(text, peerUserId); compose.Text = ""; }
|
||||
private void Refresh()
|
||||
{
|
||||
if (!IsViewLoaded) return; string name = Peer?.Nickname ?? fallbackName; Title = name;
|
||||
IEnumerable<ChatEntry> messages = model.Messages.Where(message => message.Private && message.PeerUserId == peerUserId).OrderBy(message => message.Timestamp);
|
||||
transcript.Text = string.Join("\n", messages.Select(message => $"[{message.Timestamp:t}] {(message.SenderId == model.SelfUserId ? "You" : message.Sender)}: {message.Text}"));
|
||||
if (transcript.Text.Length > 0) transcript.ScrollRangeToVisible(new(transcript.Text.Length - 1, 1));
|
||||
bool online = Peer is not null; compose.Enabled = online; send.Enabled = online; NavigationItem.RightBarButtonItem!.Enabled = online;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user