Skip to content

Commit 313f845

Browse files
committed
Stabilize reader recording controls (#55)
1 parent 11861eb commit 313f845

7 files changed

Lines changed: 59 additions & 26 deletions

File tree

src/PrompterOne.Shared/Teleprompter/Pages/TeleprompterPage.ReaderRecording.cs

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -150,22 +150,28 @@ await Diagnostics.RunAsync(
150150

151151
private async Task StopReaderRecordingAsync(bool updateUi)
152152
{
153-
await StopReaderRecordingLevelMonitorAsync();
154-
155153
if (!_isReaderRecording)
156154
{
155+
await StopReaderRecordingLevelMonitorAsync();
157156
return;
158157
}
159158

160-
var snapshot = await ReaderRecordingInterop.StopAsync();
161-
_isReaderRecording = false;
162-
_readerRecordingAudioLevelPercent = 0;
163-
_readerRecordingStatus = string.IsNullOrWhiteSpace(snapshot.FileName)
164-
? Text(UiTextKey.TeleprompterRecordingReadyStatus)
165-
: string.Format(
166-
CultureInfo.CurrentCulture,
167-
Text(UiTextKey.TeleprompterRecordingSavedStatusFormat),
168-
snapshot.FileName);
159+
try
160+
{
161+
var snapshot = await ReaderRecordingInterop.StopAsync();
162+
_isReaderRecording = false;
163+
_readerRecordingAudioLevelPercent = 0;
164+
_readerRecordingStatus = string.IsNullOrWhiteSpace(snapshot.FileName)
165+
? Text(UiTextKey.TeleprompterRecordingReadyStatus)
166+
: string.Format(
167+
CultureInfo.CurrentCulture,
168+
Text(UiTextKey.TeleprompterRecordingSavedStatusFormat),
169+
snapshot.FileName);
170+
}
171+
finally
172+
{
173+
await StopReaderRecordingLevelMonitorAsync();
174+
}
169175

170176
if (updateUi)
171177
{

src/PrompterOne.Shared/Teleprompter/Pages/TeleprompterReaderTransport.razor

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,12 +132,12 @@
132132
</div>
133133

134134
<div class="rd-recording-panel"
135-
data-active="@BuildBooleanDataAttribute(IsRecordingActive)"
135+
data-active="@BuildStateDataAttribute(IsRecordingActive)"
136136
data-test="@UiTestIds.Teleprompter.RecordingPanel">
137137
<button class="rd-record-btn"
138138
type="button"
139139
aria-label="@RecordingToggleTooltip"
140-
data-active="@BuildBooleanDataAttribute(IsRecordingActive)"
140+
data-active="@BuildStateDataAttribute(IsRecordingActive)"
141141
data-test="@UiTestIds.Teleprompter.RecordingToggle"
142142
@onclick="OnRecordingToggle">
143143
<span class="rd-record-dot" aria-hidden="true"></span>
@@ -226,6 +226,8 @@
226226

227227
private static string BuildBooleanDataAttribute(bool value) => value ? "true" : "false";
228228

229+
private static string BuildStateDataAttribute(bool value) => value ? "active" : "inactive";
230+
229231
private bool ShowsRecordingCameraSelect =>
230232
string.Equals(RecordingMode, VideoAndAudioMode, StringComparison.Ordinal);
231233

src/PrompterOne.Shared/wwwroot/design/modules/reader/20-controls.css

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,11 @@
470470
display: flex;
471471
align-items: center;
472472
gap: 8px;
473+
max-width: calc(100vw - 24px);
473474
padding: 6px 12px;
475+
box-sizing: border-box;
476+
overflow-x: auto;
477+
overflow-y: hidden;
474478
background: rgba(20,18,12,.84);
475479
backdrop-filter: blur(18px);
476480
border: 1px solid var(--gold-14);
@@ -604,7 +608,7 @@
604608
color: #E8D5B0;
605609
}
606610

607-
.rd-record-btn[data-active="true"] {
611+
.rd-record-btn[data-active="active"] {
608612
border-color: rgba(235,95,76,.42);
609613
color: #ffd8cf;
610614
}

src/PrompterOne.Shared/wwwroot/design/modules/reader/30-responsive.css

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
11
/* Module: 30-responsive.css */
22
/* Phone-width and low-height teleprompter layout compaction. */
33

4+
@media (max-width: 960px) {
5+
.rd-controls {
6+
left: 12px;
7+
right: 12px;
8+
transform: none;
9+
justify-content: flex-start;
10+
}
11+
12+
.rd-recording-panel {
13+
flex: 1 1 auto;
14+
min-width: 0;
15+
max-width: 100%;
16+
overflow-x: auto;
17+
}
18+
}
19+
420
@media (max-width: 430px) {
521
.rd-progress-shell {
622
display: none;

src/PrompterOne.Shared/wwwroot/media/browser-media.js

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
const microphoneMonitorLevelMultiplier = 2800;
88
const monitorMap = new Map();
99
const pendingCameraCaptureMap = new Map();
10-
const readerRecordingTimesliceMs = 500;
10+
const readerRecordingTimesliceMs = 100;
1111
const recordingDefaultExtension = "webm";
1212
const readerRecordingMimeCandidates = Object.freeze({
1313
audio: [
@@ -723,16 +723,18 @@
723723
}
724724

725725
readerRecordingSession = null;
726-
const stopped = new Promise((resolve, reject) => {
727-
session.recorder.addEventListener("stop", resolve, { once: true });
728-
session.recorder.addEventListener("error", event => reject(event?.error ?? new Error("MediaRecorder failed.")), { once: true });
729-
});
730-
731726
if (session.recorder.state !== "inactive") {
727+
const stopped = new Promise((resolve, reject) => {
728+
session.recorder.addEventListener("stop", resolve, { once: true });
729+
session.recorder.addEventListener("error", event => reject(event?.error ?? new Error("MediaRecorder failed.")), { once: true });
730+
});
731+
try {
732+
session.recorder.requestData();
733+
} catch {
734+
}
732735
session.recorder.stop();
736+
await stopped;
733737
}
734-
735-
await stopped;
736738
const blob = new Blob(session.chunks, { type: session.mimeType });
737739
await saveReaderRecordingBlob(blob, session.fileName, session.mimeType);
738740

tests/PrompterOne.Web.Tests/Teleprompter/TeleprompterControlRailTests.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,14 +97,15 @@ public void TeleprompterPage_RendersSeparateBackgroundCameraAndRecordingControls
9797
var microphoneSelect = cut.FindByTestId(UiTestIds.Teleprompter.RecordingMicrophoneSelect);
9898
var backgroundCameraToggle = cut.FindByTestId(UiTestIds.Teleprompter.CameraToggle);
9999

100-
Assert.Equal("false", recordingPanel.GetAttribute("data-active"));
101-
Assert.Equal("false", recordingToggle.GetAttribute("data-active"));
100+
Assert.Equal("inactive", recordingPanel.GetAttribute("data-active"));
101+
Assert.Equal("inactive", recordingToggle.GetAttribute("data-active"));
102102
Assert.Equal("video-audio", recordingMode.GetAttribute("value"));
103103
Assert.False(string.IsNullOrWhiteSpace(cameraSelect.GetAttribute("value")));
104104
Assert.False(string.IsNullOrWhiteSpace(microphoneSelect.GetAttribute("value")));
105-
Assert.NotEqual(
105+
Assert.False(string.Equals(
106106
backgroundCameraToggle.GetAttribute("data-test"),
107-
recordingToggle.GetAttribute("data-test"));
107+
recordingToggle.GetAttribute("data-test"),
108+
StringComparison.Ordinal));
108109
});
109110
}
110111

tests/PrompterOne.Web.UITests.Reader/Teleprompter/TeleprompterRecordingFlowTests.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ await page.WaitForFunctionAsync(
2929
"([testId, minimumLevel]) => Number(document.querySelector(`[data-test=\"${testId}\"]`)?.dataset.level ?? '0') >= minimumLevel",
3030
new object[] { UiTestIds.Teleprompter.RecordingLevel, BrowserTestConstants.Media.LiveLevelThreshold },
3131
new() { Timeout = BrowserTestConstants.Timing.ExtendedVisibleTimeoutMs });
32+
await page.WaitForTimeoutAsync(900);
3233

3334
await page.GetByTestId(UiTestIds.Teleprompter.RecordingToggle).ClickAsync();
3435
await page.WaitForFunctionAsync(
@@ -63,6 +64,7 @@ await page.WaitForFunctionAsync(
6364
new object[] { BrowserTestConstants.Media.PrimaryMicrophoneId },
6465
new() { Timeout = BrowserTestConstants.Timing.ExtendedVisibleTimeoutMs });
6566
await Assert.That(await page.EvaluateAsync<int>(BrowserTestConstants.Media.GetActiveVideoTrackCountScript)).IsEqualTo(0);
67+
await page.WaitForTimeoutAsync(1500);
6668

6769
await page.GetByTestId(UiTestIds.Teleprompter.RecordingToggle).ClickAsync();
6870
await page.WaitForFunctionAsync(

0 commit comments

Comments
 (0)