@@ -23,6 +23,7 @@
|
|||||||
4C75EFB728049D990006080F /* RelayPool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFB628049D990006080F /* RelayPool.swift */; };
|
4C75EFB728049D990006080F /* RelayPool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFB628049D990006080F /* RelayPool.swift */; };
|
||||||
4C75EFB92804A2740006080F /* EventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFB82804A2740006080F /* EventView.swift */; };
|
4C75EFB92804A2740006080F /* EventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFB82804A2740006080F /* EventView.swift */; };
|
||||||
4C75EFBB2804A34C0006080F /* ProofOfWork.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFBA2804A34C0006080F /* ProofOfWork.swift */; };
|
4C75EFBB2804A34C0006080F /* ProofOfWork.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFBA2804A34C0006080F /* ProofOfWork.swift */; };
|
||||||
|
4C8682872814DE470026224F /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C8682862814DE470026224F /* ProfileView.swift */; };
|
||||||
4CA2EFA0280E37AC0044ACD8 /* TimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */; };
|
4CA2EFA0280E37AC0044ACD8 /* TimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */; };
|
||||||
4CACA9D5280C31E100D9BBE8 /* ReplyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */; };
|
4CACA9D5280C31E100D9BBE8 /* ReplyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */; };
|
||||||
4CACA9DC280C38C000D9BBE8 /* Profiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CACA9DB280C38C000D9BBE8 /* Profiles.swift */; };
|
4CACA9DC280C38C000D9BBE8 /* Profiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CACA9DB280C38C000D9BBE8 /* Profiles.swift */; };
|
||||||
@@ -81,6 +82,7 @@
|
|||||||
4C75EFB628049D990006080F /* RelayPool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayPool.swift; sourceTree = "<group>"; };
|
4C75EFB628049D990006080F /* RelayPool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayPool.swift; sourceTree = "<group>"; };
|
||||||
4C75EFB82804A2740006080F /* EventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventView.swift; sourceTree = "<group>"; };
|
4C75EFB82804A2740006080F /* EventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventView.swift; sourceTree = "<group>"; };
|
||||||
4C75EFBA2804A34C0006080F /* ProofOfWork.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProofOfWork.swift; sourceTree = "<group>"; };
|
4C75EFBA2804A34C0006080F /* ProofOfWork.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProofOfWork.swift; sourceTree = "<group>"; };
|
||||||
|
4C8682862814DE470026224F /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = "<group>"; };
|
||||||
4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineView.swift; sourceTree = "<group>"; };
|
4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineView.swift; sourceTree = "<group>"; };
|
||||||
4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyView.swift; sourceTree = "<group>"; };
|
4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyView.swift; sourceTree = "<group>"; };
|
||||||
4CACA9DB280C38C000D9BBE8 /* Profiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Profiles.swift; sourceTree = "<group>"; };
|
4CACA9DB280C38C000D9BBE8 /* Profiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Profiles.swift; sourceTree = "<group>"; };
|
||||||
@@ -159,6 +161,7 @@
|
|||||||
4C0A3F90280F6528000448DE /* ChatView.swift */,
|
4C0A3F90280F6528000448DE /* ChatView.swift */,
|
||||||
4C0A3F94280F6C78000448DE /* ReplyQuoteView.swift */,
|
4C0A3F94280F6C78000448DE /* ReplyQuoteView.swift */,
|
||||||
4C0A3F96280F8E02000448DE /* ThreadView.swift */,
|
4C0A3F96280F8E02000448DE /* ThreadView.swift */,
|
||||||
|
4C8682862814DE470026224F /* ProfileView.swift */,
|
||||||
);
|
);
|
||||||
path = Views;
|
path = Views;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -407,6 +410,7 @@
|
|||||||
4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */,
|
4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */,
|
||||||
4CACA9DC280C38C000D9BBE8 /* Profiles.swift in Sources */,
|
4CACA9DC280C38C000D9BBE8 /* Profiles.swift in Sources */,
|
||||||
4C75EFAF28049D350006080F /* NostrFilter.swift in Sources */,
|
4C75EFAF28049D350006080F /* NostrFilter.swift in Sources */,
|
||||||
|
4C8682872814DE470026224F /* ProfileView.swift in Sources */,
|
||||||
4CE6DF1627F8DEBF00C66700 /* RelayConnection.swift in Sources */,
|
4CE6DF1627F8DEBF00C66700 /* RelayConnection.swift in Sources */,
|
||||||
4CEE2AF3280B25C500AB5EEF /* ProfilePicView.swift in Sources */,
|
4CEE2AF3280B25C500AB5EEF /* ProfilePicView.swift in Sources */,
|
||||||
4CEE2AF9280B2EAC00AB5EEF /* PowView.swift in Sources */,
|
4CEE2AF9280B2EAC00AB5EEF /* PowView.swift in Sources */,
|
||||||
|
|||||||
@@ -15,11 +15,12 @@ struct TimestampedProfile {
|
|||||||
|
|
||||||
enum Sheets: Identifiable {
|
enum Sheets: Identifiable {
|
||||||
case post
|
case post
|
||||||
|
case reply(NostrEvent)
|
||||||
|
|
||||||
var id: String {
|
var id: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .post:
|
case .post: return "post"
|
||||||
return "post"
|
case .reply(let ev): return "reply-" + ev.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,12 +44,14 @@ struct ContentView: View {
|
|||||||
@State var status: String = "Not connected"
|
@State var status: String = "Not connected"
|
||||||
@State var active_sheet: Sheets? = nil
|
@State var active_sheet: Sheets? = nil
|
||||||
@State var profiles: Profiles = Profiles()
|
@State var profiles: Profiles = Profiles()
|
||||||
|
@State var active_profile: String? = nil
|
||||||
@State var friends: [String: ()] = [:]
|
@State var friends: [String: ()] = [:]
|
||||||
@State var loading: Bool = true
|
@State var loading: Bool = true
|
||||||
@State var pool: RelayPool? = nil
|
@State var pool: RelayPool? = nil
|
||||||
@State var selected_timeline: Timeline? = .home
|
@State var selected_timeline: Timeline? = .home
|
||||||
@StateObject var thread: ThreadModel = ThreadModel()
|
@StateObject var thread: ThreadModel = ThreadModel()
|
||||||
@State var is_thread_open: Bool = false
|
@State var is_thread_open: Bool = false
|
||||||
|
@State var is_profile_open: Bool = false
|
||||||
@State var last_event_of_kind: [String: [Int: NostrEvent]] = [:]
|
@State var last_event_of_kind: [String: [Int: NostrEvent]] = [:]
|
||||||
@State var has_events: [String: ()] = [:]
|
@State var has_events: [String: ()] = [:]
|
||||||
@State var has_friend_event: [String: ()] = [:]
|
@State var has_friend_event: [String: ()] = [:]
|
||||||
@@ -172,9 +175,15 @@ struct ContentView: View {
|
|||||||
.environmentObject(profiles)
|
.environmentObject(profiles)
|
||||||
.padding([.leading, .trailing], 6)
|
.padding([.leading, .trailing], 6)
|
||||||
|
|
||||||
|
let pv = ProfileView()
|
||||||
|
|
||||||
NavigationLink(destination: tv, isActive: $is_thread_open) {
|
NavigationLink(destination: tv, isActive: $is_thread_open) {
|
||||||
EmptyView()
|
EmptyView()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NavigationLink(destination: pv, isActive: $is_profile_open) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.navigationBarTitle("Damus", displayMode: .inline)
|
.navigationBarTitle("Damus", displayMode: .inline)
|
||||||
}
|
}
|
||||||
@@ -200,19 +209,36 @@ struct ContentView: View {
|
|||||||
switch item {
|
switch item {
|
||||||
case .post:
|
case .post:
|
||||||
PostView(references: [])
|
PostView(references: [])
|
||||||
|
case .reply(let event):
|
||||||
|
ReplyView(replying_to: event)
|
||||||
|
.environmentObject(profiles)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onReceive(NotificationCenter.default.publisher(for: .open_thread)) { obj in
|
.onReceive(handle_notify(.boost)) { notif in
|
||||||
|
let ev = notif.object as! NostrEvent
|
||||||
|
let boost = make_boost_event(ev, privkey: privkey, pubkey: pubkey)
|
||||||
|
self.pool?.send(.event(boost))
|
||||||
|
}
|
||||||
|
.onReceive(handle_notify(.open_thread)) { obj in
|
||||||
let ev = obj.object as! NostrEvent
|
let ev = obj.object as! NostrEvent
|
||||||
thread.reset_events()
|
thread.reset_events()
|
||||||
thread.set_active_event(ev)
|
thread.set_active_event(ev)
|
||||||
is_thread_open = true
|
is_thread_open = true
|
||||||
}
|
}
|
||||||
.onReceive(NotificationCenter.default.publisher(for: .broadcast_event)) { obj in
|
.onReceive(handle_notify(.reply)) { notif in
|
||||||
|
let ev = notif.object as! NostrEvent
|
||||||
|
self.active_sheet = .reply(ev)
|
||||||
|
}
|
||||||
|
.onReceive(handle_notify(.broadcast_event)) { obj in
|
||||||
let ev = obj.object as! NostrEvent
|
let ev = obj.object as! NostrEvent
|
||||||
self.pool?.send(.event(ev))
|
self.pool?.send(.event(ev))
|
||||||
}
|
}
|
||||||
.onReceive(NotificationCenter.default.publisher(for: .post)) { obj in
|
.onReceive(handle_notify(.click_profile_pic)) { obj in
|
||||||
|
let pubkey = obj.object as! String
|
||||||
|
self.active_profile = pubkey
|
||||||
|
self.is_profile_open = true
|
||||||
|
}
|
||||||
|
.onReceive(handle_notify(.post)) { obj in
|
||||||
let post_res = obj.object as! NostrPostResult
|
let post_res = obj.object as! NostrPostResult
|
||||||
switch post_res {
|
switch post_res {
|
||||||
case .post(let post):
|
case .post(let post):
|
||||||
@@ -583,3 +609,11 @@ func save_last_notified(_ ev: NostrEvent) {
|
|||||||
UserDefaults.standard.set(String(ev.created_at), forKey: "last_notification_time")
|
UserDefaults.standard.set(String(ev.created_at), forKey: "last_notification_time")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func make_boost_event(_ ev: NostrEvent, privkey: String, pubkey: String) -> NostrEvent {
|
||||||
|
let boost = NostrEvent(content: "", pubkey: pubkey, kind: 6, tags: [["e", ev.id]])
|
||||||
|
boost.calculate_id()
|
||||||
|
boost.sign(privkey: privkey)
|
||||||
|
return boost
|
||||||
|
}
|
||||||
|
|||||||
@@ -68,8 +68,9 @@ class ThreadModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func subscribe(_ ev: NostrEvent) {
|
private func subscribe(_ ev: NostrEvent) {
|
||||||
var ref_events = NostrFilter.filter_text
|
let kinds: [Int] = [1, 5, 6]
|
||||||
var events_filter = NostrFilter.filter_text
|
var ref_events = NostrFilter.filter_kinds(kinds)
|
||||||
|
var events_filter = NostrFilter.filter_kinds(kinds)
|
||||||
|
|
||||||
// TODO: add referenced relays
|
// TODO: add referenced relays
|
||||||
ref_events.referenced_ids = ev.referenced_ids.map { $0.ref_id }
|
ref_events.referenced_ids = ev.referenced_ids.map { $0.ref_id }
|
||||||
|
|||||||
@@ -217,8 +217,6 @@ class NostrEvent: Codable, Identifiable, CustomStringConvertible {
|
|||||||
self.kind = kind
|
self.kind = kind
|
||||||
self.tags = tags
|
self.tags = tags
|
||||||
self.created_at = Int64(Date().timeIntervalSince1970)
|
self.created_at = Int64(Date().timeIntervalSince1970)
|
||||||
|
|
||||||
self.calculate_id()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func calculate_id() {
|
func calculate_id() {
|
||||||
|
|||||||
@@ -25,12 +25,24 @@ extension Notification.Name {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Notification.Name {
|
||||||
|
static var reply: Notification.Name {
|
||||||
|
return Notification.Name("reply")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension Notification.Name {
|
extension Notification.Name {
|
||||||
static var switched_timeline: Notification.Name {
|
static var switched_timeline: Notification.Name {
|
||||||
return Notification.Name("switched_timeline")
|
return Notification.Name("switched_timeline")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Notification.Name {
|
||||||
|
static var click_profile_pic: Notification.Name {
|
||||||
|
return Notification.Name("click_profile_pic")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension Notification.Name {
|
extension Notification.Name {
|
||||||
static var scroll_to_top: Notification.Name {
|
static var scroll_to_top: Notification.Name {
|
||||||
return Notification.Name("scroll_to_to")
|
return Notification.Name("scroll_to_to")
|
||||||
@@ -54,3 +66,17 @@ extension Notification.Name {
|
|||||||
return Notification.Name("send post")
|
return Notification.Name("send post")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Notification.Name {
|
||||||
|
static var boost: Notification.Name {
|
||||||
|
return Notification.Name("boost post")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handle_notify(_ name: Notification.Name) -> NotificationCenter.Publisher {
|
||||||
|
return NotificationCenter.default.publisher(for: name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func notify(_ name: NSNotification.Name, _ object: Any?) {
|
||||||
|
NotificationCenter.default.post(name: name, object: object)
|
||||||
|
}
|
||||||
|
|||||||
@@ -113,6 +113,8 @@ struct ChatView: View {
|
|||||||
if just_started {
|
if just_started {
|
||||||
HStack {
|
HStack {
|
||||||
ProfileName(pubkey: event.pubkey, profile: profile)
|
ProfileName(pubkey: event.pubkey, profile: profile)
|
||||||
|
.foregroundColor(id_to_color(event.pubkey))
|
||||||
|
//.shadow(color: Color.secondary, radius: 2, x: 2, y: 2)
|
||||||
Text("\(format_relative_time(event.created_at))")
|
Text("\(format_relative_time(event.created_at))")
|
||||||
.foregroundColor(.gray)
|
.foregroundColor(.gray)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,27 +31,17 @@ struct EventActionBar: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
*/
|
|
||||||
|
|
||||||
|
*/
|
||||||
EventActionButton(img: "bubble.left") {
|
EventActionButton(img: "bubble.left") {
|
||||||
self.sheet = .reply
|
notify(.reply, event)
|
||||||
}
|
}
|
||||||
}
|
.padding([.trailing], 40)
|
||||||
.sheet(item: $sheet) { sheet in
|
|
||||||
switch sheet {
|
EventActionButton(img: "arrow.2.squarepath") {
|
||||||
case .reply:
|
notify(.boost, event)
|
||||||
ReplyView(replying_to: event)
|
|
||||||
.environmentObject(profiles)
|
|
||||||
.onReceive(NotificationCenter.default.publisher(for: .post)) { obj in
|
|
||||||
let res = obj.object as! NostrPostResult
|
|
||||||
switch res {
|
|
||||||
case .cancel:
|
|
||||||
self.sheet = nil
|
|
||||||
case .post:
|
|
||||||
self.sheet = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,3 +54,4 @@ func EventActionButton(img: String, action: @escaping () -> ()) -> some View {
|
|||||||
.foregroundColor(.gray)
|
.foregroundColor(.gray)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ struct EventDetailView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onAppear() {
|
.onAppear() {
|
||||||
if highlight == .main {
|
if highlight.is_main {
|
||||||
scroll_to_event(scroller: scroller, id: ev.id, delay: 0.5, animate: true)
|
scroll_to_event(scroller: scroller, id: ev.id, delay: 0.5, animate: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -270,6 +270,7 @@ func calculated_collapsed_events(collapsed: Bool, active: NostrEvent?, events: [
|
|||||||
}
|
}
|
||||||
count += 1
|
count += 1
|
||||||
case .main: fallthrough
|
case .main: fallthrough
|
||||||
|
case .custom: fallthrough
|
||||||
case .reply:
|
case .reply:
|
||||||
if count != 0 {
|
if count != 0 {
|
||||||
let c = CollapsedEvents(count: count, start: start, end: i)
|
let c = CollapsedEvents(count: count, start: start, end: i)
|
||||||
|
|||||||
@@ -12,12 +12,20 @@ enum Highlight {
|
|||||||
case none
|
case none
|
||||||
case main
|
case main
|
||||||
case reply
|
case reply
|
||||||
|
case custom(Color, Float)
|
||||||
|
|
||||||
|
var is_main: Bool {
|
||||||
|
if case .main = self {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
var is_none: Bool {
|
var is_none: Bool {
|
||||||
switch self {
|
if case .none = self {
|
||||||
case .none: return true
|
return true
|
||||||
default: return false
|
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var is_replied_to: Bool {
|
var is_replied_to: Bool {
|
||||||
@@ -40,6 +48,9 @@ struct EventView: View {
|
|||||||
HStack {
|
HStack {
|
||||||
VStack {
|
VStack {
|
||||||
ProfilePicView(picture: profile?.picture, size: PFP_SIZE!, highlight: highlight)
|
ProfilePicView(picture: profile?.picture, size: PFP_SIZE!, highlight: highlight)
|
||||||
|
.onTapGesture {
|
||||||
|
NotificationCenter.default.post(name: .click_profile_pic, object: event.pubkey)
|
||||||
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ struct PostView: View {
|
|||||||
func send_post() {
|
func send_post() {
|
||||||
let new_post = NostrPost(content: self.post, references: references)
|
let new_post = NostrPost(content: self.post, references: references)
|
||||||
NotificationCenter.default.post(name: .post, object: NostrPostResult.post(new_post))
|
NotificationCenter.default.post(name: .post, object: NostrPostResult.post(new_post))
|
||||||
//dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import SwiftUI
|
|||||||
|
|
||||||
func ProfileName(pubkey: String, profile: Profile?) -> some View {
|
func ProfileName(pubkey: String, profile: Profile?) -> some View {
|
||||||
Text(String(Profile.displayName(profile: profile, pubkey: pubkey)))
|
Text(String(Profile.displayName(profile: profile, pubkey: pubkey)))
|
||||||
|
.foregroundColor(hex_to_rgb(pubkey))
|
||||||
.bold()
|
.bold()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,23 +11,24 @@ let PFP_SIZE: CGFloat? = 52.0
|
|||||||
let CORNER_RADIUS: CGFloat = 32
|
let CORNER_RADIUS: CGFloat = 32
|
||||||
|
|
||||||
func id_to_color(_ id: String) -> Color {
|
func id_to_color(_ id: String) -> Color {
|
||||||
return .init(hex: String(id.reversed().prefix(6)))
|
return hex_to_rgb(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func highlight_color(_ h: Highlight) -> Color {
|
func highlight_color(_ h: Highlight) -> Color {
|
||||||
switch h {
|
switch h {
|
||||||
case .reply: fallthrough
|
|
||||||
case .none: return Color.black
|
|
||||||
case .main: return Color.red
|
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 {
|
func pfp_line_width(_ h: Highlight) -> CGFloat {
|
||||||
switch h {
|
switch h {
|
||||||
case .none: fallthrough
|
case .reply: return 0
|
||||||
case .reply:
|
case .none: return 0
|
||||||
return 0
|
|
||||||
case .main: return 2
|
case .main: return 2
|
||||||
|
case .custom(_, let lw): return CGFloat(lw)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,23 +41,29 @@ struct ProfilePicView: View {
|
|||||||
Color.purple.opacity(0.2)
|
Color.purple.opacity(0.2)
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var MainContent: some View {
|
||||||
if let pic = picture.flatMap({ URL(string: $0) }) {
|
Group {
|
||||||
AsyncImage(url: pic) { img in
|
if let pic = picture.flatMap({ URL(string: $0) }) {
|
||||||
img.resizable()
|
AsyncImage(url: pic) { img in
|
||||||
} placeholder: { Placeholder }
|
img.resizable()
|
||||||
.frame(width: size, height: size)
|
} placeholder: { Placeholder }
|
||||||
.clipShape(Circle())
|
|
||||||
.overlay(Circle().stroke(highlight_color(highlight), lineWidth: pfp_line_width(highlight)))
|
|
||||||
.padding(2)
|
|
||||||
} else {
|
|
||||||
Placeholder
|
|
||||||
.frame(width: size, height: size)
|
.frame(width: size, height: size)
|
||||||
.cornerRadius(CORNER_RADIUS)
|
.clipShape(Circle())
|
||||||
.overlay(Circle().stroke(highlight_color(highlight), lineWidth: pfp_line_width(highlight)))
|
.overlay(Circle().stroke(highlight_color(highlight), lineWidth: pfp_line_width(highlight)))
|
||||||
.padding(2)
|
.padding(2)
|
||||||
|
} else {
|
||||||
|
Placeholder
|
||||||
|
.frame(width: size, height: size)
|
||||||
|
.cornerRadius(CORNER_RADIUS)
|
||||||
|
.overlay(Circle().stroke(highlight_color(highlight), lineWidth: pfp_line_width(highlight)))
|
||||||
|
.padding(2)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
MainContent
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ProfilePicView_Previews: PreviewProvider {
|
struct ProfilePicView_Previews: PreviewProvider {
|
||||||
@@ -66,28 +73,36 @@ struct ProfilePicView_Previews: PreviewProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
extension Color {
|
func hex_to_rgb(_ hex: String) -> Color {
|
||||||
init(hex: String) {
|
guard hex.count >= 6 else {
|
||||||
var int: UInt64 = 0
|
return Color.black
|
||||||
Scanner(string: hex).scanHexInt64(&int)
|
}
|
||||||
let a, r, g, b: UInt64
|
|
||||||
switch hex.count {
|
let arr = Array(hex.utf8)
|
||||||
case 3: // RGB (12-bit)
|
var rgb: [UInt8] = []
|
||||||
(a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
|
var i: Int = 0
|
||||||
case 6: // RGB (24-bit)
|
|
||||||
(a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
|
while i < 6 {
|
||||||
case 8: // ARGB (32-bit)
|
let cs1 = arr[i]
|
||||||
(a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
|
let cs2 = arr[i+1]
|
||||||
default:
|
|
||||||
(a, r, g, b) = (1, 1, 1, 0)
|
guard let c1 = char_to_hex(cs1) else {
|
||||||
|
return Color.black
|
||||||
}
|
}
|
||||||
|
|
||||||
self.init(
|
guard let c2 = char_to_hex(cs2) else {
|
||||||
.sRGB,
|
return Color.black
|
||||||
red: Double(r) / 255,
|
}
|
||||||
green: Double(g) / 255,
|
|
||||||
blue: Double(b) / 255,
|
rgb.append((c1 << 4) | c2)
|
||||||
opacity: Double(a) / 255
|
i += 2
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Color.init(
|
||||||
|
.sRGB,
|
||||||
|
red: Double(rgb[0]) / 255,
|
||||||
|
green: Double(rgb[1]) / 255,
|
||||||
|
blue: Double(rgb[2]) / 255,
|
||||||
|
opacity: 1
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
26
damus/Views/ProfileView.swift
Normal file
26
damus/Views/ProfileView.swift
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
//
|
||||||
|
// ProfileView.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by William Casarin on 2022-04-23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct ProfileView: View {
|
||||||
|
let profile: Profile? = nil
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
ProfilePicView(picture: profile?.picture, size: 64, highlight: .custom(Color.black, 4))
|
||||||
|
//ProfileName(pubkey: <#T##String#>, profile: <#T##Profile?#>)
|
||||||
|
}
|
||||||
|
.navigationBarTitle("Profile")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ProfileView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
ProfileView()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,12 +18,13 @@ struct ReplyQuoteView: View {
|
|||||||
HStack(alignment: .top) {
|
HStack(alignment: .top) {
|
||||||
Rectangle().frame(width: 2)
|
Rectangle().frame(width: 2)
|
||||||
.padding([.leading], 4)
|
.padding([.leading], 4)
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
|
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
HStack(alignment: .top) {
|
HStack(alignment: .top) {
|
||||||
ProfilePicView(picture: profiles.lookup(id: event.pubkey)?.picture, size: 16, highlight: .none)
|
ProfilePicView(picture: profiles.lookup(id: event.pubkey)?.picture, size: 16, highlight: .reply)
|
||||||
ProfileName(pubkey: event.pubkey, profile: profiles.lookup(id: event.pubkey))
|
Text(Profile.displayName(profile: profiles.lookup(id: event.pubkey), pubkey: event.pubkey))
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
Text("\(format_relative_time(event.created_at))")
|
Text("\(format_relative_time(event.created_at))")
|
||||||
.foregroundColor(.gray)
|
.foregroundColor(.gray)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user