Refined the recording Live Activity presentation in the Widget target.
This commit is contained in:
@@ -8,66 +8,89 @@ import WidgetKit
|
||||
/// button while recording; discard, resume, and send buttons while paused; and only a discard button while the recorded input
|
||||
/// is being processed.
|
||||
struct RecordingControls: View {
|
||||
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
|
||||
/// The dynamic content of the Live Activity to render.
|
||||
let state: RecordingActivityAttributes.ContentState
|
||||
|
||||
|
||||
// MARK: Body
|
||||
|
||||
|
||||
var body: some View {
|
||||
HStack(
|
||||
spacing: Constant.Spacing.stack
|
||||
) {
|
||||
switch state.state {
|
||||
case .recording:
|
||||
Button(intent: ToggleRecordingIntent()) {
|
||||
Image.Symbol.pause
|
||||
.resizable()
|
||||
}
|
||||
.buttonStyle(.action(
|
||||
type: .default
|
||||
))
|
||||
pauseButton
|
||||
|
||||
case .paused:
|
||||
Button(intent: DiscardRecordingIntent()) {
|
||||
Image.Symbol.discard
|
||||
.resizable()
|
||||
}
|
||||
.buttonStyle(.action(
|
||||
type: .destructive
|
||||
))
|
||||
|
||||
Button(intent: ToggleRecordingIntent()) {
|
||||
Image.Symbol.microphone
|
||||
.resizable()
|
||||
}
|
||||
.buttonStyle(.action(
|
||||
type: .default
|
||||
))
|
||||
|
||||
Button(intent: ProcessRecordingIntent()) {
|
||||
Image.Symbol.send
|
||||
.resizable()
|
||||
}
|
||||
.buttonStyle(.action(
|
||||
type: .confirmative
|
||||
))
|
||||
discardButton
|
||||
recordButton
|
||||
processButton
|
||||
|
||||
case .processing:
|
||||
Button(intent: DiscardRecordingIntent()) {
|
||||
Image.Symbol.discard
|
||||
.resizable()
|
||||
}
|
||||
.buttonStyle(.action(
|
||||
type: .destructive
|
||||
))
|
||||
discardButton
|
||||
}
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Subviews
|
||||
|
||||
private extension RecordingControls {
|
||||
|
||||
// MARK: Computed
|
||||
|
||||
/// The discard button, shown while the recording is paused or being processed, that throws the recording away.
|
||||
var discardButton: some View {
|
||||
Button(intent: DiscardRecordingIntent()) {
|
||||
Image.Symbol.discard
|
||||
.resizable()
|
||||
}
|
||||
.buttonStyle(.action(
|
||||
type: .destructive
|
||||
))
|
||||
.accessibilityLabel(Text("view.recording-controls.button.discard"))
|
||||
}
|
||||
|
||||
/// The pause button, shown while recording, that pauses the ongoing recording.
|
||||
var pauseButton: some View {
|
||||
Button(intent: ToggleRecordingIntent()) {
|
||||
Image.Symbol.pause
|
||||
.resizable()
|
||||
}
|
||||
.buttonStyle(.action(
|
||||
type: .default
|
||||
))
|
||||
.accessibilityLabel(Text("view.recording-controls.button.pause"))
|
||||
}
|
||||
|
||||
/// The send button, shown while the recording is paused, that sends it for processing.
|
||||
var processButton: some View {
|
||||
Button(intent: ProcessRecordingIntent()) {
|
||||
Image.Symbol.send
|
||||
.resizable()
|
||||
}
|
||||
.buttonStyle(.action(
|
||||
type: .confirmative
|
||||
))
|
||||
.accessibilityLabel(Text("view.recording-controls.button.send"))
|
||||
}
|
||||
|
||||
/// The resume button, shown while the recording is paused, that resumes it.
|
||||
var recordButton: some View {
|
||||
Button(intent: ToggleRecordingIntent()) {
|
||||
Image.Symbol.microphone
|
||||
.resizable()
|
||||
}
|
||||
.buttonStyle(.action(
|
||||
type: .default
|
||||
))
|
||||
.accessibilityLabel(Text("view.recording-controls.button.resume"))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
@@ -5,21 +5,21 @@ import WidgetKit
|
||||
/// The Lock Screen banner of the recording Live Activity: a centered stack of the timer of the elapsed recording time, the label
|
||||
/// naming the current state, and the control buttons.
|
||||
struct RecordingLiveActivityView: View {
|
||||
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
|
||||
/// The dynamic content of the Live Activity to render.
|
||||
let state: RecordingActivityAttributes.ContentState
|
||||
|
||||
|
||||
// MARK: Body
|
||||
|
||||
|
||||
var body: some View {
|
||||
VStack(
|
||||
alignment: .center,
|
||||
spacing: Constant.Spacing.stack
|
||||
) {
|
||||
RecordingStateLabel(state: state)
|
||||
|
||||
|
||||
RecordingTimerText(state: state)
|
||||
.font(.largeTitle)
|
||||
.fontWeight(.bold)
|
||||
@@ -27,7 +27,7 @@ struct RecordingLiveActivityView: View {
|
||||
RecordingControls(state: state)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
@@ -5,14 +5,14 @@ import WidgetKit
|
||||
/// The label naming the current state of the recording flow, next to an icon matching that state: a microphone while recording,
|
||||
/// a pause symbol while paused, and a send symbol while the recorded input is being processed.
|
||||
struct RecordingStateLabel: View {
|
||||
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
|
||||
/// The dynamic content of the Live Activity to render.
|
||||
let state: RecordingActivityAttributes.ContentState
|
||||
|
||||
|
||||
// MARK: Body
|
||||
|
||||
|
||||
var body: some View {
|
||||
Label {
|
||||
switch state.state {
|
||||
@@ -24,21 +24,12 @@ struct RecordingStateLabel: View {
|
||||
Text(.viewRecordingStateLabelStateProcessing)
|
||||
}
|
||||
} icon: {
|
||||
switch state.state {
|
||||
case .recording:
|
||||
Image.Symbol.microphone
|
||||
.foregroundStyle(.yellow)
|
||||
case .paused:
|
||||
Image.Symbol.pause
|
||||
.foregroundStyle(.yellow)
|
||||
case .processing:
|
||||
Image.Symbol.send
|
||||
.foregroundStyle(.green)
|
||||
}
|
||||
state.state.symbol
|
||||
.foregroundStyle(state.state.tint)
|
||||
}
|
||||
.font(.body)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
@@ -5,14 +5,14 @@ import WidgetKit
|
||||
/// The timer of the elapsed recording time, formatted as `mm:ss`: counted up by the system from the content state's anchor when
|
||||
/// one is given, and frozen at the reported elapsed time otherwise.
|
||||
struct RecordingTimerText: View {
|
||||
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
|
||||
/// The dynamic content of the Live Activity to render.
|
||||
let state: RecordingActivityAttributes.ContentState
|
||||
|
||||
|
||||
// MARK: Body
|
||||
|
||||
|
||||
var body: some View {
|
||||
if let anchor = state.anchor {
|
||||
Text(
|
||||
@@ -29,20 +29,20 @@ struct RecordingTimerText: View {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
private extension RecordingTimerText {
|
||||
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
|
||||
/// The format of the timer: minutes and seconds, as `mm:ss`.
|
||||
var timerFormat: Duration.TimeFormatStyle {
|
||||
.time(pattern: .minuteSecond(padMinuteToLength: 2))
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
Reference in New Issue
Block a user