Files
damus/damus/Views/Profile/ProfilePicView.swift
Daniel D’Aquino e70f270c5c zaps: Improve discoverability of profile zaps
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: https://github.com/damus-io/damus/issues/1596

Changelog-Added: Improve discoverability of profile zaps with zappability badges and profile action sheets
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-10-22 10:40:31 +08:00

158 lines
5.2 KiB
Swift

//
// ProfilePicView.swift
// damus
//
// Created by William Casarin on 2022-04-16.
//
import SwiftUI
import Kingfisher
let PFP_SIZE: CGFloat = 52.0
func highlight_color(_ h: Highlight) -> Color {
switch h {
case .main: return Color.red
case .reply: return Color.black
case .none: return Color.black
case .custom(let c, _): return c
}
}
func pfp_line_width(_ h: Highlight) -> CGFloat {
switch h {
case .reply: return 0
case .none: return 0
case .main: return 3
case .custom(_, let lw): return CGFloat(lw)
}
}
struct InnerProfilePicView: View {
let url: URL?
let fallbackUrl: URL?
let pubkey: Pubkey
let size: CGFloat
let highlight: Highlight
let disable_animation: Bool
var Placeholder: some View {
Circle()
.frame(width: size, height: size)
.foregroundColor(DamusColors.mediumGrey)
.overlay(Circle().stroke(highlight_color(highlight), lineWidth: pfp_line_width(highlight)))
.padding(2)
}
var body: some View {
KFAnimatedImage(url)
.imageContext(.pfp, disable_animation: disable_animation)
.onFailure(fallbackUrl: fallbackUrl, cacheKey: url?.absoluteString)
.cancelOnDisappear(true)
.configure { view in
view.framePreloadCount = 3
}
.placeholder { _ in
Placeholder
}
.scaledToFill()
.frame(width: size, height: size)
.clipShape(Circle())
.overlay(Circle().stroke(highlight_color(highlight), lineWidth: pfp_line_width(highlight)))
}
}
struct ProfilePicView: View {
let pubkey: Pubkey
let size: CGFloat
let highlight: Highlight
let profiles: Profiles
let disable_animation: Bool
let zappability_indicator: Bool
@State var picture: String?
init(pubkey: Pubkey, size: CGFloat, highlight: Highlight, profiles: Profiles, disable_animation: Bool, picture: String? = nil, show_zappability: Bool? = nil) {
self.pubkey = pubkey
self.profiles = profiles
self.size = size
self.highlight = highlight
self._picture = State(initialValue: picture)
self.disable_animation = disable_animation
self.zappability_indicator = show_zappability ?? false
}
func get_lnurl() -> String? {
return profiles.lookup_with_timestamp(pubkey).unsafeUnownedValue?.lnurl
}
var body: some View {
ZStack (alignment: Alignment(horizontal: .trailing, vertical: .bottom)) {
InnerProfilePicView(url: get_profile_url(picture: picture, pubkey: pubkey, profiles: profiles), fallbackUrl: URL(string: robohash(pubkey)), pubkey: pubkey, size: size, highlight: highlight, disable_animation: disable_animation)
.onReceive(handle_notify(.profile_updated)) { updated in
guard updated.pubkey == self.pubkey else {
return
}
switch updated {
case .manual(_, let profile):
if let pic = profile.picture {
self.picture = pic
}
case .remote(pubkey: let pk):
let profile_txn = profiles.lookup(id: pk)
let profile = profile_txn.unsafeUnownedValue
if let pic = profile?.picture {
self.picture = pic
}
}
}
if self.zappability_indicator, let lnurl = self.get_lnurl(), lnurl != "" {
Image("zap.fill")
.resizable()
.frame(
width: size * 0.24,
height: size * 0.24
)
.padding(size * 0.04)
.foregroundColor(.white)
.background(Color.orange)
.clipShape(Circle())
}
}
}
}
func get_profile_url(picture: String?, pubkey: Pubkey, profiles: Profiles) -> URL {
let pic = picture ?? profiles.lookup(id: pubkey).map({ $0?.picture }).value ?? robohash(pubkey)
if let url = URL(string: pic) {
return url
}
return URL(string: robohash(pubkey))!
}
func make_preview_profiles(_ pubkey: Pubkey) -> Profiles {
let profiles = Profiles(ndb: test_damus_state.ndb)
let picture = "http://cdn.jb55.com/img/red-me.jpg"
let profile = Profile(name: "jb55", display_name: "William Casarin", about: "It's me", picture: picture, banner: "", website: "https://jb55.com", lud06: nil, lud16: nil, nip05: "jb55.com", damus_donation: nil)
//let ts_profile = TimestampedProfile(profile: profile, timestamp: 0, event: test_note)
//profiles.add(id: pubkey, profile: ts_profile)
return profiles
}
struct ProfilePicView_Previews: PreviewProvider {
static let pubkey = test_note.pubkey
static var previews: some View {
ProfilePicView(
pubkey: pubkey,
size: 100,
highlight: .none,
profiles: make_preview_profiles(pubkey),
disable_animation: false
)
}
}