mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-05 20:09:04 +00:00
Fix various flakey tests.
This commit is contained in:
@@ -31,42 +31,7 @@ struct SwipeRightAction<Label: View>: ViewModifier {
|
|||||||
content
|
content
|
||||||
.offset(x: xOffset, y: 0.0)
|
.offset(x: xOffset, y: 0.0)
|
||||||
.animation(.interactiveSpring().speed(0.5), value: xOffset)
|
.animation(.interactiveSpring().speed(0.5), value: xOffset)
|
||||||
.gesture(DragGesture()
|
.simultaneousGesture(gesture)
|
||||||
.updating($dragGestureActive) { _, state, _ in
|
|
||||||
// Available actions should be computed on the fly so we use a gesture state change
|
|
||||||
// to ask whether the move should be started or not.
|
|
||||||
state = true
|
|
||||||
}
|
|
||||||
.onChanged { value in
|
|
||||||
guard canStartAction else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// We want to add a spring like behavior to the drag in which the view
|
|
||||||
// moves slower the more it's dragged. We use a circular easing function
|
|
||||||
// to generate those values up to the `swipeThreshold`
|
|
||||||
// The final translation will be between 0 and `swipeThreshold` with the action being enabled from
|
|
||||||
// `actionThreshold` onwards
|
|
||||||
let screenWidthNormalisedTranslation = max(0.0, min(value.translation.width, swipeThreshold)) / swipeThreshold
|
|
||||||
let easedTranslation = circularEaseOut(screenWidthNormalisedTranslation)
|
|
||||||
xOffset = easedTranslation * xOffsetThreshold
|
|
||||||
|
|
||||||
if xOffset > actionThreshold {
|
|
||||||
if !hasReachedActionThreshold {
|
|
||||||
feedbackGenerator.impactOccurred()
|
|
||||||
hasReachedActionThreshold = true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
hasReachedActionThreshold = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onEnded { _ in
|
|
||||||
if xOffset > actionThreshold {
|
|
||||||
action()
|
|
||||||
}
|
|
||||||
|
|
||||||
xOffset = 0.0
|
|
||||||
})
|
|
||||||
.onChange(of: dragGestureActive) { value in
|
.onChange(of: dragGestureActive) { value in
|
||||||
if value == true {
|
if value == true {
|
||||||
if shouldStartAction() {
|
if shouldStartAction() {
|
||||||
@@ -86,6 +51,55 @@ struct SwipeRightAction<Label: View>: ViewModifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var gesture: some Gesture {
|
||||||
|
DragGesture()
|
||||||
|
.updating($dragGestureActive) { _, state, _ in
|
||||||
|
// Available actions should be computed on the fly so we use a gesture state change
|
||||||
|
// to ask whether the move should be started or not.
|
||||||
|
state = true
|
||||||
|
}
|
||||||
|
.onChanged { value in
|
||||||
|
guard canStartAction, value.translation.width > value.translation.height else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Due to https://forums.developer.apple.com/forums/thread/760035 we had to make
|
||||||
|
// the drag a simultaneous gesture otherwise it was impossible to scroll the timeline.
|
||||||
|
// Therefore we need to prevent the animation to run if the user is to scrolling vertically.
|
||||||
|
// It would be nice if we could somehow abort the gesture in this case.
|
||||||
|
let width: CGFloat = if value.translation.width > abs(value.translation.height) {
|
||||||
|
value.translation.width
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
// We want to add a spring like behaviour to the drag in which the view
|
||||||
|
// moves slower the more it's dragged. We use a circular easing function
|
||||||
|
// to generate those values up to the `swipeThreshold`
|
||||||
|
// The final translation will be between 0 and `swipeThreshold` with the action being enabled from
|
||||||
|
// `actionThreshold` onwards
|
||||||
|
let screenWidthNormalisedTranslation = max(0.0, min(width, swipeThreshold)) / swipeThreshold
|
||||||
|
let easedTranslation = circularEaseOut(screenWidthNormalisedTranslation)
|
||||||
|
xOffset = easedTranslation * xOffsetThreshold
|
||||||
|
|
||||||
|
if xOffset > actionThreshold {
|
||||||
|
if !hasReachedActionThreshold {
|
||||||
|
feedbackGenerator.impactOccurred()
|
||||||
|
hasReachedActionThreshold = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hasReachedActionThreshold = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onEnded { _ in
|
||||||
|
if xOffset > actionThreshold {
|
||||||
|
action()
|
||||||
|
}
|
||||||
|
|
||||||
|
xOffset = 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Used to compute the horizontal translation amount.
|
/// Used to compute the horizontal translation amount.
|
||||||
/// The more it's dragged the less it moves on a circular ease out curve
|
/// The more it's dragged the less it moves on a circular ease out curve
|
||||||
private func circularEaseOut(_ value: Double) -> Double {
|
private func circularEaseOut(_ value: Double) -> Double {
|
||||||
|
|||||||
@@ -379,6 +379,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
await self?.updatePinnedEventIDs()
|
await self?.updatePinnedEventIDs()
|
||||||
|
await self?.updatePermissions()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:c3f1793388cf6ef7f41c06ee75bacf8666691bc58dbfc3fa0cfaf5ef6d605923
|
oid sha256:9f2b1bb7cd121631f9064509e6d7be090abd62c5a4afb09d083ab0b33d67a355
|
||||||
size 334552
|
size 329861
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:2f19d1195da5c2fffa2ed4da45983e2d4734078ffc42b1078adb4fdbfa2be1c9
|
oid sha256:0805c9072d8de90ff269e80be6c245d1bff4a0f5dc143d19bfaf59a05b170148
|
||||||
size 319244
|
size 317722
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:9cf2b33540efccf9c5ad3a5d183bda468c4e79f760ecd3e99d4d7fb43eb49c2b
|
oid sha256:f5a0569910e56f38be84e6d14bc721d5ab09ac90229a46eccbadcf54b2b88eec
|
||||||
size 331768
|
size 326819
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:02ddb5960d8da733b7a732da13e92d59794df1828208f9d118694425b821cae8
|
oid sha256:ecca3b6f3357d2e5868224b6005079c33543564d08f1b6c32c5b4cde291183eb
|
||||||
size 346141
|
size 341217
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:091843bb18a614544534f0eabd2970a5cd6f775e75591428bd3fed1ac9c74328
|
oid sha256:5eb3e3ca8c28ba9f1e4fcd5b7dcf30d162a98ec3cf8539bb8e60ac8802928bf7
|
||||||
size 374446
|
size 369833
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:36b7bbbb3452a4f20b5790b84cb83adecbfcd02f8e9e325db06a22a7c6c74115
|
oid sha256:1fd4349c47465504cae19187ac770f5f72a9ebd139d6635e88e4f780c3766a88
|
||||||
size 185184
|
size 180406
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:1343835253f92915a5b2dad424653ac4d5f2452cb54c3d62725c24e97b08ca33
|
oid sha256:b7816944b84404a010641d131a766b06a8bddab609204ebb58b9b8f13e94562e
|
||||||
size 221532
|
size 216901
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:5b55a58018122218861fa16e640108743b174596da8a30912079dc2ed1f5961b
|
oid sha256:3a083dfcee55c1be624d228589a041712b8d8f5669bd167fefb92aa8e203a6a4
|
||||||
size 344558
|
size 342283
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:81ecddd245e889c442f43fd429c86a72ec0a125b650f030e2548200e4e692529
|
oid sha256:9d78cbc723080c15e1ae3e5ce518517ac329d11a77e959179c84d275411ed976
|
||||||
size 216998
|
size 212177
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:4dc2896ad8f7837fb8e21078379d1d5534e23b87ec8ac8673e7c649ae92fab11
|
oid sha256:21a4406c80a619c4a7f97b39200d565f8cb9d84f47ce4bb8bb847fa3de187c09
|
||||||
size 291283
|
size 286492
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:711fb928fd51daf4148823c7d8ea7622169d7dfc71ef275a6f62683923aff7e5
|
oid sha256:b8d92e74b63fd664d19b8757336b32fe6b01fe7301a30ea591eee0e98e8a99e7
|
||||||
size 233789
|
size 228856
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:7d04f489f65c0269b1020b311808833f47a9600a727fef9aa8f0eb63881fe537
|
oid sha256:4fce73fbf4e5c70b18fe8cd4c4d0ffbb2507c8da30a34d8eaa5d16513343e6e0
|
||||||
size 287557
|
size 282805
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:d9ade4638d6436a04240b6695665eff1e302783bd1090715664e52040ace6d24
|
oid sha256:672046d4289a23075b82789d5fca2b6b4f1cab3b75147cd2a5eec5d5dd79c398
|
||||||
size 298036
|
size 299984
|
||||||
|
|||||||
@@ -380,10 +380,13 @@ class TimelineViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
func testPinnedEvents() async throws {
|
func testPinnedEvents() async throws {
|
||||||
ServiceLocator.shared.settings.pinningEnabled = true
|
ServiceLocator.shared.settings.pinningEnabled = true
|
||||||
|
|
||||||
|
// Note: We need to start the test with a non-default value so we know the view model has finished the Task.
|
||||||
let roomProxyMock = JoinedRoomProxyMock(.init(name: "",
|
let roomProxyMock = JoinedRoomProxyMock(.init(name: "",
|
||||||
pinnedEventIDs: .init(["test1"])))
|
pinnedEventIDs: .init(["test1"])))
|
||||||
let actionsSubject = PassthroughSubject<JoinedRoomProxyAction, Never>()
|
let actionsSubject = PassthroughSubject<JoinedRoomProxyAction, Never>()
|
||||||
roomProxyMock.underlyingActionsPublisher = actionsSubject.eraseToAnyPublisher()
|
roomProxyMock.underlyingActionsPublisher = actionsSubject.eraseToAnyPublisher()
|
||||||
|
|
||||||
let viewModel = TimelineViewModel(roomProxy: roomProxyMock,
|
let viewModel = TimelineViewModel(roomProxy: roomProxyMock,
|
||||||
timelineController: MockRoomTimelineController(),
|
timelineController: MockRoomTimelineController(),
|
||||||
mediaProvider: MockMediaProvider(),
|
mediaProvider: MockMediaProvider(),
|
||||||
@@ -409,9 +412,12 @@ class TimelineViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
func testCanUserPinEvents() async throws {
|
func testCanUserPinEvents() async throws {
|
||||||
ServiceLocator.shared.settings.pinningEnabled = true
|
ServiceLocator.shared.settings.pinningEnabled = true
|
||||||
let roomProxyMock = JoinedRoomProxyMock(.init(name: "", canUserPin: false))
|
|
||||||
|
// Note: We need to start the test with the non-default value so we know the view model has finished the Task.
|
||||||
|
let roomProxyMock = JoinedRoomProxyMock(.init(name: "", canUserPin: true))
|
||||||
let actionsSubject = PassthroughSubject<JoinedRoomProxyAction, Never>()
|
let actionsSubject = PassthroughSubject<JoinedRoomProxyAction, Never>()
|
||||||
roomProxyMock.underlyingActionsPublisher = actionsSubject.eraseToAnyPublisher()
|
roomProxyMock.underlyingActionsPublisher = actionsSubject.eraseToAnyPublisher()
|
||||||
|
|
||||||
let viewModel = TimelineViewModel(roomProxy: roomProxyMock,
|
let viewModel = TimelineViewModel(roomProxy: roomProxyMock,
|
||||||
timelineController: MockRoomTimelineController(),
|
timelineController: MockRoomTimelineController(),
|
||||||
mediaProvider: MockMediaProvider(),
|
mediaProvider: MockMediaProvider(),
|
||||||
@@ -423,13 +429,13 @@ class TimelineViewModelTests: XCTestCase {
|
|||||||
analyticsService: ServiceLocator.shared.analytics)
|
analyticsService: ServiceLocator.shared.analytics)
|
||||||
|
|
||||||
var deferred = deferFulfillment(viewModel.context.$viewState) { value in
|
var deferred = deferFulfillment(viewModel.context.$viewState) { value in
|
||||||
!value.canCurrentUserPin
|
value.canCurrentUserPin
|
||||||
}
|
}
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
|
|
||||||
roomProxyMock.canUserPinOrUnpinUserIDReturnValue = .success(true)
|
roomProxyMock.canUserPinOrUnpinUserIDReturnValue = .success(false)
|
||||||
deferred = deferFulfillment(viewModel.context.$viewState) { value in
|
deferred = deferFulfillment(viewModel.context.$viewState) { value in
|
||||||
value.canCurrentUserPin
|
!value.canCurrentUserPin
|
||||||
}
|
}
|
||||||
actionsSubject.send(.roomInfoUpdate)
|
actionsSubject.send(.roomInfoUpdate)
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
|
|||||||
Reference in New Issue
Block a user