Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Console/Console.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<LangVersion>Preview</LangVersion>
<GenerateDocumentationFile>false</GenerateDocumentationFile>
<PackageId>dotnet-whatsapp</PackageId>
<Description>A WhatsApp simulator client that complements Devlooped.WhatsApp.</Description>
Expand Down
34 changes: 30 additions & 4 deletions src/Console/Interactive.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Spectre.Console;
using Spectre.Console.Json;
using Spectre.Console.Rendering;
using Timer = System.Timers.Timer;

namespace Devlooped.WhatsApp;

Expand All @@ -24,6 +25,7 @@ partial class Interactive(IConfiguration configuration, IHttpClientFactory httpF
string? clientEndpoint;
HttpListener? listener;
bool needsNewline = true;
Timer? personTimer = null;

public Task StartAsync(CancellationToken cancellationToken)
{
Expand Down Expand Up @@ -85,6 +87,14 @@ async Task InputListener()
{
AnsiConsole.MarkupLine($":robot: Ready");
AnsiConsole.Markup($":person_beard: ");
personTimer = new Timer { AutoReset = false };
// Initially non-started
personTimer.Elapsed += (sender, e) =>
{
AnsiConsole.Markup($":person_beard: ");
needsNewline = true;
personTimer.Stop();
};

while (!cts.IsCancellationRequested)
{
Expand Down Expand Up @@ -124,8 +134,7 @@ async Task InputListener()
}
finally
{
AnsiConsole.Markup($":person_beard: ");
needsNewline = true;
RestartTimer();
}
}
}
Expand All @@ -148,6 +157,9 @@ async Task ResponseListener()
requestBody = await reader.ReadToEndAsync();
}

// Callbacks from the server push the head timer forward always,
// so given it some time to render responses.
RestartTimer();
await RenderAsync(requestBody);
}
catch (Exception ex)
Expand All @@ -171,6 +183,12 @@ async Task ResponseListener()
CancellationTokenSource typingCancellation = new();
Task? typingStatus;

void RestartTimer()
{
personTimer?.Start(); // no-op if already started
personTimer?.Interval = 500; // moves event .5'' into the future if already started
}

async Task RenderAsync(string json)
{
if (needsNewline)
Expand All @@ -183,6 +201,8 @@ async Task RenderAsync(string json)
{
AnsiConsole.Write(new Panel(payload)
{
Border = BoxBorder.None,
Padding = new Padding(0, 0, 0, 0),
Width = Math.Min(100, AnsiConsole.Profile.Width)
});
return;
Expand All @@ -209,6 +229,8 @@ async Task RenderAsync(string json)
while (!cts.IsCancellationRequested && !typingCancellation.IsCancellationRequested)
{
await Task.Delay(100);
// We should never let the head appear while we're still "typing"
RestartTimer();
}
});
return;
Expand Down Expand Up @@ -236,8 +258,8 @@ async Task RenderAsync(string json)
: TryCode(text.Trim(), false);

var grid = new Grid()
.AddColumn(new GridColumn().Width(2))
.AddColumn(new GridColumn().Width(80))
.AddColumn(new GridColumn().Width(2).Padding(0, 0))
.AddColumn(new GridColumn().Width(80).Padding(1, 0))
.AddRow(new Markup(emoji), body);

if (message is Client.InteractiveMessage interactive && interactive.Interactive.Action is { } node)
Expand All @@ -255,6 +277,8 @@ async Task RenderAsync(string json)

AnsiConsole.Write(new Panel(new JsonText(json))
{
Border = BoxBorder.None,
Padding = new Padding(0, 0, 0, 0),
Width = Math.Min(100, AnsiConsole.Profile.Width)
});
}
Expand Down Expand Up @@ -317,6 +341,8 @@ static IRenderable TryCode(string code, bool greyFallback = true)
var yaml = DictionaryConverter.ToYaml(DictionaryConverter.FromYaml(code), formatted: true);
return new Panel(yaml)
{
Border = BoxBorder.None,
Padding = new Padding(0, 0, 0, 0),
Width = Math.Min(80, AnsiConsole.Profile.Width)
};
}
Expand Down