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>
158 lines
5.2 KiB
Swift
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
|
|
)
|
|
}
|
|
}
|