From d63b5982ea213ba8b97487420e452d4468375dac Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Wed, 25 Oct 2023 10:50:05 +0200 Subject: [PATCH 01/18] Remove enable/disable long press actions --- ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift | 3 --- .../Sources/Screens/RoomScreen/RoomScreenViewModel.swift | 5 ----- .../VoiceMessages/VoiceMessageRoomTimelineView.swift | 2 -- 3 files changed, 10 deletions(-) diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift index d593578c7c..2c846d1938 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift @@ -80,9 +80,6 @@ enum RoomScreenViewAction { case scrolledToBottom - case enableLongPress(itemID: TimelineItemIdentifier) - case disableLongPress(itemID: TimelineItemIdentifier) - case playPauseAudio(itemID: TimelineItemIdentifier) case seekAudio(itemID: TimelineItemIdentifier, progress: Double) diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift index 2b7afb9977..0cbe372203 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift @@ -158,11 +158,6 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol Task { await timelineController.playPauseAudio(for: itemID) } case .seekAudio(let itemID, let progress): Task { await timelineController.seekAudio(for: itemID, progress: progress) } - case .enableLongPress(let itemID): - guard state.longPressDisabledItemID == itemID else { return } - state.longPressDisabledItemID = nil - case .disableLongPress(let itemID): - state.longPressDisabledItemID = itemID case let .endPoll(pollStartID): state.bindings.confirmationAlertInfo = .init(id: .init(), title: L10n.actionEndPoll, diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomTimelineView.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomTimelineView.swift index 61a702be75..82f3955961 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomTimelineView.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomTimelineView.swift @@ -52,9 +52,7 @@ struct VoiceMessageRoomTimelineView: View { resumePlaybackAfterScrubbing = true context.send(viewAction: .playPauseAudio(itemID: timelineItem.id)) } - context.send(viewAction: .disableLongPress(itemID: timelineItem.id)) } else { - context.send(viewAction: .enableLongPress(itemID: timelineItem.id)) if resumePlaybackAfterScrubbing { context.send(viewAction: .playPauseAudio(itemID: timelineItem.id)) resumePlaybackAfterScrubbing = false From 657a62beef90b28a75eaf7e89765b3c707207aac Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 26 Oct 2023 16:01:44 +0200 Subject: [PATCH 02/18] Working poc --- .../xcshareddata/swiftpm/Package.resolved | 9 ++ .../ProgressTapGestureModifier.swift | 72 ++++++++++++++ .../WaveformViewDragGestureModifier.swift | 98 ------------------- .../View/VoiceMessagePreviewComposer.swift | 5 +- .../VoiceMessageRoomPlaybackView.swift | 33 +++++-- ElementX/SupportingFiles/target.yml | 1 + project.yml | 4 +- 7 files changed, 113 insertions(+), 109 deletions(-) create mode 100644 ElementX/Sources/Other/VoiceMessage/ProgressTapGestureModifier.swift delete mode 100644 ElementX/Sources/Other/VoiceMessage/WaveformViewDragGestureModifier.swift diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index c0744d988e..1a5bb20087 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -196,6 +196,15 @@ "version" : "4.1.1" } }, + { + "identity" : "showtime", + "kind" : "remoteSourceControl", + "location" : "https://github.com/KaneCheshire/ShowTime", + "state" : { + "revision" : "f4e976931fc5f9b7a7d011f40c62a250c4ff53ab", + "version" : "2.5.3" + } + }, { "identity" : "swift-algorithms", "kind" : "remoteSourceControl", diff --git a/ElementX/Sources/Other/VoiceMessage/ProgressTapGestureModifier.swift b/ElementX/Sources/Other/VoiceMessage/ProgressTapGestureModifier.swift new file mode 100644 index 0000000000..3ab5c581ad --- /dev/null +++ b/ElementX/Sources/Other/VoiceMessage/ProgressTapGestureModifier.swift @@ -0,0 +1,72 @@ +// +// Copyright 2023 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import DSWaveformImageViews +import Foundation +import SwiftUI + +private struct ProgressTapGestureModifier: ViewModifier { + @Binding var progress: CGFloat + + func body(content: Content) -> some View { + GeometryReader { geometry in + content + .gesture(SpatialTapGesture() + .onEnded { tapGesture in + progress = tapGesture.location.x / geometry.size.width + }) + } + } +} + +extension View { + func progressTapGesture(progress: Binding) -> some View { + modifier(ProgressTapGestureModifier(progress: progress)) + } +} + +enum WaveformViewDragState: Equatable { + case inactive + case pressing(progress: Double) + case dragging(progress: Double) + + var progress: Double { + switch self { + case .inactive: + return .zero + case .pressing(let progress), .dragging(let progress): + return progress + } + } + + var isActive: Bool { + switch self { + case .inactive: + return false + case .pressing, .dragging: + return true + } + } + + var isDragging: Bool { + switch self { + case .inactive, .pressing: + return false + case .dragging: + return true + } + } +} diff --git a/ElementX/Sources/Other/VoiceMessage/WaveformViewDragGestureModifier.swift b/ElementX/Sources/Other/VoiceMessage/WaveformViewDragGestureModifier.swift deleted file mode 100644 index 734bb2be3b..0000000000 --- a/ElementX/Sources/Other/VoiceMessage/WaveformViewDragGestureModifier.swift +++ /dev/null @@ -1,98 +0,0 @@ -// -// Copyright 2023 New Vector Ltd -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import DSWaveformImageViews -import Foundation -import SwiftUI - -struct WaveformViewDragGestureModifier: ViewModifier { - @GestureState private var dragGestureState = WaveformViewDragState.inactive - @Binding var dragState: WaveformViewDragState - - let minimumDragDistance: Double - - func body(content: Content) -> some View { - GeometryReader { geometry in - content - .gesture(SpatialTapGesture() - .simultaneously(with: LongPressGesture()) - .sequenced(before: DragGesture(minimumDistance: minimumDragDistance, coordinateSpace: .local)) - .updating($dragGestureState) { value, state, _ in - switch value { - // (SpatialTap, LongPress) begins. - case .first(let spatialLongPress): - // Compute the progress with the spatialTap location - let progress = (spatialLongPress.first?.location ?? .zero).x / geometry.size.width - state = .pressing(progress: progress) - // Long press confirmed, dragging may begin. - case .second(let spatialLongPress, let drag) where spatialLongPress.second ?? false: - var progress: Double = dragState.progress - // Compute the progress with drag location - if let location = drag?.location { - progress = location.x / geometry.size.width - } - state = .dragging(progress: progress) - // Dragging ended or the long press cancelled. - default: - state = .inactive - } - }) - } - .onChange(of: dragGestureState) { value in - dragState = value - } - } -} - -extension View { - func waveformDragGesture(_ dragState: Binding, minimumDragDistance: Double = 0) -> some View { - modifier(WaveformViewDragGestureModifier(dragState: dragState, - minimumDragDistance: minimumDragDistance)) - } -} - -enum WaveformViewDragState: Equatable { - case inactive - case pressing(progress: Double) - case dragging(progress: Double) - - var progress: Double { - switch self { - case .inactive: - return .zero - case .pressing(let progress), .dragging(let progress): - return progress - } - } - - var isActive: Bool { - switch self { - case .inactive: - return false - case .pressing, .dragging: - return true - } - } - - var isDragging: Bool { - switch self { - case .inactive, .pressing: - return false - case .dragging: - return true - } - } -} diff --git a/ElementX/Sources/Screens/ComposerToolbar/View/VoiceMessagePreviewComposer.swift b/ElementX/Sources/Screens/ComposerToolbar/View/VoiceMessagePreviewComposer.swift index 1d27cab62e..9ae5f557df 100644 --- a/ElementX/Sources/Screens/ComposerToolbar/View/VoiceMessagePreviewComposer.swift +++ b/ElementX/Sources/Screens/ComposerToolbar/View/VoiceMessagePreviewComposer.swift @@ -31,7 +31,8 @@ struct VoiceMessagePreviewComposer: View { let onSeek: (Double) -> Void @State var dragState: WaveformViewDragState = .inactive - + @State private var tappedProgress: CGFloat = 0 + private static let elapsedTimeFormatter: DateFormatter = { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "mm:ss" @@ -64,7 +65,7 @@ struct VoiceMessagePreviewComposer: View { .fixedSize(horizontal: true, vertical: true) } waveformView - .waveformDragGesture($dragState) + .progressTapGesture(progress: $tappedProgress) .progressCursor(progress: playerState.progress) { WaveformCursorView(color: .compound.iconAccentTertiary) .opacity(showWaveformCursor ? 1 : 0) diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift index 74b47e33e3..ba6633cf10 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift @@ -43,7 +43,10 @@ struct VoiceMessageRoomPlaybackView: View { }() @State var dragState: WaveformViewDragState = .inactive - + + #warning("ag: fix me") + @State private var tappedProgress: CGFloat = 0 + var timeLabelContent: String { // Display the duration if progress is 0.0 let percent = playerState.progress > 0.0 ? playerState.progress : 1.0 @@ -73,13 +76,27 @@ struct VoiceMessageRoomPlaybackView: View { .monospacedDigit() .fixedSize(horizontal: true, vertical: true) } - waveformView - .waveformDragGesture($dragState) - .progressCursor(progress: playerState.progress) { - WaveformCursorView(color: .compound.iconAccentTertiary) - .opacity(showWaveformCursor ? 1 : 0) - .frame(width: waveformLineWidth) - } + GeometryReader { geometry in + waveformView + .progressTapGesture(progress: $tappedProgress) + .progressCursor(progress: playerState.progress) { + WaveformCursorView(color: .compound.iconAccentTertiary) + .opacity(showWaveformCursor ? 1 : 0) + .frame(width: waveformLineWidth) + .frame(width: 50) + .contentShape(Rectangle()) + .offset(x: -25, y: 0) + .gesture(DragGesture(coordinateSpace: .named("waveform")).onChanged { value in + let progress = value.location.x / geometry.size.width + MXLog.info("*** \(progress) - \(value.location)") + tappedProgress = progress + }) + } + } + .coordinateSpace(name: "waveform") + } + .onChange(of: tappedProgress) { newValue in + dragState = .dragging(progress: newValue) } .onChange(of: dragState) { newDragState in switch newDragState { diff --git a/ElementX/SupportingFiles/target.yml b/ElementX/SupportingFiles/target.yml index ab43ea33e5..733a56d85d 100644 --- a/ElementX/SupportingFiles/target.yml +++ b/ElementX/SupportingFiles/target.yml @@ -197,6 +197,7 @@ targets: - package: SwiftOGG - package: DSWaveformImage product: DSWaveformImageViews + - package: ShowTime sources: - path: ../Sources diff --git a/project.yml b/project.yml index 9a51accd56..8aa7ad6f6c 100644 --- a/project.yml +++ b/project.yml @@ -120,6 +120,8 @@ packages: DSWaveformImage: url: https://github.com/dmrschmidt/DSWaveformImage exactVersion: 14.1.1 - + ShowTime: + url: https://github.com/KaneCheshire/ShowTime + exactVersion: 2.5.3 From dd175fce13b6b3e5e4fad5307b2d665b8b4c5a0c Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 26 Oct 2023 16:36:32 +0200 Subject: [PATCH 03/18] Refactor interaction in VoiceMessageRoomPlaybackView --- .../ProgressTapGestureModifier.swift | 1 + .../VoiceMessageRoomPlaybackView.swift | 38 ++++++++----------- 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/ElementX/Sources/Other/VoiceMessage/ProgressTapGestureModifier.swift b/ElementX/Sources/Other/VoiceMessage/ProgressTapGestureModifier.swift index 3ab5c581ad..ddcb3b3fae 100644 --- a/ElementX/Sources/Other/VoiceMessage/ProgressTapGestureModifier.swift +++ b/ElementX/Sources/Other/VoiceMessage/ProgressTapGestureModifier.swift @@ -18,6 +18,7 @@ import DSWaveformImageViews import Foundation import SwiftUI +#warning("Delete me?") private struct ProgressTapGestureModifier: ViewModifier { @Binding var progress: CGFloat diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift index ba6633cf10..7fa2235146 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift @@ -26,9 +26,6 @@ struct VoiceMessageRoomPlaybackView: View { let onPlayPause: () -> Void let onSeek: (Double) -> Void let onScrubbing: (Bool) -> Void - - private let feedbackGenerator = UIImpactFeedbackGenerator(style: .heavy) - @State private var sendFeedback = false private static let elapsedTimeFormatter: DateFormatter = { let dateFormatter = DateFormatter() @@ -42,10 +39,7 @@ struct VoiceMessageRoomPlaybackView: View { return dateFormatter }() - @State var dragState: WaveformViewDragState = .inactive - - #warning("ag: fix me") - @State private var tappedProgress: CGFloat = 0 + @GestureState var dragState: WaveformViewDragState = .inactive var timeLabelContent: String { // Display the duration if progress is 0.0 @@ -78,7 +72,11 @@ struct VoiceMessageRoomPlaybackView: View { } GeometryReader { geometry in waveformView - .progressTapGesture(progress: $tappedProgress) + .gesture(SpatialTapGesture() + .updating($dragState) { tapGesture, dragState, _ in + let progress = tapGesture.location.x / geometry.size.width + dragState = .pressing(progress: progress) + }) .progressCursor(progress: playerState.progress) { WaveformCursorView(color: .compound.iconAccentTertiary) .opacity(showWaveformCursor ? 1 : 0) @@ -86,31 +84,25 @@ struct VoiceMessageRoomPlaybackView: View { .frame(width: 50) .contentShape(Rectangle()) .offset(x: -25, y: 0) - .gesture(DragGesture(coordinateSpace: .named("waveform")).onChanged { value in - let progress = value.location.x / geometry.size.width - MXLog.info("*** \(progress) - \(value.location)") - tappedProgress = progress - }) + .gesture(DragGesture(coordinateSpace: .named("waveform")) + .updating($dragState) { dragGesture, dragState, _ in + let progress = dragGesture.location.x / geometry.size.width + dragState = .dragging(progress: progress) + } + ) } } .coordinateSpace(name: "waveform") } - .onChange(of: tappedProgress) { newValue in - dragState = .dragging(progress: newValue) - } .onChange(of: dragState) { newDragState in switch newDragState { case .inactive: onScrubbing(false) - case .pressing: + case .pressing(let progress): onScrubbing(true) - feedbackGenerator.prepare() - sendFeedback = true + onSeek(max(0, min(progress, 1.0))) case .dragging(let progress): - if sendFeedback { - feedbackGenerator.impactOccurred() - sendFeedback = false - } + onScrubbing(true) onSeek(max(0, min(progress, 1.0))) } } From 0b9a67a3e6a244e6904bd0f328311ceb95e09a61 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 26 Oct 2023 16:39:59 +0200 Subject: [PATCH 04/18] Cleanup DateFormatter --- .../View/VoiceMessagePreviewComposer.swift | 16 ++--- .../VoiceMessageRoomPlaybackView.swift | 59 +++++-------------- 2 files changed, 23 insertions(+), 52 deletions(-) diff --git a/ElementX/Sources/Screens/ComposerToolbar/View/VoiceMessagePreviewComposer.swift b/ElementX/Sources/Screens/ComposerToolbar/View/VoiceMessagePreviewComposer.swift index 9ae5f557df..cf0bbace3e 100644 --- a/ElementX/Sources/Screens/ComposerToolbar/View/VoiceMessagePreviewComposer.swift +++ b/ElementX/Sources/Screens/ComposerToolbar/View/VoiceMessagePreviewComposer.swift @@ -33,18 +33,12 @@ struct VoiceMessagePreviewComposer: View { @State var dragState: WaveformViewDragState = .inactive @State private var tappedProgress: CGFloat = 0 - private static let elapsedTimeFormatter: DateFormatter = { - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "mm:ss" - return dateFormatter - }() - var timeLabelContent: String { // Display the duration if progress is 0.0 let percent = playerState.progress > 0.0 ? playerState.progress : 1.0 // If the duration is greater or equal 10 minutes, use the long format let elapsed = Date(timeIntervalSinceReferenceDate: playerState.duration * percent) - return Self.elapsedTimeFormatter.string(from: elapsed) + return DateFormatter.elapsedTimeFormatter.string(from: elapsed) } var showWaveformCursor: Bool { @@ -136,6 +130,14 @@ struct VoiceMessagePreviewComposer: View { } } +private extension DateFormatter { + static let elapsedTimeFormatter: DateFormatter = { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "mm:ss" + return dateFormatter + }() +} + struct VoiceMessagePreviewComposer_Previews: PreviewProvider, TestablePreview { static let playerState = AudioPlayerState(id: .recorderPreview, duration: 10.0, diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift index 7fa2235146..13fcc4e9a9 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift @@ -26,18 +26,6 @@ struct VoiceMessageRoomPlaybackView: View { let onPlayPause: () -> Void let onSeek: (Double) -> Void let onScrubbing: (Bool) -> Void - - private static let elapsedTimeFormatter: DateFormatter = { - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "m:ss" - return dateFormatter - }() - - private static let longElapsedTimeFormatter: DateFormatter = { - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "mm:ss" - return dateFormatter - }() @GestureState var dragState: WaveformViewDragState = .inactive @@ -47,9 +35,9 @@ struct VoiceMessageRoomPlaybackView: View { // If the duration is greater or equal 10 minutes, use the long format let elapsed = Date(timeIntervalSinceReferenceDate: playerState.duration * percent) if playerState.duration >= 600 { - return Self.longElapsedTimeFormatter.string(from: elapsed) + return DateFormatter.longElapsedTimeFormatter.string(from: elapsed) } else { - return Self.elapsedTimeFormatter.string(from: elapsed) + return DateFormatter.elapsedTimeFormatter.string(from: elapsed) } } @@ -131,37 +119,18 @@ struct VoiceMessageRoomPlaybackView: View { } } -private enum DragState: Equatable { - case inactive - case pressing(progress: Double) - case dragging(progress: Double) - - var progress: Double { - switch self { - case .inactive, .pressing: - return .zero - case .dragging(let progress): - return progress - } - } - - var isActive: Bool { - switch self { - case .inactive: - return false - case .pressing, .dragging: - return true - } - } - - var isDragging: Bool { - switch self { - case .inactive, .pressing: - return false - case .dragging: - return true - } - } +private extension DateFormatter { + static let elapsedTimeFormatter: DateFormatter = { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "m:ss" + return dateFormatter + }() + + static let longElapsedTimeFormatter: DateFormatter = { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "mm:ss" + return dateFormatter + }() } struct VoiceMessageRoomPlaybackView_Previews: PreviewProvider, TestablePreview { From a783d9ec7032b1907149ebf1bde3e0a5a4848199 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 26 Oct 2023 17:07:41 +0200 Subject: [PATCH 05/18] Fix VoiceMessagePreviewComposer --- .../ProgressTapGestureModifier.swift | 9 --- .../View/VoiceMessagePreviewComposer.swift | 58 +++++++++++++------ .../VoiceMessageRoomPlaybackView.swift | 12 ++-- 3 files changed, 47 insertions(+), 32 deletions(-) diff --git a/ElementX/Sources/Other/VoiceMessage/ProgressTapGestureModifier.swift b/ElementX/Sources/Other/VoiceMessage/ProgressTapGestureModifier.swift index ddcb3b3fae..3080a8da8f 100644 --- a/ElementX/Sources/Other/VoiceMessage/ProgressTapGestureModifier.swift +++ b/ElementX/Sources/Other/VoiceMessage/ProgressTapGestureModifier.swift @@ -53,15 +53,6 @@ enum WaveformViewDragState: Equatable { } } - var isActive: Bool { - switch self { - case .inactive: - return false - case .pressing, .dragging: - return true - } - } - var isDragging: Bool { switch self { case .inactive, .pressing: diff --git a/ElementX/Sources/Screens/ComposerToolbar/View/VoiceMessagePreviewComposer.swift b/ElementX/Sources/Screens/ComposerToolbar/View/VoiceMessagePreviewComposer.swift index cf0bbace3e..4736c5a8b9 100644 --- a/ElementX/Sources/Screens/ComposerToolbar/View/VoiceMessagePreviewComposer.swift +++ b/ElementX/Sources/Screens/ComposerToolbar/View/VoiceMessagePreviewComposer.swift @@ -42,7 +42,7 @@ struct VoiceMessagePreviewComposer: View { } var showWaveformCursor: Bool { - playerState.playbackState == .playing || dragState.isActive + playerState.playbackState == .playing || dragState.isDragging } var body: some View { @@ -58,25 +58,45 @@ struct VoiceMessagePreviewComposer: View { .monospacedDigit() .fixedSize(horizontal: true, vertical: true) } - waveformView - .progressTapGesture(progress: $tappedProgress) - .progressCursor(progress: playerState.progress) { - WaveformCursorView(color: .compound.iconAccentTertiary) - .opacity(showWaveformCursor ? 1 : 0) - .frame(width: waveformLineWidth) - } - .onChange(of: dragState) { dragState in - switch dragState { - case .inactive: - onScrubbing(false) - case .pressing(let progress): - onScrubbing(true) - onSeek(max(0, min(progress, 1.0))) - case .dragging(let progress): - onSeek(max(0, min(progress, 1.0))) + + GeometryReader { geometry in + waveformView + .gesture(SpatialTapGesture() + .onEnded { tapGesture in + let progress = tapGesture.location.x / geometry.size.width + dragState = .pressing(progress: progress) + }) + .progressCursor(progress: playerState.progress) { + WaveformCursorView(color: .compound.iconAccentTertiary) + .opacity(showWaveformCursor ? 1 : 0) + .frame(width: waveformLineWidth) + .frame(width: 50) + .contentShape(Rectangle()) + .offset(x: -25, y: 0) + .gesture(DragGesture(coordinateSpace: .named("waveform")) + .onChanged { dragGesture in + let progress = dragGesture.location.x / geometry.size.width + dragState = .dragging(progress: progress) + } + .onEnded { _ in + dragState = .inactive + } + ) } - self.dragState = dragState - } + } + .coordinateSpace(name: "waveform") + } + .onChange(of: dragState) { newDragState in + switch newDragState { + case .inactive: + onScrubbing(false) + case .pressing(let progress): + onScrubbing(false) + onSeek(max(0, min(progress, 1.0))) + case .dragging(let progress): + onScrubbing(true) + onSeek(max(0, min(progress, 1.0))) + } } .padding(.vertical, 4.0) .padding(.horizontal, 6.0) diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift index 13fcc4e9a9..42cc7a958e 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift @@ -27,7 +27,7 @@ struct VoiceMessageRoomPlaybackView: View { let onSeek: (Double) -> Void let onScrubbing: (Bool) -> Void - @GestureState var dragState: WaveformViewDragState = .inactive + @State var dragState: WaveformViewDragState = .inactive var timeLabelContent: String { // Display the duration if progress is 0.0 @@ -58,10 +58,11 @@ struct VoiceMessageRoomPlaybackView: View { .monospacedDigit() .fixedSize(horizontal: true, vertical: true) } + GeometryReader { geometry in waveformView .gesture(SpatialTapGesture() - .updating($dragState) { tapGesture, dragState, _ in + .onEnded { tapGesture in let progress = tapGesture.location.x / geometry.size.width dragState = .pressing(progress: progress) }) @@ -73,10 +74,13 @@ struct VoiceMessageRoomPlaybackView: View { .contentShape(Rectangle()) .offset(x: -25, y: 0) .gesture(DragGesture(coordinateSpace: .named("waveform")) - .updating($dragState) { dragGesture, dragState, _ in + .onChanged { dragGesture in let progress = dragGesture.location.x / geometry.size.width dragState = .dragging(progress: progress) } + .onEnded { _ in + dragState = .inactive + } ) } } @@ -87,7 +91,7 @@ struct VoiceMessageRoomPlaybackView: View { case .inactive: onScrubbing(false) case .pressing(let progress): - onScrubbing(true) + onScrubbing(false) onSeek(max(0, min(progress, 1.0))) case .dragging(let progress): onScrubbing(true) From 0d7c43f9cd2342aeb97057f3dc7e24169c14fd74 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 26 Oct 2023 17:10:10 +0200 Subject: [PATCH 06/18] Cleanup --- ...fier.swift => WaveformViewDragState.swift} | 25 ------------------- 1 file changed, 25 deletions(-) rename ElementX/Sources/Other/VoiceMessage/{ProgressTapGestureModifier.swift => WaveformViewDragState.swift} (62%) diff --git a/ElementX/Sources/Other/VoiceMessage/ProgressTapGestureModifier.swift b/ElementX/Sources/Other/VoiceMessage/WaveformViewDragState.swift similarity index 62% rename from ElementX/Sources/Other/VoiceMessage/ProgressTapGestureModifier.swift rename to ElementX/Sources/Other/VoiceMessage/WaveformViewDragState.swift index 3080a8da8f..471e967e79 100644 --- a/ElementX/Sources/Other/VoiceMessage/ProgressTapGestureModifier.swift +++ b/ElementX/Sources/Other/VoiceMessage/WaveformViewDragState.swift @@ -14,31 +14,6 @@ // limitations under the License. // -import DSWaveformImageViews -import Foundation -import SwiftUI - -#warning("Delete me?") -private struct ProgressTapGestureModifier: ViewModifier { - @Binding var progress: CGFloat - - func body(content: Content) -> some View { - GeometryReader { geometry in - content - .gesture(SpatialTapGesture() - .onEnded { tapGesture in - progress = tapGesture.location.x / geometry.size.width - }) - } - } -} - -extension View { - func progressTapGesture(progress: Binding) -> some View { - modifier(ProgressTapGestureModifier(progress: progress)) - } -} - enum WaveformViewDragState: Equatable { case inactive case pressing(progress: Double) From 266b767f2766ec038e9e44d51f796937b88604e0 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 26 Oct 2023 17:42:48 +0200 Subject: [PATCH 07/18] Delete WaveformViewDragState --- .../VoiceMessage/WaveformViewDragState.swift | 39 ------------------- .../View/VoiceMessagePreviewComposer.swift | 28 +++---------- .../VoiceMessageRoomPlaybackView.swift | 30 +++++--------- 3 files changed, 15 insertions(+), 82 deletions(-) delete mode 100644 ElementX/Sources/Other/VoiceMessage/WaveformViewDragState.swift diff --git a/ElementX/Sources/Other/VoiceMessage/WaveformViewDragState.swift b/ElementX/Sources/Other/VoiceMessage/WaveformViewDragState.swift deleted file mode 100644 index 471e967e79..0000000000 --- a/ElementX/Sources/Other/VoiceMessage/WaveformViewDragState.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// Copyright 2023 New Vector Ltd -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -enum WaveformViewDragState: Equatable { - case inactive - case pressing(progress: Double) - case dragging(progress: Double) - - var progress: Double { - switch self { - case .inactive: - return .zero - case .pressing(let progress), .dragging(let progress): - return progress - } - } - - var isDragging: Bool { - switch self { - case .inactive, .pressing: - return false - case .dragging: - return true - } - } -} diff --git a/ElementX/Sources/Screens/ComposerToolbar/View/VoiceMessagePreviewComposer.swift b/ElementX/Sources/Screens/ComposerToolbar/View/VoiceMessagePreviewComposer.swift index 4736c5a8b9..1278866a34 100644 --- a/ElementX/Sources/Screens/ComposerToolbar/View/VoiceMessagePreviewComposer.swift +++ b/ElementX/Sources/Screens/ComposerToolbar/View/VoiceMessagePreviewComposer.swift @@ -25,14 +25,12 @@ struct VoiceMessagePreviewComposer: View { @ScaledMetric private var waveformLineWidth = 2.0 @ScaledMetric private var waveformLinePadding = 2.0 @State private var resumePlaybackAfterScrubbing = false + @GestureState var isDragging = false let onPlay: () -> Void let onPause: () -> Void let onSeek: (Double) -> Void - @State var dragState: WaveformViewDragState = .inactive - @State private var tappedProgress: CGFloat = 0 - var timeLabelContent: String { // Display the duration if progress is 0.0 let percent = playerState.progress > 0.0 ? playerState.progress : 1.0 @@ -42,7 +40,7 @@ struct VoiceMessagePreviewComposer: View { } var showWaveformCursor: Bool { - playerState.playbackState == .playing || dragState.isDragging + playerState.playbackState == .playing || isDragging } var body: some View { @@ -64,7 +62,7 @@ struct VoiceMessagePreviewComposer: View { .gesture(SpatialTapGesture() .onEnded { tapGesture in let progress = tapGesture.location.x / geometry.size.width - dragState = .pressing(progress: progress) + onSeek(max(0, min(progress, 1.0))) }) .progressCursor(progress: playerState.progress) { WaveformCursorView(color: .compound.iconAccentTertiary) @@ -74,30 +72,16 @@ struct VoiceMessagePreviewComposer: View { .contentShape(Rectangle()) .offset(x: -25, y: 0) .gesture(DragGesture(coordinateSpace: .named("waveform")) - .onChanged { dragGesture in + .updating($isDragging) { dragGesture, isDragging, _ in + isDragging = true let progress = dragGesture.location.x / geometry.size.width - dragState = .dragging(progress: progress) - } - .onEnded { _ in - dragState = .inactive + onSeek(max(0, min(progress, 1.0))) } ) } } .coordinateSpace(name: "waveform") } - .onChange(of: dragState) { newDragState in - switch newDragState { - case .inactive: - onScrubbing(false) - case .pressing(let progress): - onScrubbing(false) - onSeek(max(0, min(progress, 1.0))) - case .dragging(let progress): - onScrubbing(true) - onSeek(max(0, min(progress, 1.0))) - } - } .padding(.vertical, 4.0) .padding(.horizontal, 6.0) .background { diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift index 42cc7a958e..9902642a82 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift @@ -22,12 +22,11 @@ struct VoiceMessageRoomPlaybackView: View { @ObservedObject var playerState: AudioPlayerState @ScaledMetric private var waveformLineWidth = 2.0 @ScaledMetric private var waveformLinePadding = 2.0 + @GestureState var isDragging = false let onPlayPause: () -> Void let onSeek: (Double) -> Void let onScrubbing: (Bool) -> Void - - @State var dragState: WaveformViewDragState = .inactive var timeLabelContent: String { // Display the duration if progress is 0.0 @@ -42,7 +41,7 @@ struct VoiceMessageRoomPlaybackView: View { } var showWaveformCursor: Bool { - playerState.playbackState == .playing || dragState.isDragging + playerState.playbackState == .playing || isDragging } var body: some View { @@ -64,7 +63,7 @@ struct VoiceMessageRoomPlaybackView: View { .gesture(SpatialTapGesture() .onEnded { tapGesture in let progress = tapGesture.location.x / geometry.size.width - dragState = .pressing(progress: progress) + onSeek(max(0, min(progress, 1.0))) }) .progressCursor(progress: playerState.progress) { WaveformCursorView(color: .compound.iconAccentTertiary) @@ -74,32 +73,21 @@ struct VoiceMessageRoomPlaybackView: View { .contentShape(Rectangle()) .offset(x: -25, y: 0) .gesture(DragGesture(coordinateSpace: .named("waveform")) - .onChanged { dragGesture in + .updating($isDragging) { dragGesture, isDragging, _ in + isDragging = true let progress = dragGesture.location.x / geometry.size.width - dragState = .dragging(progress: progress) - } - .onEnded { _ in - dragState = .inactive + onSeek(max(0, min(progress, 1.0))) } ) } } .coordinateSpace(name: "waveform") } - .onChange(of: dragState) { newDragState in - switch newDragState { - case .inactive: - onScrubbing(false) - case .pressing(let progress): - onScrubbing(false) - onSeek(max(0, min(progress, 1.0))) - case .dragging(let progress): - onScrubbing(true) - onSeek(max(0, min(progress, 1.0))) - } - } .padding(.leading, 2) .padding(.trailing, 8) + .onChange(of: isDragging) { isDragging in + onScrubbing(isDragging) + } } @ViewBuilder From 0410045d42c80d0137a5f3e2a80841f198ebd55f Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 26 Oct 2023 18:16:12 +0200 Subject: [PATCH 08/18] Refactor WaveformCursorView --- .../Progress/ProgressCursorModifier.swift | 2 +- .../VoiceMessage/WaveformCursorView.swift | 15 +++++++++--- .../View/VoiceMessagePreviewComposer.swift | 23 ++++++++----------- .../VoiceMessageRoomPlaybackView.swift | 23 ++++++++----------- 4 files changed, 33 insertions(+), 30 deletions(-) diff --git a/ElementX/Sources/Other/Progress/ProgressCursorModifier.swift b/ElementX/Sources/Other/Progress/ProgressCursorModifier.swift index 943873cf71..63a359861c 100644 --- a/ElementX/Sources/Other/Progress/ProgressCursorModifier.swift +++ b/ElementX/Sources/Other/Progress/ProgressCursorModifier.swift @@ -19,7 +19,7 @@ import SwiftUI extension View { func progressCursor(progress: CGFloat, - cursorView: @escaping () -> CursorView) -> some View { + @ViewBuilder cursorView: @escaping () -> CursorView) -> some View { modifier(ProgressCursorModifier(progress: progress, cursorView: cursorView)) } diff --git a/ElementX/Sources/Other/VoiceMessage/WaveformCursorView.swift b/ElementX/Sources/Other/VoiceMessage/WaveformCursorView.swift index 5220c1792c..c2b0e303e0 100644 --- a/ElementX/Sources/Other/VoiceMessage/WaveformCursorView.swift +++ b/ElementX/Sources/Other/VoiceMessage/WaveformCursorView.swift @@ -18,17 +18,26 @@ import Compound import SwiftUI struct WaveformCursorView: View { + @ScaledMetric private var visibleWidth = 2.0 + private let interactiveWidth: CGFloat = 50 var color: Color = .compound.iconAccentTertiary var body: some View { - RoundedRectangle(cornerRadius: 1) - .fill(color) + Rectangle() + .foregroundColor(.clear) + .frame(width: visibleWidth) + .background { + RoundedRectangle(cornerRadius: 1).fill(color) + } + .frame(width: interactiveWidth) + .contentShape(Rectangle()) + .offset(x: -interactiveWidth / 2, y: 0) } } struct WaveformCursorView_Previews: PreviewProvider, TestablePreview { static var previews: some View { WaveformCursorView(color: .compound.iconAccentTertiary) - .frame(width: 2, height: 25) + .frame(height: 25) } } diff --git a/ElementX/Sources/Screens/ComposerToolbar/View/VoiceMessagePreviewComposer.swift b/ElementX/Sources/Screens/ComposerToolbar/View/VoiceMessagePreviewComposer.swift index 1278866a34..0040d16a06 100644 --- a/ElementX/Sources/Screens/ComposerToolbar/View/VoiceMessagePreviewComposer.swift +++ b/ElementX/Sources/Screens/ComposerToolbar/View/VoiceMessagePreviewComposer.swift @@ -65,19 +65,16 @@ struct VoiceMessagePreviewComposer: View { onSeek(max(0, min(progress, 1.0))) }) .progressCursor(progress: playerState.progress) { - WaveformCursorView(color: .compound.iconAccentTertiary) - .opacity(showWaveformCursor ? 1 : 0) - .frame(width: waveformLineWidth) - .frame(width: 50) - .contentShape(Rectangle()) - .offset(x: -25, y: 0) - .gesture(DragGesture(coordinateSpace: .named("waveform")) - .updating($isDragging) { dragGesture, isDragging, _ in - isDragging = true - let progress = dragGesture.location.x / geometry.size.width - onSeek(max(0, min(progress, 1.0))) - } - ) + if showWaveformCursor { + WaveformCursorView(color: .compound.iconAccentTertiary) + .gesture(DragGesture(coordinateSpace: .named("waveform")) + .updating($isDragging) { dragGesture, isDragging, _ in + isDragging = true + let progress = dragGesture.location.x / geometry.size.width + onSeek(max(0, min(progress, 1.0))) + } + ) + } } } .coordinateSpace(name: "waveform") diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift index 9902642a82..fe0ecdb905 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift @@ -66,19 +66,16 @@ struct VoiceMessageRoomPlaybackView: View { onSeek(max(0, min(progress, 1.0))) }) .progressCursor(progress: playerState.progress) { - WaveformCursorView(color: .compound.iconAccentTertiary) - .opacity(showWaveformCursor ? 1 : 0) - .frame(width: waveformLineWidth) - .frame(width: 50) - .contentShape(Rectangle()) - .offset(x: -25, y: 0) - .gesture(DragGesture(coordinateSpace: .named("waveform")) - .updating($isDragging) { dragGesture, isDragging, _ in - isDragging = true - let progress = dragGesture.location.x / geometry.size.width - onSeek(max(0, min(progress, 1.0))) - } - ) + if showWaveformCursor { + WaveformCursorView(color: .compound.iconAccentTertiary) + .gesture(DragGesture(coordinateSpace: .named("waveform")) + .updating($isDragging) { dragGesture, isDragging, _ in + isDragging = true + let progress = dragGesture.location.x / geometry.size.width + onSeek(max(0, min(progress, 1.0))) + } + ) + } } } .coordinateSpace(name: "waveform") From f50cd214cec6aa421a8dd964f6383f17e4939f1c Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 26 Oct 2023 18:22:44 +0200 Subject: [PATCH 09/18] Cleanup --- .../xcshareddata/swiftpm/Package.resolved | 9 --------- .../Sources/Other/VoiceMessage/WaveformCursorView.swift | 2 +- .../View/VoiceMessagePreviewComposer.swift | 6 ++++-- .../VoiceMessages/VoiceMessageRoomPlaybackView.swift | 6 ++++-- ElementX/SupportingFiles/target.yml | 1 - project.yml | 5 ----- 6 files changed, 9 insertions(+), 20 deletions(-) diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 1a5bb20087..c0744d988e 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -196,15 +196,6 @@ "version" : "4.1.1" } }, - { - "identity" : "showtime", - "kind" : "remoteSourceControl", - "location" : "https://github.com/KaneCheshire/ShowTime", - "state" : { - "revision" : "f4e976931fc5f9b7a7d011f40c62a250c4ff53ab", - "version" : "2.5.3" - } - }, { "identity" : "swift-algorithms", "kind" : "remoteSourceControl", diff --git a/ElementX/Sources/Other/VoiceMessage/WaveformCursorView.swift b/ElementX/Sources/Other/VoiceMessage/WaveformCursorView.swift index c2b0e303e0..d0d0b6c7c1 100644 --- a/ElementX/Sources/Other/VoiceMessage/WaveformCursorView.swift +++ b/ElementX/Sources/Other/VoiceMessage/WaveformCursorView.swift @@ -26,7 +26,7 @@ struct WaveformCursorView: View { Rectangle() .foregroundColor(.clear) .frame(width: visibleWidth) - .background { + .overlay { RoundedRectangle(cornerRadius: 1).fill(color) } .frame(width: interactiveWidth) diff --git a/ElementX/Sources/Screens/ComposerToolbar/View/VoiceMessagePreviewComposer.swift b/ElementX/Sources/Screens/ComposerToolbar/View/VoiceMessagePreviewComposer.swift index 0040d16a06..2729ba0f68 100644 --- a/ElementX/Sources/Screens/ComposerToolbar/View/VoiceMessagePreviewComposer.swift +++ b/ElementX/Sources/Screens/ComposerToolbar/View/VoiceMessagePreviewComposer.swift @@ -67,7 +67,7 @@ struct VoiceMessagePreviewComposer: View { .progressCursor(progress: playerState.progress) { if showWaveformCursor { WaveformCursorView(color: .compound.iconAccentTertiary) - .gesture(DragGesture(coordinateSpace: .named("waveform")) + .gesture(DragGesture(coordinateSpace: .named(Self.namespaceName)) .updating($isDragging) { dragGesture, isDragging, _ in isDragging = true let progress = dragGesture.location.x / geometry.size.width @@ -77,7 +77,7 @@ struct VoiceMessagePreviewComposer: View { } } } - .coordinateSpace(name: "waveform") + .coordinateSpace(name: Self.namespaceName) } .padding(.vertical, 4.0) .padding(.horizontal, 6.0) @@ -129,6 +129,8 @@ struct VoiceMessagePreviewComposer: View { } } } + + private static let namespaceName = "voice-message-waveform" } private extension DateFormatter { diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift index fe0ecdb905..792b9a5e59 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift @@ -68,7 +68,7 @@ struct VoiceMessageRoomPlaybackView: View { .progressCursor(progress: playerState.progress) { if showWaveformCursor { WaveformCursorView(color: .compound.iconAccentTertiary) - .gesture(DragGesture(coordinateSpace: .named("waveform")) + .gesture(DragGesture(coordinateSpace: .named(Self.namespaceName)) .updating($isDragging) { dragGesture, isDragging, _ in isDragging = true let progress = dragGesture.location.x / geometry.size.width @@ -78,7 +78,7 @@ struct VoiceMessageRoomPlaybackView: View { } } } - .coordinateSpace(name: "waveform") + .coordinateSpace(name: Self.namespaceName) } .padding(.leading, 2) .padding(.trailing, 8) @@ -106,6 +106,8 @@ struct VoiceMessageRoomPlaybackView: View { waveform: playerState.waveform, progress: playerState.progress) } + + private static let namespaceName = "voice-message-waveform" } private extension DateFormatter { diff --git a/ElementX/SupportingFiles/target.yml b/ElementX/SupportingFiles/target.yml index 733a56d85d..ab43ea33e5 100644 --- a/ElementX/SupportingFiles/target.yml +++ b/ElementX/SupportingFiles/target.yml @@ -197,7 +197,6 @@ targets: - package: SwiftOGG - package: DSWaveformImage product: DSWaveformImageViews - - package: ShowTime sources: - path: ../Sources diff --git a/project.yml b/project.yml index 8aa7ad6f6c..69cd46d59f 100644 --- a/project.yml +++ b/project.yml @@ -120,8 +120,3 @@ packages: DSWaveformImage: url: https://github.com/dmrschmidt/DSWaveformImage exactVersion: 14.1.1 - ShowTime: - url: https://github.com/KaneCheshire/ShowTime - exactVersion: 2.5.3 - - From 2ca35636d99ac3b187771ccd567cbe19dcc8696f Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 26 Oct 2023 19:03:02 +0200 Subject: [PATCH 10/18] Add WaveformInteractionModifier --- ElementX.xcodeproj/project.pbxproj | 4 ++ .../VoiceMessage/WaveformCursorView.swift | 15 +---- .../WaveformInteractionModifier.swift | 62 +++++++++++++++++++ .../View/VoiceMessagePreviewComposer.swift | 28 ++------- .../VoiceMessageRoomPlaybackView.swift | 28 ++------- 5 files changed, 79 insertions(+), 58 deletions(-) create mode 100644 ElementX/Sources/Other/VoiceMessage/WaveformInteractionModifier.swift diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 29a01eda90..f5c623b9e6 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -356,6 +356,7 @@ 62910B515BCB4B455E24D7C1 /* AdvancedSettingsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D086854995173E897F993C26 /* AdvancedSettingsScreenViewModelProtocol.swift */; }; 6298AB0906DDD3525CD78C6B /* LRUCache in Frameworks */ = {isa = PBXBuildFile; productRef = 1081D3630AAD3ACEDDEC3A98 /* LRUCache */; }; 62A7FC3A0191BC7181AA432B /* AudioRecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 907FA4DE17DEA1A3738EFB83 /* AudioRecorder.swift */; }; + 63CDC201A5980F304F6D0A1C /* WaveformInteractionModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFEE91FB8ABB5F5884B6D940 /* WaveformInteractionModifier.swift */; }; 63E46D18B91D08E15FC04125 /* ExpiringTaskRunner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B25F959A434BB9923A3223F /* ExpiringTaskRunner.swift */; }; 642DF13C49ED4121C148230E /* TestablePreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1E227F34BE43B08E098796E /* TestablePreview.swift */; }; 6448F8D1D3CA4CD27BB4CADD /* RoomMemberProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F36C5D9B37E50915ECBD3EE /* RoomMemberProxy.swift */; }; @@ -1681,6 +1682,7 @@ BFC9F57320EC80C7CE34FE4A /* VoiceMessagePreviewComposer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessagePreviewComposer.swift; sourceTree = ""; }; BFDCAC6CAAD65A2C24EA9C4B /* Dictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dictionary.swift; sourceTree = ""; }; BFEA446F8618DBA79A9239CC /* MessageForwardingScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageForwardingScreen.swift; sourceTree = ""; }; + BFEE91FB8ABB5F5884B6D940 /* WaveformInteractionModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaveformInteractionModifier.swift; sourceTree = ""; }; C024C151639C4E1B91FCC68B /* ElementXAttributeScope.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementXAttributeScope.swift; sourceTree = ""; }; C070FD43DC6BF4E50217965A /* LocalizationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizationTests.swift; sourceTree = ""; }; C08E9043618AE5B0BF7B07E1 /* TemplateScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateScreenViewModelTests.swift; sourceTree = ""; }; @@ -3861,6 +3863,7 @@ AD0FF64B0E6470F66F42E182 /* EstimatedWaveformView.swift */, B8516302ACCA94A0E680AB3B /* VoiceMessageButton.swift */, FB9EABCA9348DFA27439A809 /* WaveformCursorView.swift */, + BFEE91FB8ABB5F5884B6D940 /* WaveformInteractionModifier.swift */, 94028A227645FA880B966211 /* WaveformSource.swift */, 03BA7958A4BB9C22CA8884EF /* WaveformViewDragGestureModifier.swift */, ); @@ -5898,6 +5901,7 @@ CF3827071B0BC9638BD44F5D /* WaitlistScreenViewModel.swift in Sources */, B717A820BE02C6FE2CB53F6E /* WaitlistScreenViewModelProtocol.swift in Sources */, CA12AE0DCD57D49CD96C699A /* WaveformCursorView.swift in Sources */, + 63CDC201A5980F304F6D0A1C /* WaveformInteractionModifier.swift in Sources */, B773ACD8881DB18E876D950C /* WaveformSource.swift in Sources */, 9D9EF9DD484E58A2E8877187 /* WaveformViewDragGestureModifier.swift in Sources */, D871C8CF46950F959C9A62C3 /* WelcomeScreen.swift in Sources */, diff --git a/ElementX/Sources/Other/VoiceMessage/WaveformCursorView.swift b/ElementX/Sources/Other/VoiceMessage/WaveformCursorView.swift index d0d0b6c7c1..5220c1792c 100644 --- a/ElementX/Sources/Other/VoiceMessage/WaveformCursorView.swift +++ b/ElementX/Sources/Other/VoiceMessage/WaveformCursorView.swift @@ -18,26 +18,17 @@ import Compound import SwiftUI struct WaveformCursorView: View { - @ScaledMetric private var visibleWidth = 2.0 - private let interactiveWidth: CGFloat = 50 var color: Color = .compound.iconAccentTertiary var body: some View { - Rectangle() - .foregroundColor(.clear) - .frame(width: visibleWidth) - .overlay { - RoundedRectangle(cornerRadius: 1).fill(color) - } - .frame(width: interactiveWidth) - .contentShape(Rectangle()) - .offset(x: -interactiveWidth / 2, y: 0) + RoundedRectangle(cornerRadius: 1) + .fill(color) } } struct WaveformCursorView_Previews: PreviewProvider, TestablePreview { static var previews: some View { WaveformCursorView(color: .compound.iconAccentTertiary) - .frame(height: 25) + .frame(width: 2, height: 25) } } diff --git a/ElementX/Sources/Other/VoiceMessage/WaveformInteractionModifier.swift b/ElementX/Sources/Other/VoiceMessage/WaveformInteractionModifier.swift new file mode 100644 index 0000000000..5df64d00c3 --- /dev/null +++ b/ElementX/Sources/Other/VoiceMessage/WaveformInteractionModifier.swift @@ -0,0 +1,62 @@ +// +// Copyright 2023 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SwiftUI + +extension View { + func waveformInteraction(isDragging: GestureState, progress: Double, showCursor: Bool, onSeek: @escaping (Double) -> Void) -> some View { + modifier(WaveformInteractionModifier(isDragging: isDragging, progress: progress, showCursor: showCursor, onSeek: onSeek)) + } +} + +private struct WaveformInteractionModifier: ViewModifier { + let isDragging: GestureState + let progress: Double + let showCursor: Bool + let onSeek: (Double) -> Void + + @ScaledMetric private var cursorVisibleWidth = 2.0 + private let cursorInteractiveWidth: CGFloat = 50 + + func body(content: Content) -> some View { + GeometryReader { geometry in + content + .gesture(SpatialTapGesture() + .onEnded { tapGesture in + let progress = tapGesture.location.x / geometry.size.width + onSeek(max(0, min(progress, 1.0))) + }) + .progressCursor(progress: progress) { + WaveformCursorView(color: .compound.iconAccentTertiary) + .frame(width: cursorVisibleWidth) + .opacity(showCursor ? 1 : 0) + .frame(width: cursorInteractiveWidth) + .contentShape(Rectangle()) + .gesture(DragGesture(coordinateSpace: .named(Self.namespaceName)) + .updating(isDragging) { dragGesture, isDragging, _ in + isDragging = true + let progress = dragGesture.location.x / geometry.size.width + onSeek(max(0, min(progress, 1.0))) + } + ) + .offset(x: -cursorInteractiveWidth / 2, y: 0) + } + } + .coordinateSpace(name: Self.namespaceName) + } + + private static let namespaceName = "voice-message-waveform" +} diff --git a/ElementX/Sources/Screens/ComposerToolbar/View/VoiceMessagePreviewComposer.swift b/ElementX/Sources/Screens/ComposerToolbar/View/VoiceMessagePreviewComposer.swift index 2729ba0f68..b8da88d25f 100644 --- a/ElementX/Sources/Screens/ComposerToolbar/View/VoiceMessagePreviewComposer.swift +++ b/ElementX/Sources/Screens/ComposerToolbar/View/VoiceMessagePreviewComposer.swift @@ -57,27 +57,11 @@ struct VoiceMessagePreviewComposer: View { .fixedSize(horizontal: true, vertical: true) } - GeometryReader { geometry in - waveformView - .gesture(SpatialTapGesture() - .onEnded { tapGesture in - let progress = tapGesture.location.x / geometry.size.width - onSeek(max(0, min(progress, 1.0))) - }) - .progressCursor(progress: playerState.progress) { - if showWaveformCursor { - WaveformCursorView(color: .compound.iconAccentTertiary) - .gesture(DragGesture(coordinateSpace: .named(Self.namespaceName)) - .updating($isDragging) { dragGesture, isDragging, _ in - isDragging = true - let progress = dragGesture.location.x / geometry.size.width - onSeek(max(0, min(progress, 1.0))) - } - ) - } - } - } - .coordinateSpace(name: Self.namespaceName) + waveformView + .waveformInteraction(isDragging: $isDragging, + progress: playerState.progress, + showCursor: showWaveformCursor, + onSeek: onSeek) } .padding(.vertical, 4.0) .padding(.horizontal, 6.0) @@ -129,8 +113,6 @@ struct VoiceMessagePreviewComposer: View { } } } - - private static let namespaceName = "voice-message-waveform" } private extension DateFormatter { diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift index 792b9a5e59..f389f5ac95 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift @@ -58,27 +58,11 @@ struct VoiceMessageRoomPlaybackView: View { .fixedSize(horizontal: true, vertical: true) } - GeometryReader { geometry in - waveformView - .gesture(SpatialTapGesture() - .onEnded { tapGesture in - let progress = tapGesture.location.x / geometry.size.width - onSeek(max(0, min(progress, 1.0))) - }) - .progressCursor(progress: playerState.progress) { - if showWaveformCursor { - WaveformCursorView(color: .compound.iconAccentTertiary) - .gesture(DragGesture(coordinateSpace: .named(Self.namespaceName)) - .updating($isDragging) { dragGesture, isDragging, _ in - isDragging = true - let progress = dragGesture.location.x / geometry.size.width - onSeek(max(0, min(progress, 1.0))) - } - ) - } - } - } - .coordinateSpace(name: Self.namespaceName) + waveformView + .waveformInteraction(isDragging: $isDragging, + progress: playerState.progress, + showCursor: showWaveformCursor, + onSeek: onSeek) } .padding(.leading, 2) .padding(.trailing, 8) @@ -106,8 +90,6 @@ struct VoiceMessageRoomPlaybackView: View { waveform: playerState.waveform, progress: playerState.progress) } - - private static let namespaceName = "voice-message-waveform" } private extension DateFormatter { From 862321607c3eabe7fa5b99ae8d77599a0d1ec444 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 26 Oct 2023 21:18:44 +0200 Subject: [PATCH 11/18] Add selection hapitc feedback --- .../Other/VoiceMessage/WaveformInteractionModifier.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ElementX/Sources/Other/VoiceMessage/WaveformInteractionModifier.swift b/ElementX/Sources/Other/VoiceMessage/WaveformInteractionModifier.swift index 5df64d00c3..7085729627 100644 --- a/ElementX/Sources/Other/VoiceMessage/WaveformInteractionModifier.swift +++ b/ElementX/Sources/Other/VoiceMessage/WaveformInteractionModifier.swift @@ -30,6 +30,7 @@ private struct WaveformInteractionModifier: ViewModifier { @ScaledMetric private var cursorVisibleWidth = 2.0 private let cursorInteractiveWidth: CGFloat = 50 + private let feedbackGenerator = UISelectionFeedbackGenerator() func body(content: Content) -> some View { GeometryReader { geometry in @@ -37,6 +38,7 @@ private struct WaveformInteractionModifier: ViewModifier { .gesture(SpatialTapGesture() .onEnded { tapGesture in let progress = tapGesture.location.x / geometry.size.width + feedbackGenerator.selectionChanged() onSeek(max(0, min(progress, 1.0))) }) .progressCursor(progress: progress) { @@ -49,6 +51,7 @@ private struct WaveformInteractionModifier: ViewModifier { .updating(isDragging) { dragGesture, isDragging, _ in isDragging = true let progress = dragGesture.location.x / geometry.size.width + feedbackGenerator.selectionChanged() onSeek(max(0, min(progress, 1.0))) } ) From d1b19fee93daf2198bd2c20cb9360b484b13d495 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 26 Oct 2023 21:26:29 +0200 Subject: [PATCH 12/18] Fix ComposerToolbar ZStack alignment --- .../Sources/Screens/ComposerToolbar/View/ComposerToolbar.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ElementX/Sources/Screens/ComposerToolbar/View/ComposerToolbar.swift b/ElementX/Sources/Screens/ComposerToolbar/View/ComposerToolbar.swift index 31d717eb32..ae2730014f 100644 --- a/ElementX/Sources/Screens/ComposerToolbar/View/ComposerToolbar.swift +++ b/ElementX/Sources/Screens/ComposerToolbar/View/ComposerToolbar.swift @@ -116,7 +116,7 @@ struct ComposerToolbar: View { @ViewBuilder private var mainTopBarContent: some View { - ZStack { + ZStack(alignment: .bottom) { topBarLayout { if !context.composerActionsEnabled { RoomAttachmentPicker(context: context) From 186f9f1d67ff62b3758ad5970a68a5acd92dd266 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 27 Oct 2023 08:37:10 +0200 Subject: [PATCH 13/18] Refine cursor size --- .../VoiceMessage/WaveformInteractionModifier.swift | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ElementX/Sources/Other/VoiceMessage/WaveformInteractionModifier.swift b/ElementX/Sources/Other/VoiceMessage/WaveformInteractionModifier.swift index 7085729627..4664100c47 100644 --- a/ElementX/Sources/Other/VoiceMessage/WaveformInteractionModifier.swift +++ b/ElementX/Sources/Other/VoiceMessage/WaveformInteractionModifier.swift @@ -29,7 +29,8 @@ private struct WaveformInteractionModifier: ViewModifier { let onSeek: (Double) -> Void @ScaledMetric private var cursorVisibleWidth = 2.0 - private let cursorInteractiveWidth: CGFloat = 50 + @ScaledMetric private var cursorVisibleHeight = 24.0 + private let cursorInteractiveSize: CGFloat = 50 private let feedbackGenerator = UISelectionFeedbackGenerator() func body(content: Content) -> some View { @@ -43,9 +44,10 @@ private struct WaveformInteractionModifier: ViewModifier { }) .progressCursor(progress: progress) { WaveformCursorView(color: .compound.iconAccentTertiary) - .frame(width: cursorVisibleWidth) + .frame(width: cursorVisibleWidth, height: cursorVisibleHeight) .opacity(showCursor ? 1 : 0) - .frame(width: cursorInteractiveWidth) + .frame(width: cursorInteractiveSize) + .frame(maxHeight: cursorInteractiveSize) .contentShape(Rectangle()) .gesture(DragGesture(coordinateSpace: .named(Self.namespaceName)) .updating(isDragging) { dragGesture, isDragging, _ in @@ -55,7 +57,7 @@ private struct WaveformInteractionModifier: ViewModifier { onSeek(max(0, min(progress, 1.0))) } ) - .offset(x: -cursorInteractiveWidth / 2, y: 0) + .offset(x: -cursorInteractiveSize / 2, y: 0) } } .coordinateSpace(name: Self.namespaceName) From 05efe19b57456f04e24c776dec2117afd7a882b3 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 27 Oct 2023 10:01:40 +0200 Subject: [PATCH 14/18] Remove haptic feedback --- .../Other/VoiceMessage/WaveformInteractionModifier.swift | 3 --- 1 file changed, 3 deletions(-) diff --git a/ElementX/Sources/Other/VoiceMessage/WaveformInteractionModifier.swift b/ElementX/Sources/Other/VoiceMessage/WaveformInteractionModifier.swift index 4664100c47..76062a89e7 100644 --- a/ElementX/Sources/Other/VoiceMessage/WaveformInteractionModifier.swift +++ b/ElementX/Sources/Other/VoiceMessage/WaveformInteractionModifier.swift @@ -31,7 +31,6 @@ private struct WaveformInteractionModifier: ViewModifier { @ScaledMetric private var cursorVisibleWidth = 2.0 @ScaledMetric private var cursorVisibleHeight = 24.0 private let cursorInteractiveSize: CGFloat = 50 - private let feedbackGenerator = UISelectionFeedbackGenerator() func body(content: Content) -> some View { GeometryReader { geometry in @@ -39,7 +38,6 @@ private struct WaveformInteractionModifier: ViewModifier { .gesture(SpatialTapGesture() .onEnded { tapGesture in let progress = tapGesture.location.x / geometry.size.width - feedbackGenerator.selectionChanged() onSeek(max(0, min(progress, 1.0))) }) .progressCursor(progress: progress) { @@ -53,7 +51,6 @@ private struct WaveformInteractionModifier: ViewModifier { .updating(isDragging) { dragGesture, isDragging, _ in isDragging = true let progress = dragGesture.location.x / geometry.size.width - feedbackGenerator.selectionChanged() onSeek(max(0, min(progress, 1.0))) } ) From 73585c5d259d03d6c1ff9cd1609ff190fbd73b3f Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 27 Oct 2023 10:07:00 +0200 Subject: [PATCH 15/18] Fix preview test --- .../PreviewTests/test_composerToolbar.Voice-Message.png | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UnitTests/__Snapshots__/PreviewTests/test_composerToolbar.Voice-Message.png b/UnitTests/__Snapshots__/PreviewTests/test_composerToolbar.Voice-Message.png index bcb569293e..793475ae67 100644 --- a/UnitTests/__Snapshots__/PreviewTests/test_composerToolbar.Voice-Message.png +++ b/UnitTests/__Snapshots__/PreviewTests/test_composerToolbar.Voice-Message.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:09e3015e6bc13ff47decd46943fdee10c9c4b79d1361143f6d35e48c0822e5f8 -size 93714 +oid sha256:6387ef1c9d6ba711f20718a3bf9c3f32d5af688dbdb078481301d4a5ccc88a69 +size 93719 From a596e2263f6223f8bef6a15ed130e2e424f6d5ac Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 27 Oct 2023 10:37:30 +0200 Subject: [PATCH 16/18] Delete longPressDisabledItemID --- ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift | 3 +-- .../RoomScreen/View/Style/LongPressWithFeedback.swift | 7 ++----- .../View/Style/TimelineItemBubbledStylerView.swift | 2 +- .../View/Style/TimelineItemPlainStylerView.swift | 2 +- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift index 2c846d1938..da95cdd9c5 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift @@ -104,8 +104,7 @@ struct RoomScreenViewState: BindableState { var isEncryptedOneToOneRoom = false var timelineViewState = TimelineViewState() // check the doc before changing this var swiftUITimelineEnabled = false - - var longPressDisabledItemID: TimelineItemIdentifier? + var ownUserID: String var showCallButton = false diff --git a/ElementX/Sources/Screens/RoomScreen/View/Style/LongPressWithFeedback.swift b/ElementX/Sources/Screens/RoomScreen/View/Style/LongPressWithFeedback.swift index 8a676a1ec5..680b6ea512 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/Style/LongPressWithFeedback.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/Style/LongPressWithFeedback.swift @@ -18,7 +18,6 @@ import SwiftUI struct LongPressWithFeedback: ViewModifier { let action: () -> Void - let disabled: () -> Bool @State private var triggerTask: Task? @State private var isLongPressing = false @@ -50,8 +49,6 @@ struct LongPressWithFeedback: ViewModifier { if Task.isCancelled { return } - guard !disabled() else { return } - action() feedbackGenerator.impactOccurred() } @@ -60,8 +57,8 @@ struct LongPressWithFeedback: ViewModifier { } extension View { - func longPressWithFeedback(disabled: @escaping @autoclosure () -> Bool = false, action: @escaping () -> Void) -> some View { - modifier(LongPressWithFeedback(action: action, disabled: disabled)) + func longPressWithFeedback(action: @escaping () -> Void) -> some View { + modifier(LongPressWithFeedback(action: action)) } } diff --git a/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineItemBubbledStylerView.swift b/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineItemBubbledStylerView.swift index feebf5f3ef..27d924d357 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineItemBubbledStylerView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineItemBubbledStylerView.swift @@ -128,7 +128,7 @@ struct TimelineItemBubbledStylerView: View { } // We need a tap gesture before this long one so that it doesn't // steal away the gestures from the scroll view - .longPressWithFeedback(disabled: context.viewState.longPressDisabledItemID == timelineItem.id) { + .longPressWithFeedback { context.send(viewAction: .timelineItemMenu(itemID: timelineItem.id)) } .swipeRightAction { diff --git a/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineItemPlainStylerView.swift b/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineItemPlainStylerView.swift index 0cf444f6f8..4b662db583 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineItemPlainStylerView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineItemPlainStylerView.swift @@ -73,7 +73,7 @@ struct TimelineItemPlainStylerView: View { } // We need a tap gesture before this long one so that it doesn't // steal away the gestures from the scroll view - .longPressWithFeedback(disabled: context.viewState.longPressDisabledItemID == timelineItem.id) { + .longPressWithFeedback { context.send(viewAction: .timelineItemMenu(itemID: timelineItem.id)) } .swipeRightAction { From 7b9dd0ed834054f181d90c28d4aed967cf0ba046 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 27 Oct 2023 10:58:37 +0200 Subject: [PATCH 17/18] Remove progress animation --- .../Sources/Other/VoiceMessage/WaveformInteractionModifier.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/ElementX/Sources/Other/VoiceMessage/WaveformInteractionModifier.swift b/ElementX/Sources/Other/VoiceMessage/WaveformInteractionModifier.swift index 76062a89e7..d6149735b5 100644 --- a/ElementX/Sources/Other/VoiceMessage/WaveformInteractionModifier.swift +++ b/ElementX/Sources/Other/VoiceMessage/WaveformInteractionModifier.swift @@ -58,6 +58,7 @@ private struct WaveformInteractionModifier: ViewModifier { } } .coordinateSpace(name: Self.namespaceName) + .animation(nil, value: progress) } private static let namespaceName = "voice-message-waveform" From 7f3ffed404a84a67b20ffdcf92aa62a2d0489a5c Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 27 Oct 2023 11:26:49 +0200 Subject: [PATCH 18/18] Project file --- ElementX.xcodeproj/project.pbxproj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index f5c623b9e6..16ecb30173 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -588,7 +588,6 @@ 9D2E03DB175A6AB14589076D /* AnalyticsEvents in Frameworks */ = {isa = PBXBuildFile; productRef = 2A3F7BCCB18C15B30CCA39A9 /* AnalyticsEvents */; }; 9D79B94493FB32249F7E472F /* PlaceholderAvatarImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C705E605EF57C19DBE86FFA1 /* PlaceholderAvatarImage.swift */; }; 9D9690D2FD4CD26FF670620F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C75EF87651B00A176AB08E97 /* AppDelegate.swift */; }; - 9D9EF9DD484E58A2E8877187 /* WaveformViewDragGestureModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BA7958A4BB9C22CA8884EF /* WaveformViewDragGestureModifier.swift */; }; 9DC5FB22B8F86C3B51E907C1 /* HomeScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D6E4C37E9F0E53D3DF951AC /* HomeScreenUITests.swift */; }; 9DD5AA10E85137140FEA86A3 /* MediaProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F17EFA1D3D09FC2F9C5E1CB2 /* MediaProvider.swift */; }; 9DD84E014ADFB2DD813022D5 /* RoomDetailsEditScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E5B2CBEF8F96424F095508 /* RoomDetailsEditScreenViewModelTests.swift */; }; @@ -1034,7 +1033,6 @@ 035177BCD8E8308B098AC3C2 /* WindowManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowManager.swift; sourceTree = ""; }; 0376C429FAB1687C3D905F3E /* MockCoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockCoder.swift; sourceTree = ""; }; 0392E3FDE372C9B56FEEED8B /* test_voice_message.m4a */ = {isa = PBXFileReference; path = test_voice_message.m4a; sourceTree = ""; }; - 03BA7958A4BB9C22CA8884EF /* WaveformViewDragGestureModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaveformViewDragGestureModifier.swift; sourceTree = ""; }; 03DD998E523D4EC93C7ED703 /* RoomNotificationSettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsScreenViewModelProtocol.swift; sourceTree = ""; }; 03FABD73FD8086EFAB699F42 /* MediaUploadPreviewScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadPreviewScreenViewModelTests.swift; sourceTree = ""; }; 045253F9967A535EE5B16691 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = ""; }; @@ -3865,7 +3863,6 @@ FB9EABCA9348DFA27439A809 /* WaveformCursorView.swift */, BFEE91FB8ABB5F5884B6D940 /* WaveformInteractionModifier.swift */, 94028A227645FA880B966211 /* WaveformSource.swift */, - 03BA7958A4BB9C22CA8884EF /* WaveformViewDragGestureModifier.swift */, ); path = VoiceMessage; sourceTree = ""; @@ -5903,7 +5900,6 @@ CA12AE0DCD57D49CD96C699A /* WaveformCursorView.swift in Sources */, 63CDC201A5980F304F6D0A1C /* WaveformInteractionModifier.swift in Sources */, B773ACD8881DB18E876D950C /* WaveformSource.swift in Sources */, - 9D9EF9DD484E58A2E8877187 /* WaveformViewDragGestureModifier.swift in Sources */, D871C8CF46950F959C9A62C3 /* WelcomeScreen.swift in Sources */, 383055C6ABE5BE058CEE1DDB /* WelcomeScreenScreenCoordinator.swift in Sources */, BD2BF1EC73FFB0C01552ECDA /* WelcomeScreenScreenModels.swift in Sources */,