Skip to content

Commit

Permalink
zaps: Improve discoverability of profile zaps
Browse files Browse the repository at this point in the history
via zappability badges and profile action sheets

This commit improves discoverability of zaps with the following changes:

1. New zap icon appears on profile pictures of events where the author of such event has zaps setup
2. Clicking on a profile picture from an event shows an action sheet that makes it easier to see a preview of their profile, and a zap button

Testing
-------

Devices:
- iPhone 14 Pro simulator
- iPad 10 simulator

iOS:
- 17.0.1
- 16.4

Damus: This commit

Coverage:
1. Checked that zap icon appears on profile pictures on events in different feeds and threads
2. Checked that this zap icon only appears for profiles that have zaps enabled
3. Checked that profile action sheet looks good on both light mode and dark mode
4. Checked that long descriptions are truncated and the "see more" "see less" buttons work
5. Checked that clicking "see more" or "see less" adapts the size of the action sheet (on iPhone)
6. Checked that action sheet looks good whether or not the user has a website link setup
7. Checked that long presses on the zap button in the action sheet bring the same options as the normal profile view
8. Checked all the buttons in the action sheet take the user to the expected place
9. Checked that the original profile view looks good (on both light and dark mode)

Notes:
- Action sheet cannot be resized on iPad.
- Could not test on Mac Catalyst because there seems to be a crash on the creation of a new account

Reference ticket: #1596

Changelog-Added: Improve discoverability of profile zaps with zappability badges and profile action sheets
Signed-off-by: Daniel D’Aquino <[email protected]>
Signed-off-by: William Casarin <[email protected]>
  • Loading branch information
