Use kingfisher for profile pic loading
Changelog-Changed: Use an optimized library for image loading Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
@@ -26,7 +26,6 @@
|
|||||||
4C285C8C28398BC7008A31F1 /* Keys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C285C8B28398BC6008A31F1 /* Keys.swift */; };
|
4C285C8C28398BC7008A31F1 /* Keys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C285C8B28398BC6008A31F1 /* Keys.swift */; };
|
||||||
4C285C8E28399BFE008A31F1 /* SaveKeysView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C285C8D28399BFD008A31F1 /* SaveKeysView.swift */; };
|
4C285C8E28399BFE008A31F1 /* SaveKeysView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C285C8D28399BFD008A31F1 /* SaveKeysView.swift */; };
|
||||||
4C363A8428233689006E126D /* Parser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363A8328233689006E126D /* Parser.swift */; };
|
4C363A8428233689006E126D /* Parser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363A8328233689006E126D /* Parser.swift */; };
|
||||||
4C363A8628234FDE006E126D /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363A8528234FDE006E126D /* ImageCache.swift */; };
|
|
||||||
4C363A8828236948006E126D /* BlocksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363A8728236948006E126D /* BlocksView.swift */; };
|
4C363A8828236948006E126D /* BlocksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363A8728236948006E126D /* BlocksView.swift */; };
|
||||||
4C363A8A28236B57006E126D /* MentionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363A8928236B57006E126D /* MentionView.swift */; };
|
4C363A8A28236B57006E126D /* MentionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363A8928236B57006E126D /* MentionView.swift */; };
|
||||||
4C363A8C28236B92006E126D /* PubkeyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363A8B28236B92006E126D /* PubkeyView.swift */; };
|
4C363A8C28236B92006E126D /* PubkeyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363A8B28236B92006E126D /* PubkeyView.swift */; };
|
||||||
@@ -146,7 +145,6 @@
|
|||||||
4C285C8B28398BC6008A31F1 /* Keys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keys.swift; sourceTree = "<group>"; };
|
4C285C8B28398BC6008A31F1 /* Keys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keys.swift; sourceTree = "<group>"; };
|
||||||
4C285C8D28399BFD008A31F1 /* SaveKeysView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveKeysView.swift; sourceTree = "<group>"; };
|
4C285C8D28399BFD008A31F1 /* SaveKeysView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveKeysView.swift; sourceTree = "<group>"; };
|
||||||
4C363A8328233689006E126D /* Parser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Parser.swift; sourceTree = "<group>"; };
|
4C363A8328233689006E126D /* Parser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Parser.swift; sourceTree = "<group>"; };
|
||||||
4C363A8528234FDE006E126D /* ImageCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCache.swift; sourceTree = "<group>"; };
|
|
||||||
4C363A8728236948006E126D /* BlocksView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlocksView.swift; sourceTree = "<group>"; };
|
4C363A8728236948006E126D /* BlocksView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlocksView.swift; sourceTree = "<group>"; };
|
||||||
4C363A8928236B57006E126D /* MentionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MentionView.swift; sourceTree = "<group>"; };
|
4C363A8928236B57006E126D /* MentionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MentionView.swift; sourceTree = "<group>"; };
|
||||||
4C363A8B28236B92006E126D /* PubkeyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PubkeyView.swift; sourceTree = "<group>"; };
|
4C363A8B28236B92006E126D /* PubkeyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PubkeyView.swift; sourceTree = "<group>"; };
|
||||||
@@ -366,7 +364,6 @@
|
|||||||
4CEE2AF4280B29E600AB5EEF /* TimeAgo.swift */,
|
4CEE2AF4280B29E600AB5EEF /* TimeAgo.swift */,
|
||||||
4CE4F8CC281352B30009DFBB /* Notifications.swift */,
|
4CE4F8CC281352B30009DFBB /* Notifications.swift */,
|
||||||
4C363A8328233689006E126D /* Parser.swift */,
|
4C363A8328233689006E126D /* Parser.swift */,
|
||||||
4C363A8528234FDE006E126D /* ImageCache.swift */,
|
|
||||||
4C363AA728297703006E126D /* InsertSort.swift */,
|
4C363AA728297703006E126D /* InsertSort.swift */,
|
||||||
4C477C9D282C3A4800033AA3 /* TipCounter.swift */,
|
4C477C9D282C3A4800033AA3 /* TipCounter.swift */,
|
||||||
4C285C8B28398BC6008A31F1 /* Keys.swift */,
|
4C285C8B28398BC6008A31F1 /* Keys.swift */,
|
||||||
@@ -610,7 +607,6 @@
|
|||||||
4C285C86283892E7008A31F1 /* CreateAccountModel.swift in Sources */,
|
4C285C86283892E7008A31F1 /* CreateAccountModel.swift in Sources */,
|
||||||
4C64987C286D03E000EAE2B3 /* DirectMessagesView.swift in Sources */,
|
4C64987C286D03E000EAE2B3 /* DirectMessagesView.swift in Sources */,
|
||||||
4C363A8C28236B92006E126D /* PubkeyView.swift in Sources */,
|
4C363A8C28236B92006E126D /* PubkeyView.swift in Sources */,
|
||||||
4C363A8628234FDE006E126D /* ImageCache.swift in Sources */,
|
|
||||||
4C5C7E68284ED36500A22DF5 /* SearchHomeModel.swift in Sources */,
|
4C5C7E68284ED36500A22DF5 /* SearchHomeModel.swift in Sources */,
|
||||||
4C75EFB728049D990006080F /* RelayPool.swift in Sources */,
|
4C75EFB728049D990006080F /* RelayPool.swift in Sources */,
|
||||||
4CE6DEE927F7A08100C66700 /* ContentView.swift in Sources */,
|
4CE6DEE927F7A08100C66700 /* ContentView.swift in Sources */,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Starscream
|
import Starscream
|
||||||
|
//import Kingfisher
|
||||||
|
|
||||||
let BOOTSTRAP_RELAYS = [
|
let BOOTSTRAP_RELAYS = [
|
||||||
"wss://relay.damus.io",
|
"wss://relay.damus.io",
|
||||||
@@ -204,6 +205,7 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
.onAppear() {
|
.onAppear() {
|
||||||
self.connect()
|
self.connect()
|
||||||
|
//KingfisherManager.shared.cache.clearDiskCache()
|
||||||
setup_notifications()
|
setup_notifications()
|
||||||
}
|
}
|
||||||
.sheet(item: $active_sheet) { item in
|
.sheet(item: $active_sheet) { item in
|
||||||
@@ -371,7 +373,6 @@ struct ContentView: View {
|
|||||||
boosts: EventCounter(our_pubkey: pubkey),
|
boosts: EventCounter(our_pubkey: pubkey),
|
||||||
contacts: Contacts(),
|
contacts: Contacts(),
|
||||||
tips: TipCounter(our_pubkey: pubkey),
|
tips: TipCounter(our_pubkey: pubkey),
|
||||||
image_cache: ImageCache(),
|
|
||||||
profiles: Profiles(),
|
profiles: Profiles(),
|
||||||
dms: home.dms
|
dms: home.dms
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ struct DamusState {
|
|||||||
let boosts: EventCounter
|
let boosts: EventCounter
|
||||||
let contacts: Contacts
|
let contacts: Contacts
|
||||||
let tips: TipCounter
|
let tips: TipCounter
|
||||||
let image_cache: ImageCache
|
|
||||||
let profiles: Profiles
|
let profiles: Profiles
|
||||||
let dms: DirectMessagesModel
|
let dms: DirectMessagesModel
|
||||||
|
|
||||||
@@ -23,6 +22,6 @@ struct DamusState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static var empty: DamusState {
|
static var empty: DamusState {
|
||||||
return DamusState.init(pool: RelayPool(), keypair: Keypair(pubkey: "", privkey: ""), likes: EventCounter(our_pubkey: ""), boosts: EventCounter(our_pubkey: ""), contacts: Contacts(), tips: TipCounter(our_pubkey: ""), image_cache: ImageCache(), profiles: Profiles(), dms: DirectMessagesModel())
|
return DamusState.init(pool: RelayPool(), keypair: Keypair(pubkey: "", privkey: ""), likes: EventCounter(our_pubkey: ""), boosts: EventCounter(our_pubkey: ""), contacts: Contacts(), tips: TipCounter(our_pubkey: ""), profiles: Profiles(), dms: DirectMessagesModel())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ class FollowersModel: ObservableObject {
|
|||||||
if ev.known_kind == .contacts {
|
if ev.known_kind == .contacts {
|
||||||
handle_contact_event(ev)
|
handle_contact_event(ev)
|
||||||
} else if ev.known_kind == .metadata {
|
} else if ev.known_kind == .metadata {
|
||||||
process_metadata_event(image_cache: damus_state.image_cache, profiles: damus_state.profiles, ev: ev)
|
process_metadata_event(profiles: damus_state.profiles, ev: ev)
|
||||||
}
|
}
|
||||||
|
|
||||||
case .notice(let msg):
|
case .notice(let msg):
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ class FollowingModel {
|
|||||||
switch nev {
|
switch nev {
|
||||||
case .event(_, let ev):
|
case .event(_, let ev):
|
||||||
if ev.kind == 0 {
|
if ev.kind == 0 {
|
||||||
process_metadata_event(image_cache: damus_state.image_cache, profiles: damus_state.profiles, ev: ev)
|
process_metadata_event(profiles: damus_state.profiles, ev: ev)
|
||||||
}
|
}
|
||||||
case .notice(let msg):
|
case .notice(let msg):
|
||||||
print("followingmodel notice: \(msg)")
|
print("followingmodel notice: \(msg)")
|
||||||
|
|||||||
@@ -321,7 +321,7 @@ class HomeModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handle_metadata_event(_ ev: NostrEvent) {
|
func handle_metadata_event(_ ev: NostrEvent) {
|
||||||
process_metadata_event(image_cache: damus_state.image_cache, profiles: damus_state.profiles, ev: ev)
|
process_metadata_event(profiles: damus_state.profiles, ev: ev)
|
||||||
}
|
}
|
||||||
|
|
||||||
func get_last_event_of_kind(relay_id: String, kind: Int) -> NostrEvent? {
|
func get_last_event_of_kind(relay_id: String, kind: Int) -> NostrEvent? {
|
||||||
@@ -510,7 +510,7 @@ func print_filters(relay_id: String?, filters groups: [[NostrFilter]]) {
|
|||||||
print("-----")
|
print("-----")
|
||||||
}
|
}
|
||||||
|
|
||||||
func process_metadata_event(image_cache: ImageCache, profiles: Profiles, ev: NostrEvent) {
|
func process_metadata_event(profiles: Profiles, ev: NostrEvent) {
|
||||||
guard let profile: Profile = decode_data(Data(ev.content.utf8)) else {
|
guard let profile: Profile = decode_data(Data(ev.content.utf8)) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -527,14 +527,9 @@ func process_metadata_event(image_cache: ImageCache, profiles: Profiles, ev: Nos
|
|||||||
|
|
||||||
// load pfps asap
|
// load pfps asap
|
||||||
let picture = tprof.profile.picture ?? robohash(ev.pubkey)
|
let picture = tprof.profile.picture ?? robohash(ev.pubkey)
|
||||||
if let url = URL(string: picture) {
|
if let _ = URL(string: picture) {
|
||||||
Task<UIImage?, Never>.init(priority: .background) {
|
DispatchQueue.main.async {
|
||||||
let pfp_key = pfp_cache_key(url: url)
|
notify(.profile_updated, ProfileUpdate(pubkey: ev.pubkey, profile: profile))
|
||||||
let res = await image_cache.lookup_or_load_image(key: pfp_key, url: url)
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
notify(.profile_updated, ProfileUpdate(pubkey: ev.pubkey, profile: profile))
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ func load_profiles(profiles_subid: String, relay_id: String, events: [NostrEvent
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ev.known_kind == .metadata {
|
if ev.known_kind == .metadata {
|
||||||
process_metadata_event(image_cache: damus_state.image_cache, profiles: damus_state.profiles, ev: ev)
|
process_metadata_event(profiles: damus_state.profiles, ev: ev)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ class ThreadModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ev.known_kind == .metadata {
|
if ev.known_kind == .metadata {
|
||||||
process_metadata_event(image_cache: damus_state.image_cache, profiles: damus_state.profiles, ev: ev)
|
process_metadata_event(profiles: damus_state.profiles, ev: ev)
|
||||||
} else if ev.is_textlike {
|
} else if ev.is_textlike {
|
||||||
self.add_event(ev, privkey: self.damus_state.keypair.privkey)
|
self.add_event(ev, privkey: self.damus_state.keypair.privkey)
|
||||||
} else if ev.known_kind == .channel_meta || ev.known_kind == .channel_create {
|
} else if ev.known_kind == .channel_meta || ev.known_kind == .channel_create {
|
||||||
|
|||||||
@@ -1,193 +0,0 @@
|
|||||||
//
|
|
||||||
// ImageCache.swift
|
|
||||||
// damus
|
|
||||||
//
|
|
||||||
// Created by William Casarin on 2022-05-04.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import SwiftUI
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
enum ImageProcessingStatus {
|
|
||||||
case processing
|
|
||||||
case done
|
|
||||||
}
|
|
||||||
|
|
||||||
class ImageCache {
|
|
||||||
private let lock = NSLock()
|
|
||||||
private var state: [String: ImageProcessingStatus] = [:]
|
|
||||||
|
|
||||||
private func get_state(_ key: String) -> ImageProcessingStatus? {
|
|
||||||
lock.lock(); defer { lock.unlock() }
|
|
||||||
|
|
||||||
return state[key]
|
|
||||||
}
|
|
||||||
|
|
||||||
private func set_state(_ key: String, new_state: ImageProcessingStatus) {
|
|
||||||
lock.lock(); defer { lock.unlock() }
|
|
||||||
|
|
||||||
state[key] = new_state
|
|
||||||
}
|
|
||||||
|
|
||||||
lazy var cache: NSCache<NSString, UIImage> = {
|
|
||||||
let cache = NSCache<NSString, UIImage>()
|
|
||||||
cache.totalCostLimit = 1024 * 1024 * 100 // 100MB
|
|
||||||
return cache
|
|
||||||
}()
|
|
||||||
|
|
||||||
// simple polling until I can figure out a better way to do this
|
|
||||||
func wait_for_image(_ key: String) async {
|
|
||||||
while true {
|
|
||||||
let why_would_this_happen: ()? = try? await Task.sleep(nanoseconds: 100_000_000) // 100ms
|
|
||||||
if why_would_this_happen == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if get_state(key) == .done {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookup_sync(key: String) -> UIImage? {
|
|
||||||
let status = get_state(key)
|
|
||||||
|
|
||||||
switch status {
|
|
||||||
case .done:
|
|
||||||
break
|
|
||||||
case .processing:
|
|
||||||
return nil
|
|
||||||
case .none:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if let decoded = cache.object(forKey: NSString(string: key)) {
|
|
||||||
return decoded
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookup_or_load_image(key: String, url: URL?) async -> UIImage? {
|
|
||||||
if let img = await lookup(key: key) {
|
|
||||||
return img
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let url = url else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return await load_image(cache: self, from: url, key: key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func get_cache_url(key: String, suffix: String, ext: String = "png") -> URL? {
|
|
||||||
let urls = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)
|
|
||||||
|
|
||||||
guard let root = urls.first else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return root.appendingPathComponent("\(key)\(suffix).\(ext)")
|
|
||||||
}
|
|
||||||
|
|
||||||
private func lookup_file_cache(key: String, suffix: String = "_pfp") -> UIImage? {
|
|
||||||
guard let img_file = get_cache_url(key: key, suffix: suffix) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let img = UIImage(contentsOfFile: img_file.path) else {
|
|
||||||
//print("failed to load \(key)\(suffix).png from file cache")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
save_to_memory_cache(key: key, img: img)
|
|
||||||
|
|
||||||
return img
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookup(key: String) async -> UIImage? {
|
|
||||||
let status = get_state(key)
|
|
||||||
|
|
||||||
switch status {
|
|
||||||
case .done:
|
|
||||||
break
|
|
||||||
case .processing:
|
|
||||||
await wait_for_image(key)
|
|
||||||
case .none:
|
|
||||||
return lookup_file_cache(key: key)
|
|
||||||
}
|
|
||||||
|
|
||||||
if let decoded = cache.object(forKey: NSString(string: key)) {
|
|
||||||
return decoded
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func remove(key: String) {
|
|
||||||
lock.lock(); defer { lock.unlock() }
|
|
||||||
cache.removeObject(forKey: NSString(string: key))
|
|
||||||
}
|
|
||||||
|
|
||||||
func insert(_ image: UIImage, key: String) async -> UIImage? {
|
|
||||||
let scale = await UIScreen.main.scale
|
|
||||||
let size = CGSize(width: PFP_SIZE * scale, height: PFP_SIZE * scale)
|
|
||||||
|
|
||||||
set_state(key, new_state: .processing)
|
|
||||||
|
|
||||||
let decoded_image = await image.byPreparingThumbnail(ofSize: size)
|
|
||||||
|
|
||||||
save_to_memory_cache(key: key, img: decoded_image ?? UIImage())
|
|
||||||
if let img = decoded_image {
|
|
||||||
if !save_to_file_cache(key: key, img: img) {
|
|
||||||
print("failed saving \(key) pfp to file cache")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return decoded_image
|
|
||||||
}
|
|
||||||
|
|
||||||
func save_to_file_cache(key: String, img: UIImage, suffix: String = "_pfp") -> Bool {
|
|
||||||
guard let url = get_cache_url(key: key, suffix: suffix) else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let data = img.pngData() else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return (try? data.write(to: url)) != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func save_to_memory_cache(key: String, img: UIImage) {
|
|
||||||
lock.lock()
|
|
||||||
cache.setObject(img, forKey: NSString(string: key))
|
|
||||||
state[key] = .done
|
|
||||||
lock.unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func load_image(cache: ImageCache, from url: URL, key: String) async -> UIImage? {
|
|
||||||
guard let (data, _) = try? await URLSession.shared.data(from: url) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let img = UIImage(data: data) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return await cache.insert(img, key: key)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func hashed_hexstring(_ str: String) -> String {
|
|
||||||
guard let data = str.data(using: .utf8) else {
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
return hex_encode(sha256(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
func pfp_cache_key(url: URL) -> String {
|
|
||||||
return hashed_hexstring(url.absoluteString)
|
|
||||||
}
|
|
||||||
@@ -75,7 +75,7 @@ struct ChatView: View {
|
|||||||
HStack {
|
HStack {
|
||||||
VStack {
|
VStack {
|
||||||
if is_active || just_started {
|
if is_active || just_started {
|
||||||
ProfilePicView(pubkey: event.pubkey, size: 32, highlight: is_active ? .main : .none, image_cache: damus_state.image_cache, profiles: damus_state.profiles)
|
ProfilePicView(pubkey: event.pubkey, size: 32, highlight: is_active ? .main : .none, profiles: damus_state.profiles)
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
@@ -96,7 +96,7 @@ struct ChatView: View {
|
|||||||
|
|
||||||
if let ref_id = thread.replies.lookup(event.id) {
|
if let ref_id = thread.replies.lookup(event.id) {
|
||||||
if !is_reply_to_prev() {
|
if !is_reply_to_prev() {
|
||||||
ReplyQuoteView(privkey: damus_state.keypair.privkey, quoter: event, event_id: ref_id, image_cache: damus_state.image_cache, profiles: damus_state.profiles)
|
ReplyQuoteView(privkey: damus_state.keypair.privkey, quoter: event, event_id: ref_id, profiles: damus_state.profiles)
|
||||||
.frame(maxHeight: expand_reply ? nil : 100)
|
.frame(maxHeight: expand_reply ? nil : 100)
|
||||||
.environmentObject(thread)
|
.environmentObject(thread)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ struct DMChatView: View {
|
|||||||
let profile_page = ProfileView(damus_state: damus_state, profile: pmodel, followers: fmodel)
|
let profile_page = ProfileView(damus_state: damus_state, profile: pmodel, followers: fmodel)
|
||||||
return NavigationLink(destination: profile_page) {
|
return NavigationLink(destination: profile_page) {
|
||||||
HStack {
|
HStack {
|
||||||
ProfilePicView(pubkey: pubkey, size: 24, highlight: .none, image_cache: damus_state.image_cache, profiles: damus_state.profiles)
|
ProfilePicView(pubkey: pubkey, size: 24, highlight: .none, profiles: damus_state.profiles)
|
||||||
|
|
||||||
ProfileName(pubkey: pubkey, profile: profile, contacts: damus_state.contacts, show_friend_confirmed: true)
|
ProfileName(pubkey: pubkey, profile: profile, contacts: damus_state.contacts, show_friend_confirmed: true)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ struct EventView: View {
|
|||||||
let pv = ProfileView(damus_state: damus, profile: pmodel, followers: FollowersModel(damus_state: damus, target: pubkey))
|
let pv = ProfileView(damus_state: damus, profile: pmodel, followers: FollowersModel(damus_state: damus, target: pubkey))
|
||||||
|
|
||||||
NavigationLink(destination: pv) {
|
NavigationLink(destination: pv) {
|
||||||
ProfilePicView(pubkey: pubkey, size: PFP_SIZE, highlight: highlight, image_cache: damus.image_cache, profiles: damus.profiles)
|
ProfilePicView(pubkey: pubkey, size: PFP_SIZE, highlight: highlight, profiles: damus.profiles)
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ struct FollowUserView: View {
|
|||||||
let pv = ProfileView(damus_state: damus_state, profile: pmodel, followers: followers)
|
let pv = ProfileView(damus_state: damus_state, profile: pmodel, followers: followers)
|
||||||
|
|
||||||
NavigationLink(destination: pv) {
|
NavigationLink(destination: pv) {
|
||||||
ProfilePicView(pubkey: target.pubkey, size: PFP_SIZE, highlight: .none, image_cache: damus_state.image_cache, profiles: damus_state.profiles)
|
ProfilePicView(pubkey: target.pubkey, size: PFP_SIZE, highlight: .none, profiles: damus_state.profiles)
|
||||||
|
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
let profile = damus_state.profiles.lookup(id: target.pubkey)
|
let profile = damus_state.profiles.lookup(id: target.pubkey)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import Kingfisher
|
||||||
|
|
||||||
let PFP_SIZE: CGFloat = 52.0
|
let PFP_SIZE: CGFloat = 52.0
|
||||||
|
|
||||||
@@ -35,11 +36,9 @@ struct ProfilePicView: View {
|
|||||||
let pubkey: String
|
let pubkey: String
|
||||||
let size: CGFloat
|
let size: CGFloat
|
||||||
let highlight: Highlight
|
let highlight: Highlight
|
||||||
let image_cache: ImageCache
|
|
||||||
let profiles: Profiles
|
let profiles: Profiles
|
||||||
|
|
||||||
@State var picture: String? = nil
|
@State var picture: String? = nil
|
||||||
@State var img: Image? = nil
|
|
||||||
|
|
||||||
var PlaceholderColor: Color {
|
var PlaceholderColor: Color {
|
||||||
return id_to_color(pubkey)
|
return id_to_color(pubkey)
|
||||||
@@ -55,34 +54,24 @@ struct ProfilePicView: View {
|
|||||||
|
|
||||||
var MainContent: some View {
|
var MainContent: some View {
|
||||||
Group {
|
Group {
|
||||||
if let img = self.img {
|
let pic = picture ?? profiles.lookup(id: pubkey)?.picture ?? robohash(pubkey)
|
||||||
img
|
let url = URL(string: pic)
|
||||||
.resizable()
|
let processor = /*DownsamplingImageProcessor(size: CGSize(width: size, height: size))
|
||||||
.frame(width: size, height: size)
|
|>*/ ResizingImageProcessor(referenceSize: CGSize(width: size, height: size))
|
||||||
.clipShape(Circle())
|
|> RoundCornerImageProcessor(cornerRadius: 20)
|
||||||
.overlay(Circle().stroke(highlight_color(highlight), lineWidth: pfp_line_width(highlight)))
|
KFImage.url(url)
|
||||||
.padding(2)
|
.placeholder { _ in
|
||||||
} else {
|
Placeholder
|
||||||
Placeholder
|
}
|
||||||
}
|
.setProcessor(processor)
|
||||||
|
.scaleFactor(UIScreen.main.scale)
|
||||||
|
.loadDiskFileSynchronously()
|
||||||
|
.fade(duration: 0.1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
MainContent
|
MainContent
|
||||||
.task {
|
|
||||||
let pic = picture ?? profiles.lookup(id: pubkey)?.picture ?? robohash(pubkey)
|
|
||||||
guard let url = URL(string: pic) else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let pfp_key = pfp_cache_key(url: url)
|
|
||||||
let ui_img = await image_cache.lookup_or_load_image(key: pfp_key, url: url)
|
|
||||||
|
|
||||||
if let ui_img = ui_img {
|
|
||||||
self.img = Image(uiImage: ui_img)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onReceive(handle_notify(.profile_updated)) { notif in
|
.onReceive(handle_notify(.profile_updated)) { notif in
|
||||||
let updated = notif.object as! ProfileUpdate
|
let updated = notif.object as! ProfileUpdate
|
||||||
|
|
||||||
@@ -91,12 +80,7 @@ struct ProfilePicView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let pic = updated.profile.picture {
|
if let pic = updated.profile.picture {
|
||||||
if let url = URL(string: pic) {
|
self.picture = pic
|
||||||
let pfp_key = pfp_cache_key(url: url)
|
|
||||||
if let ui_img = image_cache.lookup_sync(key: pfp_key) {
|
|
||||||
self.img = Image(uiImage: ui_img)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,7 +103,6 @@ struct ProfilePicView_Previews: PreviewProvider {
|
|||||||
pubkey: pubkey,
|
pubkey: pubkey,
|
||||||
size: 100,
|
size: 100,
|
||||||
highlight: .none,
|
highlight: .none,
|
||||||
image_cache: ImageCache(),
|
|
||||||
profiles: make_preview_profiles(pubkey))
|
profiles: make_preview_profiles(pubkey))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,13 +13,7 @@ struct ProfilePictureSelector: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
let highlight: Highlight = .custom(Color.white, 2.0)
|
let highlight: Highlight = .custom(Color.white, 2.0)
|
||||||
ZStack {
|
ZStack {
|
||||||
/*
|
ProfilePicView(pubkey: pubkey, size: 80.0, highlight: highlight, profiles: Profiles())
|
||||||
Image(systemName: "camera")
|
|
||||||
.font(.title)
|
|
||||||
.foregroundColor(.white)
|
|
||||||
*/
|
|
||||||
|
|
||||||
ProfilePicView(pubkey: pubkey, size: 80.0, highlight: highlight, image_cache: ImageCache(), profiles: Profiles())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ struct ProfileView: View {
|
|||||||
let data = damus_state.profiles.lookup(id: profile.pubkey)
|
let data = damus_state.profiles.lookup(id: profile.pubkey)
|
||||||
|
|
||||||
HStack(alignment: .center) {
|
HStack(alignment: .center) {
|
||||||
ProfilePicView(pubkey: profile.pubkey, size: PFP_SIZE, highlight: .custom(Color.black, 2), image_cache: damus_state.image_cache, profiles: damus_state.profiles)
|
ProfilePicView(pubkey: profile.pubkey, size: PFP_SIZE, highlight: .custom(Color.black, 2), profiles: damus_state.profiles)
|
||||||
|
|
||||||
ProfileNameView(pubkey: profile.pubkey, profile: data, contacts: damus_state.contacts)
|
ProfileNameView(pubkey: profile.pubkey, profile: data, contacts: damus_state.contacts)
|
||||||
|
|
||||||
@@ -181,7 +181,7 @@ struct ProfileView_Previews: PreviewProvider {
|
|||||||
|
|
||||||
func test_damus_state() -> DamusState {
|
func test_damus_state() -> DamusState {
|
||||||
let pubkey = "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"
|
let pubkey = "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"
|
||||||
let damus = DamusState(pool: RelayPool(), keypair: Keypair(pubkey: pubkey, privkey: "privkey"), likes: EventCounter(our_pubkey: pubkey), boosts: EventCounter(our_pubkey: pubkey), contacts: Contacts(), tips: TipCounter(our_pubkey: pubkey), image_cache: ImageCache(), profiles: Profiles(), dms: DirectMessagesModel())
|
let damus = DamusState(pool: RelayPool(), keypair: Keypair(pubkey: pubkey, privkey: "privkey"), likes: EventCounter(our_pubkey: pubkey), boosts: EventCounter(our_pubkey: pubkey), contacts: Contacts(), tips: TipCounter(our_pubkey: pubkey), profiles: Profiles(), dms: DirectMessagesModel())
|
||||||
|
|
||||||
let prof = Profile(name: "damus", display_name: "Damus", about: "iOS app!", picture: "https://damus.io/img/logo.png")
|
let prof = Profile(name: "damus", display_name: "Damus", about: "iOS app!", picture: "https://damus.io/img/logo.png")
|
||||||
let tsprof = TimestampedProfile(profile: prof, timestamp: 0)
|
let tsprof = TimestampedProfile(profile: prof, timestamp: 0)
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ struct ReplyQuoteView: View {
|
|||||||
let privkey: String?
|
let privkey: String?
|
||||||
let quoter: NostrEvent
|
let quoter: NostrEvent
|
||||||
let event_id: String
|
let event_id: String
|
||||||
let image_cache: ImageCache
|
|
||||||
let profiles: Profiles
|
let profiles: Profiles
|
||||||
|
|
||||||
@EnvironmentObject var thread: ThreadModel
|
@EnvironmentObject var thread: ThreadModel
|
||||||
@@ -25,7 +24,7 @@ struct ReplyQuoteView: View {
|
|||||||
|
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
HStack(alignment: .top) {
|
HStack(alignment: .top) {
|
||||||
ProfilePicView(pubkey: event.pubkey, size: 16, highlight: .reply, image_cache: image_cache, profiles: profiles)
|
ProfilePicView(pubkey: event.pubkey, size: 16, highlight: .reply, profiles: profiles)
|
||||||
Text(Profile.displayName(profile: profiles.lookup(id: event.pubkey), pubkey: event.pubkey))
|
Text(Profile.displayName(profile: profiles.lookup(id: event.pubkey), pubkey: event.pubkey))
|
||||||
.foregroundColor(.accentColor)
|
.foregroundColor(.accentColor)
|
||||||
Text("\(format_relative_time(event.created_at))")
|
Text("\(format_relative_time(event.created_at))")
|
||||||
@@ -59,7 +58,7 @@ struct ReplyQuoteView_Previews: PreviewProvider {
|
|||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
let s = test_damus_state()
|
let s = test_damus_state()
|
||||||
let quoter = NostrEvent(content: "a\nb\nc", pubkey: "pubkey")
|
let quoter = NostrEvent(content: "a\nb\nc", pubkey: "pubkey")
|
||||||
ReplyQuoteView(privkey: s.keypair.privkey, quoter: quoter, event_id: "pubkey2", image_cache: s.image_cache, profiles: s.profiles)
|
ReplyQuoteView(privkey: s.keypair.privkey, quoter: quoter, event_id: "pubkey2", profiles: s.profiles)
|
||||||
.environmentObject(ThreadModel(event: quoter, damus_state: s))
|
.environmentObject(ThreadModel(event: quoter, damus_state: s))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user