Fixed all underlying issues with the "Answer" call.

This commit is contained in:
2026-04-06 16:08:37 -04:00
parent aa7f6597c4
commit 7d8755ca71
6 changed files with 134 additions and 38 deletions

View File

@@ -197,6 +197,55 @@ public partial class MainPage : ContentPage
return; return;
} }
if (type == "rtc_offer_updated" || type == "rtc_answer_updated" || type == "rtc_candidate_added" || type == "rtc_call_left")
{
var rtcNotification = JsonSerializer.Deserialize<RtcNotificationMessage>(e.Data);
if (rtcNotification is null)
return;
var notificationType = rtcNotification.Type ?? string.Empty;
var notificationChannelId = rtcNotification.ChannelId ?? string.Empty;
if (notificationChannelId != _currentChannelId)
return;
SafeSendRawToWebView("RTC notification received: " + notificationType + " for " + notificationChannelId);
MainThread.BeginInvokeOnMainThread(async () =>
{
switch (notificationType)
{
case "rtc_offer_updated":
{
var offer = await GetRtcOffer();
await SendRtcSignalToJsAsync(offer);
break;
}
case "rtc_answer_updated":
{
var answer = await ServerAPI.GetAnswerForChannelAsync(_currentChannelId);
if (answer is not null)
{
var json = JsonSerializer.Serialize(answer);
await hybridWebView.EvaluateJavaScriptAsync($"window.AnswerCallback({json})");
}
break;
}
case "rtc_candidate_added":
{
break;
}
case "rtc_call_left":
{
SafeSendRawToWebView("RTC call left notification received.");
break;
}
}
});
return;
}
if (type != "encrypted_chat") if (type != "encrypted_chat")
return; return;
@@ -358,15 +407,28 @@ public partial class MainPage : ContentPage
public async Task<bool> JoinRtcChannel() public async Task<bool> JoinRtcChannel()
{ {
//TODO: get bool value for if channel ID has an active call if (string.IsNullOrWhiteSpace(_currentChannelId))
//TODO: Join RTC using current channel ID return false;
SafeSendRawToWebView($"Attempting to join RTC Channel {_currentChannelName}");
bool active = await ServerAPI.GetIsChannelActiveAsync(_currentChannelId);
SafeSendRawToWebView($"Rtc Channel {_currentChannelName} is active: {active}");
return active;
//await hybridWebView.EvaluateJavaScriptAsync($"window.channelCallJoin({active})");
_wsc.Send($"RTC_JOIN_CHANNEL|{_username}|{_currentChannelId}");
SafeSendRawToWebView($"Attempting to join RTC Channel {_currentChannelName} | {_currentChannelId} ");
bool active = await ServerAPI.GetIsChannelActiveAsync(_currentChannelId);
SafeSendRawToWebView($"Rtc Channel {_currentChannelName} | {_currentChannelId} is active: {active}");
return active;
} }
public void LeaveRtcChannel()
{
if (string.IsNullOrWhiteSpace(_currentChannelId))
return;
_wsc.Send($"RTC_LEAVE_CHANNEL|{_username}|{_currentChannelId}");
}
public async void WriteRtcOffer(string json) public async void WriteRtcOffer(string json)
{ {
try try
@@ -391,7 +453,12 @@ public partial class MainPage : ContentPage
RtcDescription? offer = await ServerAPI.GetOffersForChannelAsync(_currentChannelId); RtcDescription? offer = await ServerAPI.GetOffersForChannelAsync(_currentChannelId);
return JsonSerializer.Serialize(offer); return JsonSerializer.Serialize(offer);
} }
public async void WriteRtcAnswer(string json) public async void WriteRtcAnswer(string json)
{
SafeSendRawToWebView("WriteRtcAnswer entered with: " + json);
try
{ {
RtcDescription? description = JsonSerializer.Deserialize<RtcDescription>(json); RtcDescription? description = JsonSerializer.Deserialize<RtcDescription>(json);
DBOffer answer = new DBOffer DBOffer answer = new DBOffer
@@ -401,11 +468,18 @@ public partial class MainPage : ContentPage
SessionDescription = description SessionDescription = description
}; };
await ServerAPI.PostAnswerAsync(answer); await ServerAPI.PostAnswerAsync(answer);
SafeSendRawToWebView("WriteRtcAnswer posted successfully");
}
catch (Exception ex)
{
SafeSendRawToWebView("WriteRtcAnswer failed: " + ex.Message);
}
} }
public async void AnswerCallback(RtcDescription answer) public async void AnswerCallback(RtcDescription answer)
{ {
await hybridWebView.EvaluateJavaScriptAsync($"window.AnswerCallback({answer})"); var json = JsonSerializer.Serialize(answer);
await hybridWebView.EvaluateJavaScriptAsync($"window.AnswerCallback({json})");
} }
private void OnSendMessageButtonClicked(object sender, EventArgs e) private void OnSendMessageButtonClicked(object sender, EventArgs e)

View File