danieldaquino authored and jb55 committed Oct 22, 2023
1 parent 4ed79ff commit e70f270
Show file tree
Hide file tree
Showing 14 changed files with 453 additions and 137 deletions.
9 changes: 9 additions & 0 deletions damus.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -430,13 +430,16 @@
BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */; };
D2277EEA2A089BD5006C3807 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2277EE92A089BD5006C3807 /* Router.swift */; };
D71DC1EC2A9129C3006E207C /* PostViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71DC1EB2A9129C3006E207C /* PostViewTests.swift */; };
D723C38E2AB8D83400065664 /* ContentFilters.swift in Sources */ = {isa = PBXBuildFile; fileRef = D723C38D2AB8D83400065664 /* ContentFilters.swift */; };
D72A2D022AD9C136002AFF62 /* EventViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72A2CFF2AD9B66B002AFF62 /* EventViewTests.swift */; };
D72A2D052AD9C1B5002AFF62 /* MockDamusState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72A2D042AD9C1B5002AFF62 /* MockDamusState.swift */; };
D72A2D072AD9C1FB002AFF62 /* MockProfiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72A2D062AD9C1FB002AFF62 /* MockProfiles.swift */; };
D723C38E2AB8D83400065664 /* ContentFilters.swift in Sources */ = {isa = PBXBuildFile; fileRef = D723C38D2AB8D83400065664 /* ContentFilters.swift */; };
D7315A2A2ACDF3B70036E30A /* DamusCacheManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7315A292ACDF3B70036E30A /* DamusCacheManager.swift */; };
D7315A2C2ACDF4DA0036E30A /* DamusCacheManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7315A2B2ACDF4DA0036E30A /* DamusCacheManagerTests.swift */; };
D783A63F2AD4E53D00658DDA /* SuggestedHashtagsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D783A63E2AD4E53D00658DDA /* SuggestedHashtagsView.swift */; };
D76874F32AE3632B00FB0F68 /* ZapButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D76874F22AE3632B00FB0F68 /* ZapButtonView.swift */; };
D77BFA0B2AE3051200621634 /* ProfileActionSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77BFA0A2AE3051200621634 /* ProfileActionSheetView.swift */; };
D78525252A7B2EA4002FA637 /* NoteContentViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78525242A7B2EA4002FA637 /* NoteContentViewTests.swift */; };
D7870BC12AC4750B0080BA88 /* MentionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7870BC02AC4750B0080BA88 /* MentionView.swift */; };
D7870BC32AC47EBC0080BA88 /* EventLoaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7870BC22AC47EBC0080BA88 /* EventLoaderView.swift */; };
Expand Down Expand Up @@ -1136,6 +1139,8 @@
D7315A292ACDF3B70036E30A /* DamusCacheManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusCacheManager.swift; sourceTree = "<group>"; };
D7315A2B2ACDF4DA0036E30A /* DamusCacheManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusCacheManagerTests.swift; sourceTree = "<group>"; };
D783A63E2AD4E53D00658DDA /* SuggestedHashtagsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestedHashtagsView.swift; sourceTree = "<group>"; };
D76874F22AE3632B00FB0F68 /* ZapButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZapButtonView.swift; sourceTree = "<group>"; };
D77BFA0A2AE3051200621634 /* ProfileActionSheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileActionSheetView.swift; sourceTree = "<group>"; };
D78525242A7B2EA4002FA637 /* NoteContentViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteContentViewTests.swift; sourceTree = "<group>"; };
D7870BC02AC4750B0080BA88 /* MentionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MentionView.swift; sourceTree = "<group>"; };
D7870BC22AC47EBC0080BA88 /* EventLoaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventLoaderView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1725,6 +1730,7 @@
5C513FCB2984ACA60072348F /* QRCodeView.swift */,
643EA5C7296B764E005081BB /* RelayFilterView.swift */,
D783A63E2AD4E53D00658DDA /* SuggestedHashtagsView.swift */,
D77BFA0A2AE3051200621634 /* ProfileActionSheetView.swift */,
);
path = Views;
sourceTree = "<group>";
Expand Down Expand Up @@ -2236,6 +2242,7 @@
4C9F18E129AA9B6C008C55EC /* CustomizeZapView.swift */,
4CA3FA0F29F593D000FDB3C3 /* ZapTypePicker.swift */,
4C73C5132A4437C10062CAC0 /* ZapUserView.swift */,
D76874F22AE3632B00FB0F68 /* ZapButtonView.swift */,
);
path = Zaps;
sourceTree = "<group>";
Expand Down Expand Up @@ -2960,6 +2967,8 @@
E4FA1C032A24BB7F00482697 /* SearchSettingsView.swift in Sources */,
4C75EFBB2804A34C0006080F /* ProofOfWork.swift in Sources */,
4C3AC7A52836987600E1F516 /* MainTabView.swift in Sources */,
D76874F32AE3632B00FB0F68 /* ZapButtonView.swift in Sources */,
D77BFA0B2AE3051200621634 /* ProfileActionSheetView.swift in Sources */,
4C1A9A1F29DDD24B00516EAC /* AppearanceSettingsView.swift in Sources */,
3AA59D1D2999B0400061C48E /* DraftsModel.swift in Sources */,
3169CAED294FCCFC00EE4006 /* Constants.swift in Sources */,
Expand Down
14 changes: 14 additions & 0 deletions damus/Components/NeutralButtonStyle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@ struct NeutralButtonStyle: ButtonStyle {
}
}

struct NeutralCircleButtonStyle: ButtonStyle {
func makeBody(configuration: Self.Configuration) -> some View {
return configuration.label
.padding(20)
.background(DamusColors.neutral1)
.cornerRadius(9999)
.overlay(
RoundedRectangle(cornerRadius: 9999)
.stroke(DamusColors.neutral3, lineWidth: 1)
)
.scaleEffect(configuration.isPressed ? 0.95 : 1)
}
}


