mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-05 11:58:32 +00:00
Voice messages UI polish (#1959)
* Add VoiceMessageButton * Add VoiceMessageRecordBadge * Make composer transition faster * Use VoiceMessageButton * Adjust recording badge * Update localisation key * Remove animation in VoiceMessageButton * Fix preview tests * Cleanup * Update preview tests
This commit is contained in:
@@ -85,8 +85,8 @@ struct CompletionSuggestionView: View {
|
||||
}
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
var topPadding: CGFloat = isFirst ? Constants.topPadding : Constants.listItemPadding
|
||||
var bottomPadding: CGFloat = Constants.listItemPadding
|
||||
let topPadding: CGFloat = isFirst ? Constants.topPadding : Constants.listItemPadding
|
||||
let bottomPadding: CGFloat = Constants.listItemPadding
|
||||
|
||||
return content
|
||||
.padding(.top, topPadding)
|
||||
|
||||
@@ -94,7 +94,7 @@ struct ComposerToolbar: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.animation(.elementDefault, value: context.viewState.composerMode)
|
||||
.animation(.linear(duration: 0.15), value: context.viewState.composerMode)
|
||||
}
|
||||
|
||||
private var bottomBar: some View {
|
||||
|
||||
@@ -29,9 +29,7 @@ struct VoiceMessagePreviewComposer: View {
|
||||
let onPlay: () -> Void
|
||||
let onPause: () -> Void
|
||||
let onSeek: (Double) -> Void
|
||||
|
||||
@ScaledMetric private var playPauseButtonSize = 32
|
||||
@ScaledMetric private var playPauseImagePadding = 8
|
||||
|
||||
@State var dragState: WaveformViewDragState = .inactive
|
||||
|
||||
private static let elapsedTimeFormatter: DateFormatter = {
|
||||
@@ -55,7 +53,9 @@ struct VoiceMessagePreviewComposer: View {
|
||||
var body: some View {
|
||||
HStack {
|
||||
HStack {
|
||||
playPauseButton
|
||||
VoiceMessageButton(state: .init(state: playerState.playbackState),
|
||||
size: .small,
|
||||
action: onPlayPause)
|
||||
Text(timeLabelContent)
|
||||
.lineLimit(1)
|
||||
.font(.compound.bodySMSemibold)
|
||||
@@ -112,32 +112,6 @@ struct VoiceMessagePreviewComposer: View {
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var playPauseButton: some View {
|
||||
Button {
|
||||
onPlayPause()
|
||||
} label: {
|
||||
ZStack {
|
||||
Circle()
|
||||
.foregroundColor(.compound.bgCanvasDefault)
|
||||
if playerState.playbackState == .loading {
|
||||
ProgressView()
|
||||
} else {
|
||||
Image(asset: playerState.playbackState == .playing ? Asset.Images.mediaPause : Asset.Images.mediaPlay)
|
||||
.resizable()
|
||||
.padding(playPauseImagePadding)
|
||||
.offset(x: playerState.playbackState == .playing ? 0 : 2)
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.foregroundColor(.compound.iconSecondary)
|
||||
.accessibilityLabel(playerState.playbackState == .playing ? L10n.a11yPause : L10n.a11yPlay)
|
||||
}
|
||||
}
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.disabled(playerState.playbackState == .loading)
|
||||
.frame(width: playPauseButtonSize, height: playPauseButtonSize)
|
||||
}
|
||||
|
||||
private func onPlayPause() {
|
||||
if playerState.playbackState == .playing {
|
||||
onPause()
|
||||
|
||||
@@ -43,9 +43,9 @@ struct VoiceMessageRecordingView: View {
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
Circle()
|
||||
VoiceMessageRecordingBadge()
|
||||
.frame(width: recordingIndicatorSize, height: recordingIndicatorSize)
|
||||
.foregroundColor(.red)
|
||||
|
||||
Text(timeLabelContent)
|
||||
.lineLimit(1)
|
||||
.font(.compound.bodySMSemibold)
|
||||
@@ -61,6 +61,21 @@ struct VoiceMessageRecordingView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private struct VoiceMessageRecordingBadge: View {
|
||||
@State private var opacity: CGFloat = 0
|
||||
|
||||
var body: some View {
|
||||
Circle()
|
||||
.foregroundColor(.red)
|
||||
.opacity(opacity)
|
||||
.onAppear {
|
||||
withElementAnimation(.easeOut(duration: 1).repeatForever(autoreverses: true)) {
|
||||
opacity = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct VoiceMessageRecordingView_Previews: PreviewProvider, TestablePreview {
|
||||
static let recorderState = AudioRecorderState()
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ class StaticLocationScreenViewModel: StaticLocationScreenViewModelType, StaticLo
|
||||
let action: () -> Void = { [weak self] in self?.actionsSubject.send(.openSystemSettings) }
|
||||
state.bindings.alertInfo = .init(locationSharingViewError: .missingAuthorization,
|
||||
primaryButton: .init(title: L10n.actionNotNow, role: .cancel, action: nil),
|
||||
secondaryButton: .init(title: L10n.actionOpenSettings, action: action))
|
||||
secondaryButton: .init(title: L10n.commonSettings, action: action))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -962,7 +962,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
state.bindings.confirmationAlertInfo = .init(id: .init(),
|
||||
title: L10n.dialogPermissionMicrophoneTitleIos(InfoPlistReader.main.bundleDisplayName),
|
||||
message: L10n.dialogPermissionMicrophoneDescriptionIos,
|
||||
primaryButton: .init(title: L10n.actionOpenSettings, action: { [weak self] in self?.openSystemSettings() }),
|
||||
primaryButton: .init(title: L10n.commonSettings, action: { [weak self] in self?.openSystemSettings() }),
|
||||
secondaryButton: .init(title: L10n.actionNotNow, role: .cancel, action: nil))
|
||||
default:
|
||||
MXLog.error("failed to start voice message recording: \(error)")
|
||||
|
||||
Reference in New Issue
Block a user