Add support for OnlyZaps mode on the new chat thread

With this commit, long-presses on chat bubbles will now reveal a zap
sheet if they are on OnlyZaps mode and have zaps unlocked.

Users without OnlyZaps or with Zaps blocked will continue to see the
emoji reaction sheet

Closes: https://github.com/damus-io/damus/issues/2327
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit is contained in:
Daniel D’Aquino
2024-07-13 00:31:44 +00:00
committed by William Casarin
parent 605d88add1
commit 35df9f7ab7
2 changed files with 112 additions and 11 deletions

View File

@@ -31,7 +31,7 @@ struct ChatEventView: View {
@State var long_press_bounce_work_item: DispatchWorkItem?
@State var popover_state: PopoverState = .closed {
didSet {
let generator = UIImpactFeedbackGenerator(style: popover_state == .open_emoji_selector ? .heavy : .light)
let generator = UIImpactFeedbackGenerator(style: popover_state.some_sheet_open() ? .heavy : .light)
generator.impactOccurred()
}
}
@@ -43,6 +43,11 @@ struct ChatEventView: View {
enum PopoverState: String {
case closed
case open_emoji_selector
case open_zap_sheet
func some_sheet_open() -> Bool {
return self == .open_zap_sheet || self == .open_emoji_selector
}
}
var just_started: Bool {
@@ -90,9 +95,22 @@ struct ChatEventView: View {
var by_other_user: Bool {
return event.pubkey != damus_state.pubkey
}
var is_ours: Bool { return !by_other_user }
// MARK: Zapping properties
var lnurl: String? {
damus_state.profiles.lookup_with_timestamp(event.pubkey)?.map({ pr in
pr?.lnurl
}).value
}
var zap_target: ZapTarget {
ZapTarget.note(id: event.id, author: event.pubkey)
}
// MARK: Views
var event_bubble: some View {
ChatBubble(
direction: is_ours ? .right : .left,
@@ -170,6 +188,14 @@ struct ChatEventView: View {
EmojiPickerView(selectedEmoji: $selected_emoji, emojiProvider: damus_state.emoji_provider)
}.presentationDetents([.medium, .large])
}
.sheet(isPresented: Binding(get: { popover_state == .open_zap_sheet }, set: { new_state in
withAnimation(new_state == true ? .easeIn(duration: 0.5) : .easeOut(duration: 0.1)) {
popover_state = new_state == true ? .open_zap_sheet : .closed
}
})) {
ZapSheetViewIfPossible(damus_state: damus_state, target: zap_target, lnurl: lnurl)
.presentationDetents([.medium, .large])
}
.onChange(of: selected_emoji) { newSelectedEmoji in
if let newSelectedEmoji {
send_like(emoji: newSelectedEmoji.value)
@@ -177,8 +203,8 @@ struct ChatEventView: View {
}
}
}
.scaleEffect(self.popover_state == .open_emoji_selector ? 1.08 : is_pressing ? 1.02 : 1)
.shadow(color: (is_pressing || self.popover_state == .open_emoji_selector) ? .black.opacity(0.1) : .black.opacity(0.3), radius: (is_pressing || self.popover_state == .open_emoji_selector) ? 8 : 0, y: (is_pressing || self.popover_state == .open_emoji_selector) ? 15 : 0)
.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()
}, onPressingChanged: { is_pressing in
@@ -192,7 +218,8 @@ struct ChatEventView: View {
// Ensure the action is performed only if the condition is still valid
if self.is_pressing {
withAnimation(.bouncy(duration: 0.2, extraBounce: 0.35)) {
popover_state = .open_emoji_selector
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
}
}
}

View File

@@ -295,6 +295,64 @@ struct CustomizeZapView: View {
}
}
struct ZapSheetViewIfPossible: View {
let damus_state: DamusState
let target: ZapTarget
let lnurl: String?
var zap_sheet: ZapSheet? {
guard let lnurl else { return nil }
return ZapSheet(target: target, lnurl: lnurl)
}
@Environment(\.dismiss) var dismiss
@Environment(\.colorScheme) var colorScheme
var body: some View {
if let zap_sheet {
CustomizeZapView(state: damus_state, target: zap_sheet.target, lnurl: zap_sheet.lnurl)
}
else {
zap_sheet_not_possible
}
}
var zap_sheet_not_possible: some View {
VStack(alignment: .center, spacing: 20) {
Image(systemName: "bolt.trianglebadge.exclamationmark.fill")
.resizable()
.scaledToFit()
.frame(width: 70)
Text("User not zappable", comment: "Headline indicating a user cannot be zapped")
.font(.headline)
Text("This user cannot be zapped because they have not configured zaps on their account yet. Time to orange-pill?", comment: "Comment explaining why a user cannot be zapped.")
.multilineTextAlignment(.center)
.opacity(0.6)
self.dm_button
}
.padding()
}
var dm_button: some View {
let dm_model = damus_state.dms.lookup_or_create(target.pubkey)
return VStack(alignment: .center, spacing: 10) {
Button(
action: {
damus_state.nav.push(route: Route.DMChat(dms: dm_model))
dismiss()
},
label: {
Image("messages")
.profile_button_style(scheme: colorScheme)
}
)
.buttonStyle(NeutralButtonShape.circle.style)
Text("Orange-pill", comment: "Button label that allows the user to start a direct message conversation with the user shown on-screen, to orange-pill them (i.e. help them to setup zaps)")
.foregroundStyle(.secondary)
.font(.caption)
}
}
}
extension View {
func hideKeyboard() {
let resign = #selector(UIResponder.resignFirstResponder)
@@ -302,9 +360,25 @@ extension View {
}
}
struct CustomizeZapView_Previews: PreviewProvider {
static var previews: some View {
CustomizeZapView(state: test_damus_state, target: ZapTarget.note(id: test_note.id, author: test_note.pubkey), lnurl: "")
.frame(width: 400, height: 600)
}
fileprivate func test_zap_sheet() -> ZapSheet {
let zap_target = ZapTarget.note(id: test_note.id, author: test_note.pubkey)
let lnurl = ""
return ZapSheet(target: zap_target, lnurl: lnurl)
}
#Preview {
CustomizeZapView(state: test_damus_state, target: test_zap_sheet().target, lnurl: test_zap_sheet().lnurl)
.frame(width: 400, height: 600)
}
#Preview {
ZapSheetViewIfPossible(damus_state: test_damus_state, target: test_zap_sheet().target, lnurl: test_zap_sheet().lnurl)
.frame(width: 400, height: 600)
}
#Preview {
ZapSheetViewIfPossible(damus_state: test_damus_state, target: test_zap_sheet().target, lnurl: nil)
.frame(width: 400, height: 600)
}