Skip to content

Commit

Permalink
Add WaveformInteractionModifier
Browse files Browse the repository at this point in the history
  • Loading branch information
alfogrillo committed Oct 26, 2023
1 parent 492f99f commit c393984
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 58 deletions.
4 changes: 4 additions & 0 deletions ElementX.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
Expand Down Expand Up @@ -1679,6 +1680,7 @@
BFC9F57320EC80C7CE34FE4A /* VoiceMessagePreviewComposer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessagePreviewComposer.swift; sourceTree = "<group>"; };
BFDCAC6CAAD65A2C24EA9C4B /* Dictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dictionary.swift; sourceTree = "<group>"; };
BFEA446F8618DBA79A9239CC /* MessageForwardingScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageForwardingScreen.swift; sourceTree = "<group>"; };
BFEE91FB8ABB5F5884B6D940 /* WaveformInteractionModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaveformInteractionModifier.swift; sourceTree = "<group>"; };
C024C151639C4E1B91FCC68B /* ElementXAttributeScope.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementXAttributeScope.swift; sourceTree = "<group>"; };
C070FD43DC6BF4E50217965A /* LocalizationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizationTests.swift; sourceTree = "<group>"; };
C08E9043618AE5B0BF7B07E1 /* TemplateScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateScreenViewModelTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3859,6 +3861,7 @@
AD0FF64B0E6470F66F42E182 /* EstimatedWaveformView.swift */,
B8516302ACCA94A0E680AB3B /* VoiceMessageButton.swift */,
FB9EABCA9348DFA27439A809 /* WaveformCursorView.swift */,
BFEE91FB8ABB5F5884B6D940 /* WaveformInteractionModifier.swift */,
94028A227645FA880B966211 /* WaveformSource.swift */,
);
path = VoiceMessage;
Expand Down Expand Up @@ -5895,6 +5898,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 */,
D871C8CF46950F959C9A62C3 /* WelcomeScreen.swift in Sources */,
383055C6ABE5BE058CEE1DDB /* WelcomeScreenScreenCoordinator.swift in Sources */,
Expand Down
15 changes: 3 additions & 12 deletions ElementX/Sources/Other/VoiceMessage/WaveformCursorView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Original file line number Diff line number Diff line change
@@ -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<Bool>, 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<Bool>
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"
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -129,8 +113,6 @@ struct VoiceMessagePreviewComposer: View {
}
}
}

private static let namespaceName = "voice-message-waveform"
}

private extension DateFormatter {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -106,8 +90,6 @@ struct VoiceMessageRoomPlaybackView: View {
waveform: playerState.waveform,
progress: playerState.progress)
}

private static let namespaceName = "voice-message-waveform"
}

private extension DateFormatter {
Expand Down

0 comments on commit c393984

Please sign in to comment.