Fix sensitive chat bubble on iOS 18

This commit fixes an issue with the chat bubble view where it would
unexpectedly trigger the reaction emoji keyboard when scrolling or
swiping, which became specially sensitive on iOS 18.

The fix consists of 2 parts:
- Changing the long press gesture logic to better adhere to Apple's API specs
- Modify the SwipeActions library to allow the gesture priority to be
  configurable, and demote the swiping gesture to have normal priority
  (it was found that having a high-priority drag gesture prevents
  long-presses from being activated)

Closes: https://github.com/damus-io/damus/issues/2577
Changelog-Fixed: Fix sensitive long-press gesture on event chat bubble in iOS 18
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit is contained in:
Daniel D’Aquino
2024-10-14 17:24:04 -07:00
parent 42f5af0ffd
commit a6449020b6
3 changed files with 15 additions and 26 deletions

View File

@@ -5564,10 +5564,10 @@
};
D78DB8572C1CE9CA00F0AB12 /* XCRemoteSwiftPackageReference "SwipeActions" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/aheze/SwipeActions";
repositoryURL = "https://github.com/damus-io/SwipeActions.git";
requirement = {
kind = exactVersion;
version = 1.1.0;
kind = revision;
revision = 33d99756c3112e1a07c1732e3cddc5ad5bd0c5f4;
};
};
D7A343EC2AD0D77C00CED48B /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */ = {

View File

@@ -1,5 +1,5 @@
{
"originHash" : "babaf4d5748afecf49bbb702530d8e9576460692f478b0a50ee43195dd4440e2",
"originHash" : "1b14e62192b3fa4b04a57cb4601d175b325dc16cb5f22c4c8eb975a675328637",
"pins" : [
{
"identity" : "emojikit",
@@ -92,10 +92,9 @@
{
"identity" : "swipeactions",
"kind" : "remoteSourceControl",
"location" : "https://github.com/aheze/SwipeActions",
"location" : "https://github.com/damus-io/SwipeActions.git",
"state" : {
"revision" : "41e6f6dce02d8cfa164f8c5461a41340850ca3ab",
"version" : "1.1.0"
"revision" : "33d99756c3112e1a07c1732e3cddc5ad5bd0c5f4"
}
}
],

View File

@@ -27,8 +27,6 @@ struct ChatEventView: View {
// MARK: long-press reaction control objects
/// Whether the user is actively pressing the view
@State var is_pressing = false
/// The dispatched work item scheduled by a timer to bounce the event bubble and show the emoji selector
@State var long_press_bounce_work_item: DispatchWorkItem?
@State var popover_state: PopoverState = .closed {
didSet {
let generator = UIImpactFeedbackGenerator(style: popover_state.some_sheet_open() ? .heavy : .light)
@@ -39,6 +37,7 @@ struct ChatEventView: View {
@State private var isOnTopHalfOfScreen: Bool = false
@ObservedObject var bar: ActionBarModel
@Environment(\.swipeViewGroupSelection) var swipeViewGroupSelection
enum PopoverState: String {
case closed
@@ -206,28 +205,18 @@ struct ChatEventView: View {
.scaleEffect(self.popover_state.some_sheet_open() ? 1.08 : is_pressing ? 1.02 : 1)
.shadow(color: (is_pressing || self.popover_state.some_sheet_open()) ? .black.opacity(0.1) : .black.opacity(0.3), radius: (is_pressing || self.popover_state.some_sheet_open()) ? 8 : 0, y: (is_pressing || self.popover_state.some_sheet_open()) ? 15 : 0)
.onLongPressGesture(minimumDuration: 0.5, maximumDistance: 10, perform: {
long_press_bounce_work_item?.cancel()
withAnimation(.bouncy(duration: 0.2, extraBounce: 0.35)) {
let should_show_zap_sheet = !damus_state.settings.nozaps && damus_state.settings.onlyzaps_mode
popover_state = should_show_zap_sheet ? .open_zap_sheet : .open_emoji_selector
}
}, onPressingChanged: { is_pressing in
withAnimation(is_pressing ? .easeIn(duration: 0.5) : .easeOut(duration: 0.1)) {
self.is_pressing = is_pressing
if popover_state != .closed {
return
}
if self.is_pressing {
let item = DispatchWorkItem {
// Ensure the action is performed only if the condition is still valid
if self.is_pressing {
withAnimation(.bouncy(duration: 0.2, extraBounce: 0.35)) {
let should_show_zap_sheet = !damus_state.settings.nozaps && damus_state.settings.onlyzaps_mode
popover_state = should_show_zap_sheet ? .open_zap_sheet : .open_emoji_selector
}
}
}
long_press_bounce_work_item = item
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: item)
}
}
})
.onChange(of: swipeViewGroupSelection.wrappedValue) { newValue in
self.is_pressing = false
}
.background(
GeometryReader { geometry in
EmptyView()
@@ -310,6 +299,7 @@ struct ChatEventView: View {
.swipeSpacing(-20)
.swipeActionsStyle(.mask)
.swipeMinimumDistance(20)
.swipeDragGesturePriority(.normal)
}
}