@@ -0,0 +1,9 @@
namespace RelayClient.Models;
public sealed class RtcNotificationMessage
{
public string? Type { get; set; }
public string? ChannelId { get; set; }
public string? Username { get; set; }
public string? Direction { get; set; }
}

View File

@@ -231,8 +231,10 @@ async function channelCallJoin(activeCall)
const answer = await peerConnection.createAnswer(); const answer = await peerConnection.createAnswer();
await peerConnection.setLocalDescription(answer); await peerConnection.setLocalDescription(answer);
LogMessage(`Joining call with media answer: ${JSON.stringify(answer)}`); LogMessage("Joining call with media answer: " + JSON.stringify(answer));
await window.HybridWebView.InvokeDotNet("WriteRtcAnswer", [JSON.stringify(roomAnswer)]); LogMessage("Calling C# WriteRtcAnswer with: " + JSON.stringify(answer));
await window.HybridWebView.InvokeDotNet("WriteRtcAnswer", [JSON.stringify(answer)]);
LogMessage("C# WriteRtcAnswer invoked");
//TODO: Update offer in SurrealDB to include answer //TODO: Update offer in SurrealDB to include answer
} }
else else

View File

@@ -45,9 +45,14 @@ public class ServerAPI
return offer; return offer;
} }
public static async Task<Uri> PostAnswerAsync(DBOffer answer) public static async Task<Uri?> PostAnswerAsync(DBOffer answer)
{ {
HttpResponseMessage response = await client.PostAsJsonAsync("api/rtc/answer", answer); HttpResponseMessage response = await client.PostAsJsonAsync("api/rtc/answer", answer);
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine("PostAnswerAsync status: " + response.StatusCode);
Console.WriteLine("PostAnswerAsync body: " + body);
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
return response.Headers.Location; return response.Headers.Location;
} }
@@ -94,6 +99,18 @@ public class ServerAPI
return response.Headers.Location; return response.Headers.Location;
} }
public static async Task<RtcDescription?> GetAnswerForChannelAsync(string? channelId)
{
if (string.IsNullOrWhiteSpace(channelId))
return null;
HttpResponseMessage response = await client.GetAsync($"api/rtc/answer/{channelId}");
if (!response.IsSuccessStatusCode)
return null;
var json = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<RtcDescription>(json);
}
} }
public class RtcDescription public class RtcDescription

View File

@@ -49,15 +49,11 @@ public static class RtcEndpoints
// Store a new SDP answer for the specified channel call. // Store a new SDP answer for the specified channel call.
app.MapPost("/api/rtc/answer", async (RtcOffer request, RtcCallService rtcCallService) => app.MapPost("/api/rtc/answer", async (RtcOffer request, RtcCallService rtcCallService) =>
{ {
Console.WriteLine($"RTC answer received for channel {request.ChannelId} from {request.Username}");
await rtcCallService.WriteAnswerAsync(request.ChannelId, request.SessionDescription); await rtcCallService.WriteAnswerAsync(request.ChannelId, request.SessionDescription);
//DON'T FUCKING HARDCODE STRINGS INTO API REQUESTS
// await rtcCallService.WriteAnswerAsync( Console.WriteLine($"Broadcasting rtc_answer_updated for {request.ChannelId}");
// request.ChannelId,
// new RtcSessionDescription
// {
// Type = "answer",
// Sdp = request.Sdp
// });
RtcNotificationService.BroadcastToChannel(new RtcNotificationMessage RtcNotificationService.BroadcastToChannel(new RtcNotificationMessage
{ {

View File

@@ -124,13 +124,14 @@ public sealed class RtcCallService
/// <returns> /// <returns>
/// A list of answers for the channel ordered from oldest to newest. /// A list of answers for the channel ordered from oldest to newest.
/// </returns> /// </returns>
public async Task<List<RtcAnswer>> GetAnswersAsync(string channelId) public async Task<List<RtcSessionDescription>> GetAnswersAsync(string channelId)
{ {
var answers = await _db.Select<RtcAnswer>("rtc_answers"); var activeCall = await GetActiveCallAsync(channelId);
return answers
.Where(x => x.ChannelId == channelId) if (activeCall?.Answer is null)
.OrderBy(x => x.CreatedAt) return [];
.ToList();
return [activeCall.Answer];
} }
/// <summary> /// <summary>
@@ -140,13 +141,10 @@ public sealed class RtcCallService
/// <returns> /// <returns>
/// The newest answer for the channel, or null if no answer exists. /// The newest answer for the channel, or null if no answer exists.
/// </returns> /// </returns>
public async Task<RtcAnswer?> GetLatestAnswerAsync(string channelId) public async Task<RtcSessionDescription?> GetLatestAnswerAsync(string channelId)
{ {
var answers = await _db.Select<RtcAnswer>("rtc_answers"); var activeCall = await GetActiveCallAsync(channelId);
return answers return activeCall?.Answer;
.Where(x => x.ChannelId == channelId)
.OrderByDescending(x => x.CreatedAt)
.FirstOrDefault();
} }
/// <summary> /// <summary>