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:
committed by
William Casarin
parent
605d88add1
commit
35df9f7ab7
@@ -31,7 +31,7 @@ struct ChatEventView: View {
|
|||||||
@State var long_press_bounce_work_item: DispatchWorkItem?
|
@State var long_press_bounce_work_item: DispatchWorkItem?
|
||||||
@State var popover_state: PopoverState = .closed {
|
@State var popover_state: PopoverState = .closed {
|
||||||
didSet {
|
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()
|
generator.impactOccurred()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,6 +43,11 @@ struct ChatEventView: View {
|
|||||||
enum PopoverState: String {
|
enum PopoverState: String {
|
||||||
case closed
|
case closed
|
||||||
case open_emoji_selector
|
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 {
|
var just_started: Bool {
|
||||||
@@ -90,9 +95,22 @@ struct ChatEventView: View {
|
|||||||
var by_other_user: Bool {
|
var by_other_user: Bool {
|
||||||
return event.pubkey != damus_state.pubkey
|
return event.pubkey != damus_state.pubkey
|
||||||
}
|
}
|
||||||
|
|
||||||
var is_ours: Bool { return !by_other_user }
|
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 {
|
var event_bubble: some View {
|
||||||
ChatBubble(
|
ChatBubble(
|
||||||
direction: is_ours ? .right : .left,
|
direction: is_ours ? .right : .left,
|
||||||
@@ -170,6 +188,14 @@ struct ChatEventView: View {
|
|||||||
EmojiPickerView(selectedEmoji: $selected_emoji, emojiProvider: damus_state.emoji_provider)
|
EmojiPickerView(selectedEmoji: $selected_emoji, emojiProvider: damus_state.emoji_provider)
|
||||||
}.presentationDetents([.medium, .large])
|
}.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
|
.onChange(of: selected_emoji) { newSelectedEmoji in
|
||||||
if let newSelectedEmoji {
|
if let newSelectedEmoji {
|
||||||
send_like(emoji: newSelectedEmoji.value)
|
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)
|
.scaleEffect(self.popover_state.some_sheet_open() ? 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)
|
.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: {
|
.onLongPressGesture(minimumDuration: 0.5, maximumDistance: 10, perform: {
|
||||||
long_press_bounce_work_item?.cancel()
|
long_press_bounce_work_item?.cancel()
|
||||||
}, onPressingChanged: { is_pressing in
|
}, onPressingChanged: { is_pressing in
|
||||||
@@ -192,7 +218,8 @@ struct ChatEventView: View {
|
|||||||
// Ensure the action is performed only if the condition is still valid
|
// Ensure the action is performed only if the condition is still valid
|
||||||
if self.is_pressing {
|
if self.is_pressing {
|
||||||
withAnimation(.bouncy(duration: 0.2, extraBounce: 0.35)) {
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
extension View {
|
||||||
func hideKeyboard() {
|
func hideKeyboard() {
|
||||||
let resign = #selector(UIResponder.resignFirstResponder)
|
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: "")
|
fileprivate func test_zap_sheet() -> ZapSheet {
|
||||||
.frame(width: 400, height: 600)
|
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)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user