struct NeutralButtonStyle_Previews: PreviewProvider {
static var previews: some View {
Expand Down
11 changes: 11 additions & 0 deletions damus/Components/SelectableText.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,27 @@ import SwiftUI
struct SelectableText: View {

let attributedString: AttributedString
let textAlignment: NSTextAlignment

@State private var selectedTextHeight: CGFloat = .zero
@State private var selectedTextWidth: CGFloat = .zero

let size: EventViewKind

init(attributedString: AttributedString, textAlignment: NSTextAlignment? = nil, size: EventViewKind) {
self.attributedString = attributedString
self.textAlignment = textAlignment ?? NSTextAlignment.natural
self.size = size
}

var body: some View {
GeometryReader { geo in
TextViewRepresentable(
attributedString: attributedString,
textColor: UIColor.label,
font: eventviewsize_to_uifont(size),
fixedWidth: selectedTextWidth,
textAlignment: self.textAlignment,
height: $selectedTextHeight
)
.padding([.leading, .trailing], -1.0)
Expand All @@ -48,6 +56,7 @@ struct SelectableText: View {
let textColor: UIColor
let font: UIFont
let fixedWidth: CGFloat
let textAlignment: NSTextAlignment

@Binding var height: CGFloat

Expand All @@ -61,12 +70,14 @@ struct SelectableText: View {
view.textContainerInset = .zero
view.textContainerInset.left = 1.0
view.textContainerInset.right = 1.0
view.textAlignment = textAlignment
return view
}

func updateUIView(_ uiView: UITextView, context: UIViewRepresentableContext<Self>) {
let mutableAttributedString = createNSAttributedString()
uiView.attributedText = mutableAttributedString
uiView.textAlignment = self.textAlignment

let newHeight = mutableAttributedString.height(containerWidth: fixedWidth)

Expand Down
30 changes: 27 additions & 3 deletions damus/Components/WebsiteLink.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,57 @@ import SwiftUI

struct WebsiteLink: View {
let url: URL
let style: StyleVariant
@Environment(\.openURL) var openURL

init(url: URL, style: StyleVariant? = nil) {
self.url = url
self.style = style ?? .normal
}

var body: some View {
HStack {
Image("link")
.foregroundColor(.gray)
.font(.footnote)
.resizable()
.frame(width: 16, height: 16)
.foregroundColor(self.style == .accent ? .white : .gray)
.padding(.vertical, 5)
.padding([.leading], 10)

Button(action: {
openURL(url)
}, label: {
Text(link_text)
.font(.footnote)
.foregroundColor(.accentColor)
.foregroundColor(self.style == .accent ? .white : .accentColor)
.truncationMode(.tail)
.lineLimit(1)
})
.padding(.vertical, 5)
.padding([.trailing], 10)
}
.background(
self.style == .accent ?
AnyView(RoundedRectangle(cornerRadius: 50).fill(PinkGradient))
: AnyView(Color.clear)
)
}

var link_text: String {
url.host ?? url.absoluteString
}

enum StyleVariant {
case normal
case accent
}
}

struct WebsiteLink_Previews: PreviewProvider {
static var previews: some View {
WebsiteLink(url: URL(string: "https://jb55.com")!)
.previewDisplayName("Normal")
WebsiteLink(url: URL(string: "https://jb55.com")!, style: .accent)
.previewDisplayName("Accent")
}
}
4 changes: 4 additions & 0 deletions damus/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ enum Sheets: Identifiable {
case post(PostAction)
case report(ReportTarget)
case event(NostrEvent)
case profile_action(Pubkey)
case zap(ZapSheet)
case select_wallet(SelectWallet)
case filter
Expand All @@ -42,6 +43,7 @@ enum Sheets: Identifiable {
case .user_status: return "user_status"
case .post(let action): return "post-" + (action.ev?.id.hex() ?? "")
case .event(let ev): return "event-" + ev.id.hex()
case .profile_action(let pubkey): return "profile-action-" + pubkey.npub
case .zap(let sheet): return "zap-" + hex_encode(sheet.target.id)
case .select_wallet: return "select-wallet"
case .filter: return "filter"
Expand Down Expand Up @@ -316,6 +318,8 @@ struct ContentView: View {
.presentationDragIndicator(.visible)
case .event:
EventDetailView()
case .profile_action(let pubkey):
ProfileActionSheetView(damus_state: damus_state!, pubkey: pubkey)
case .zap(let zapsheet):
CustomizeZapView(state: damus_state!, target: zapsheet.target, lnurl: zapsheet.lnurl)
case .select_wallet(let select):
Expand Down
4 changes: 2 additions & 2 deletions damus/Views/Events/EventProfile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ struct EventProfile: View {

var body: some View {
HStack(alignment: .center, spacing: 10) {
ProfilePicView(pubkey: pubkey, size: pfp_size, highlight: .none, profiles: damus_state.profiles, disable_animation: disable_animation)
ProfilePicView(pubkey: pubkey, size: pfp_size, highlight: .none, profiles: damus_state.profiles, disable_animation: disable_animation, show_zappability: true)
.onTapGesture {
damus_state.nav.push(route: .ProfileByKey(pubkey: pubkey))
notify(.present_sheet(Sheets.profile_action(pubkey)))
}

VStack(alignment: .leading, spacing: 0) {
Expand Down
12 changes: 10 additions & 2 deletions damus/Views/Profile/AboutView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,23 @@ import SwiftUI
struct AboutView: View {
let state: DamusState
let about: String
let max_about_length = 280
let max_about_length: Int
let text_alignment: NSTextAlignment
@State var show_full_about: Bool = false
@State private var about_string: AttributedString? = nil

init(state: DamusState, about: String, max_about_length: Int? = nil, text_alignment: NSTextAlignment? = nil) {
self.state = state
self.about = about
self.max_about_length = max_about_length ?? 280
self.text_alignment = text_alignment ?? .natural
}

var body: some View {
Group {
if let about_string {
let truncated_about = show_full_about ? about_string : about_string.truncateOrNil(maxLength: max_about_length)
SelectableText(attributedString: truncated_about ?? about_string, size: .subheadline)
SelectableText(attributedString: truncated_about ?? about_string, textAlignment: self.text_alignment, size: .subheadline)

if truncated_about != nil {
if show_full_about {
Expand Down
6 changes: 3 additions & 3 deletions damus/Views/Profile/MaybeAnonPfpView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,16 @@ struct MaybeAnonPfpView: View {
}

var body: some View {
Group {
ZStack {
if is_anon {
Image("question")
.resizable()
.font(.largeTitle)
.frame(width: size, height: size)
} else {
ProfilePicView(pubkey: pubkey, size: size, highlight: .none, profiles: state.profiles, disable_animation: state.settings.disable_animation)
ProfilePicView(pubkey: pubkey, size: size, highlight: .none, profiles: state.profiles, disable_animation: state.settings.disable_animation, show_zappability: true)
.onTapGesture {
state.nav.push(route: Route.ProfileByKey(pubkey: pubkey))
notify(.present_sheet(Sheets.profile_action(pubkey)))
}
}
}
Expand Down
80 changes: 1 addition & 79 deletions damus/Views/Profile/ProfileNameView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,84 +7,6 @@

import SwiftUI

fileprivate struct KeyView: View {
let pubkey: Pubkey

@Environment(\.colorScheme) var colorScheme

@State private var isCopied = false

func keyColor() -> Color {
colorScheme == .light ? DamusColors.black : DamusColors.white
}

private func copyPubkey(_ pubkey: String) {
UIPasteboard.general.string = pubkey
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
withAnimation {
isCopied = true
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
withAnimation {
isCopied = false
}
}
}
}

func pubkey_context_menu(pubkey: Pubkey) -> some View {
return self.contextMenu {
Button {
UIPasteboard.general.string = pubkey.npub
} label: {
Label(NSLocalizedString("Copy Account ID", comment: "Context menu option for copying the ID of the account that created the note."), image: "copy2")
}
}
}

var body: some View {
let bech32 = pubkey.npub

HStack {
Text(verbatim: "\(abbrev_pubkey(bech32, amount: 16))")
.font(.footnote)
.foregroundColor(keyColor())
.padding(5)
.padding([.leading, .trailing], 5)
.background(RoundedRectangle(cornerRadius: 11).foregroundColor(DamusColors.adaptableGrey))

if isCopied {
HStack {
Image("check-circle")
.resizable()
.frame(width: 20, height: 20)
Text(NSLocalizedString("Copied", comment: "Label indicating that a user's key was copied."))
.font(.footnote)
.layoutPriority(1)
}
.foregroundColor(DamusColors.green)
} else {
HStack {
Button {
copyPubkey(bech32)
} label: {
Label {
Text("Public key", comment: "Label indicating that the text is a user's public account key.")
} icon: {
Image("copy2")
.resizable()
.contentShape(Rectangle())
.foregroundColor(.accentColor)
.frame(width: 20, height: 20)
}
.labelStyle(IconOnlyLabelStyle())
.symbolRenderingMode(.hierarchical)
}
}
}
}
}
}

struct ProfileNameView: View {
let pubkey: Pubkey
let damus: DamusState
Expand Down Expand Up @@ -116,7 +38,7 @@ struct ProfileNameView: View {

Spacer()

KeyView(pubkey: pubkey)
PubkeyView(pubkey: pubkey)
.pubkey_context_menu(pubkey: pubkey)
}
}
Expand Down
Loading

0 comments on commit e70f270

Please sign in to comment.