commit a88324333b2a616c6d9838af67e054cb63de5421
parent ce989450f4fb5ab60b4f1aff56261a4b0eeed166
Author: William Casarin <jb55@jb55.com>
Date: Sat, 30 Apr 2022 10:37:29 -0700
profiles
Signed-off-by: William Casarin <jb55@jb55.com>
Diffstat:
12 files changed, 286 insertions(+), 65 deletions(-)
diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj
@@ -13,6 +13,9 @@
4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F92280F66F5000448DE /* ReplyMap.swift */; };
4C0A3F95280F6C78000448DE /* ReplyQuoteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F94280F6C78000448DE /* ReplyQuoteView.swift */; };
4C0A3F97280F8E02000448DE /* ThreadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F96280F8E02000448DE /* ThreadView.swift */; };
+ 4C3BEFD22819DB9B00B3DE84 /* ProfileModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFD12819DB9B00B3DE84 /* ProfileModel.swift */; };
+ 4C3BEFD42819DE8F00B3DE84 /* NostrKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFD32819DE8F00B3DE84 /* NostrKind.swift */; };
+ 4C3BEFD6281D995700B3DE84 /* ActionBarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFD5281D995700B3DE84 /* ActionBarModel.swift */; };
4C75EFA427FA577B0006080F /* PostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFA327FA577B0006080F /* PostView.swift */; };
4C75EFA627FF87A20006080F /* Nostr.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFA527FF87A20006080F /* Nostr.swift */; };
4C75EFAD28049CFB0006080F /* PostButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFAC28049CFB0006080F /* PostButton.swift */; };
@@ -71,6 +74,9 @@
4C0A3F92280F66F5000448DE /* ReplyMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyMap.swift; sourceTree = "<group>"; };
4C0A3F94280F6C78000448DE /* ReplyQuoteView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyQuoteView.swift; sourceTree = "<group>"; };
4C0A3F96280F8E02000448DE /* ThreadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadView.swift; sourceTree = "<group>"; };
+ 4C3BEFD12819DB9B00B3DE84 /* ProfileModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileModel.swift; sourceTree = "<group>"; };
+ 4C3BEFD32819DE8F00B3DE84 /* NostrKind.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NostrKind.swift; sourceTree = "<group>"; };
+ 4C3BEFD5281D995700B3DE84 /* ActionBarModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionBarModel.swift; sourceTree = "<group>"; };
4C75EFA327FA577B0006080F /* PostView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostView.swift; sourceTree = "<group>"; };
4C75EFA527FF87A20006080F /* Nostr.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Nostr.swift; sourceTree = "<group>"; };
4C75EFA72804823E0006080F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
@@ -140,6 +146,8 @@
children = (
4C0A3F8E280F640A000448DE /* ThreadModel.swift */,
4C0A3F92280F66F5000448DE /* ReplyMap.swift */,
+ 4C3BEFD12819DB9B00B3DE84 /* ProfileModel.swift */,
+ 4C3BEFD5281D995700B3DE84 /* ActionBarModel.swift */,
);
path = Models;
sourceTree = "<group>";
@@ -179,6 +187,7 @@
4C75EFBA2804A34C0006080F /* ProofOfWork.swift */,
4CEE2AEC2805B22500AB5EEF /* NostrRequest.swift */,
4CACA9DB280C38C000D9BBE8 /* Profiles.swift */,
+ 4C3BEFD32819DE8F00B3DE84 /* NostrKind.swift */,
);
path = Nostr;
sourceTree = "<group>";
@@ -407,13 +416,16 @@
4CEE2AF7280B2DEA00AB5EEF /* ProfileName.swift in Sources */,
4CEE2B02280B39E800AB5EEF /* EventActionBar.swift in Sources */,
4C0A3F8F280F640A000448DE /* ThreadModel.swift in Sources */,
+ 4C3BEFD22819DB9B00B3DE84 /* ProfileModel.swift in Sources */,
4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */,
4CACA9DC280C38C000D9BBE8 /* Profiles.swift in Sources */,
4C75EFAF28049D350006080F /* NostrFilter.swift in Sources */,
4C8682872814DE470026224F /* ProfileView.swift in Sources */,
4CE6DF1627F8DEBF00C66700 /* RelayConnection.swift in Sources */,
+ 4C3BEFD6281D995700B3DE84 /* ActionBarModel.swift in Sources */,
4CEE2AF3280B25C500AB5EEF /* ProfilePicView.swift in Sources */,
4CEE2AF9280B2EAC00AB5EEF /* PowView.swift in Sources */,
+ 4C3BEFD42819DE8F00B3DE84 /* NostrKind.swift in Sources */,
4CE6DEE727F7A08100C66700 /* damusApp.swift in Sources */,
4CEE2AED2805B22500AB5EEF /* NostrRequest.swift in Sources */,
4C0A3F95280F6C78000448DE /* ReplyQuoteView.swift in Sources */,
@@ -587,6 +599,7 @@
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = damus/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = Damus;
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
@@ -621,6 +634,7 @@
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = damus/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = Damus;
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
diff --git a/damus/Assets.xcassets/AppIcon.appiconset/Contents.json b/damus/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -1,98 +1,116 @@
{
"images" : [
- {
- "idiom" : "iphone",
- "scale" : "2x",
- "size" : "20x20"
+ {
+ "size" : "20x20",
+ "idiom": "iphone",
+ "filename" : "damus2-20@2x.png",
+ "scale": "2x"
},
- {
- "idiom" : "iphone",
- "scale" : "3x",
- "size" : "20x20"
+ {
+ "size" : "20x20",
+ "idiom": "iphone",
+ "filename" : "damus2-20@3x.png",
+ "scale": "3x"
},
- {
- "idiom" : "iphone",
- "scale" : "2x",
- "size" : "29x29"
+ {
+ "size" : "20x20",
+ "idiom": "ipad",
+ "filename" : "damus2-20.png",
+ "scale": "1x"
},
{
- "idiom" : "iphone",
- "scale" : "3x",
- "size" : "29x29"
+ "size" : "20x20",
+ "idiom": "ipad",
+ "filename" : "damus2-20@2x.png",
+ "scale": "2x"
},
{
+ "size" : "29x29",
"idiom" : "iphone",
- "scale" : "2x",
- "size" : "40x40"
+ "filename" : "damus2-29@2x.png",
+ "scale" : "2x"
},
{
+ "size" : "29x29",
"idiom" : "iphone",
- "scale" : "3x",
- "size" : "40x40"
+ "filename" : "damus2-29@3x.png",
+ "scale" : "3x"
},
{
+ "size" : "40x40",
"idiom" : "iphone",
- "scale" : "2x",
- "size" : "60x60"
+ "filename" : "damus2-40@2x.png",
+ "scale" : "2x"
},
{
+ "size" : "40x40",
"idiom" : "iphone",
- "scale" : "3x",
- "size" : "60x60"
+ "filename" : "damus2-40@3x.png",
+ "scale" : "3x"
},
{
- "idiom" : "ipad",
- "scale" : "1x",
- "size" : "20x20"
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "damus2-60@2x.png",
+ "scale" : "2x"
},
{
- "idiom" : "ipad",
- "scale" : "2x",
- "size" : "20x20"
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "damus2-60@3x.png",
+ "scale" : "3x"
},
{
+ "size" : "29x29",
"idiom" : "ipad",
- "scale" : "1x",
- "size" : "29x29"
+ "filename" : "damus2-29.png",
+ "scale" : "1x"
},
{
+ "size" : "29x29",
"idiom" : "ipad",
- "scale" : "2x",
- "size" : "29x29"
+ "filename" : "damus2-29@2x.png",
+ "scale" : "2x"
},
{
+ "size" : "40x40",
"idiom" : "ipad",
- "scale" : "1x",
- "size" : "40x40"
+ "filename" : "damus2-40.png",
+ "scale" : "1x"
},
{
+ "size" : "40x40",
"idiom" : "ipad",
- "scale" : "2x",
- "size" : "40x40"
+ "filename" : "damus2-40@2x.png",
+ "scale" : "2x"
},
{
+ "size" : "76x76",
"idiom" : "ipad",
- "scale" : "1x",
- "size" : "76x76"
+ "filename" : "damus2-76.png",
+ "scale" : "1x"
},
{
+ "size" : "76x76",
"idiom" : "ipad",
- "scale" : "2x",
- "size" : "76x76"
+ "filename" : "damus2-76@2x.png",
+ "scale" : "2x"
},
{
+ "size" : "83.5x83.5",
"idiom" : "ipad",
- "scale" : "2x",
- "size" : "83.5x83.5"
+ "filename" : "damus2-83.5@2x.png",
+ "scale" : "2x"
},
{
+ "size" : "1024x1024",
"idiom" : "ios-marketing",
- "scale" : "1x",
- "size" : "1024x1024"
+ "filename" : "damus2-1024.png",
+ "scale" : "1x"
}
],
"info" : {
- "author" : "xcode",
- "version" : 1
+ "version" : 1,
+ "author" : "xcode"
}
}
diff --git a/damus/ContentView.swift b/damus/ContentView.swift
@@ -44,7 +44,7 @@ struct ContentView: View {
@State var status: String = "Not connected"
@State var active_sheet: Sheets? = nil
@State var profiles: Profiles = Profiles()
- @State var active_profile: String? = nil
+ @State var active_profile: ProfileModel = ProfileModel()
@State var friends: [String: ()] = [:]
@State var loading: Bool = true
@State var pool: RelayPool? = nil
@@ -153,7 +153,7 @@ struct ContentView: View {
case .home:
PostingTimelineView
.onAppear() {
- switch_timeline(.home)
+ //switch_timeline(.home)
}
case .notifications:
@@ -175,7 +175,9 @@ struct ContentView: View {
.environmentObject(profiles)
.padding([.leading, .trailing], 6)
- let pv = ProfileView()
+ let pv = ProfileView(pool: pool)
+ .environmentObject(active_profile)
+ .environmentObject(profiles)
NavigationLink(destination: tv, isActive: $is_thread_open) {
EmptyView()
@@ -235,7 +237,7 @@ struct ContentView: View {
}
.onReceive(handle_notify(.click_profile_pic)) { obj in
let pubkey = obj.object as! String
- self.active_profile = pubkey
+ self.active_profile.set_pubkey(pubkey)
self.is_profile_open = true
}
.onReceive(handle_notify(.post)) { obj in
@@ -317,6 +319,7 @@ struct ContentView: View {
self.pool = pool
self.thread.pool = pool
+ self.active_profile.pool = pool
pool.connect()
}
diff --git a/damus/Models/ActionBarModel.swift b/damus/Models/ActionBarModel.swift
@@ -0,0 +1,22 @@
+//
+// ActionBarModel.swift
+// damus
+//
+// Created by William Casarin on 2022-04-30.
+//
+
+import Foundation
+
+
+class ActionBarModel: ObservableObject {
+ @Published var our_like_event: NostrEvent? = nil
+ @Published var our_boost_event: NostrEvent? = nil
+
+ var liked: Bool {
+ return our_like_event != nil
+ }
+
+ var boosted: Bool {
+ return our_boost_event != nil
+ }
+}
diff --git a/damus/Models/ProfileModel.swift b/damus/Models/ProfileModel.swift
@@ -0,0 +1,87 @@
+//
+// ProfileModel.swift
+// damus
+//
+// Created by William Casarin on 2022-04-27.
+//
+
+import Foundation
+
+class ProfileModel: ObservableObject {
+ @Published var events: [NostrEvent] = []
+ @Published var pubkey: String?
+ var seen_event: Set<String> = Set()
+
+ var sub_id = UUID().description
+
+ var pool: RelayPool? = nil
+
+ deinit {
+ unsubscribe()
+ }
+
+ func unsubscribe() {
+ print("unsubscribing from profile \(pubkey ?? "?") with sub_id \(sub_id)")
+ pool?.unsubscribe(sub_id: sub_id)
+ }
+
+ func set_pubkey(_ pk: String) {
+ if pk == self.pubkey {
+ return
+ }
+
+ self.events.removeAll()
+ self.seen_event.removeAll()
+
+ unsubscribe()
+ self.sub_id = UUID().description
+ self.pubkey = pk
+ subscribe()
+ }
+
+ func subscribe() {
+ guard let pubkey = self.pubkey else {
+ return
+ }
+
+ let kinds: [Int] = [
+ NostrKind.text.rawValue,
+ NostrKind.delete.rawValue,
+ NostrKind.boost.rawValue
+ ]
+
+ var filter = NostrFilter.filter_kinds(kinds)
+ filter.authors = [pubkey]
+
+ print("subscribing to profile \(pubkey) with sub_id \(sub_id)")
+ pool?.subscribe(sub_id: sub_id, filters: [filter], handler: handle_event)
+ }
+
+ func add_event(_ ev: NostrEvent) {
+ if seen_event.contains(ev.id) {
+ return
+ }
+ if ev.kind == 1 {
+ self.events.append(ev)
+ self.events = self.events.sorted { $0.created_at > $1.created_at }
+ }
+ seen_event.insert(ev.id)
+ }
+
+ private func handle_event(relay_id: String, ev: NostrConnectionEvent) {
+ switch ev {
+ case .ws_event:
+ return
+ case .nostr_event(let resp):
+ switch resp {
+ case .event(let sid, let ev):
+ if sid != self.sub_id {
+ return
+ }
+ add_event(ev)
+ case .notice(let notice):
+ notify(.notice, notice)
+ }
+ }
+ }
+}
diff --git a/damus/Nostr/Nostr.swift b/damus/Nostr/Nostr.swift
@@ -18,11 +18,6 @@ struct Profile: Decodable {
}
}
-enum NostrKind: Int {
- case metadata = 0
- case text = 1
-}
-
enum NostrTag {
case other_event(OtherEvent)
case key_event(KeyEvent)
diff --git a/damus/Nostr/NostrEvent.swift b/damus/Nostr/NostrEvent.swift
@@ -53,6 +53,10 @@ class NostrEvent: Codable, Identifiable, CustomStringConvertible {
let p = pow.map { String($0) } ?? "?"
return "NostrEvent { id: \(id) pubkey \(pubkey) kind \(kind) tags \(tags) pow \(p) content '\(content)' }"
}
+
+ var known_kind: NostrKind? {
+ return NostrKind.init(rawValue: kind)
+ }
private enum CodingKeys: String, CodingKey {
case id, sig, tags, pubkey, created_at, kind, content
diff --git a/damus/Nostr/NostrKind.swift b/damus/Nostr/NostrKind.swift
@@ -0,0 +1,18 @@
+//
+// NostrKind.swift
+// damus
+//
+// Created by William Casarin on 2022-04-27.
+//
+
+import Foundation
+
+
+enum NostrKind: Int {
+ case metadata = 0
+ case text = 1
+ case contacts = 3
+ case delete = 5
+ case boost = 6
+ case like = 7
+}
diff --git a/damus/Nostr/RelayPool.swift b/damus/Nostr/RelayPool.swift
@@ -90,7 +90,17 @@ class RelayPool {
relay.connection.disconnect()
}
}
-
+
+ func unsubscribe(sub_id: String) {
+ self.remove_handler(sub_id: sub_id)
+ self.send(.unsubscribe(sub_id))
+ }
+
+ func subscribe(sub_id: String, filters: [NostrFilter], handler: @escaping (String, NostrConnectionEvent) -> ()) {
+ register_handler(sub_id: sub_id, handler: handler)
+ send(.subscribe(.init(filters: filters, sub_id: sub_id)))
+ }
+
func send(_ req: NostrRequest, to: [String]? = nil) {
let relays = to.map{ get_relays($0) } ?? self.relays
diff --git a/damus/Views/ChatView.swift b/damus/Views/ChatView.swift
@@ -107,14 +107,14 @@ struct ChatView: View {
}
.frame(maxWidth: 32)
//}
-
+
Group {
VStack(alignment: .leading) {
if just_started {
HStack {
ProfileName(pubkey: event.pubkey, profile: profile)
- .foregroundColor(id_to_color(event.pubkey))
- //.shadow(color: Color.secondary, radius: 2, x: 2, y: 2)
+ .foregroundColor(colorScheme == .dark ? id_to_color(event.pubkey) : Color.black)
+ //.shadow(color: Color.black, radius: 2)
Text("\(format_relative_time(event.created_at))")
.foregroundColor(.gray)
}
diff --git a/damus/Views/ProfileName.swift b/damus/Views/ProfileName.swift
@@ -9,7 +9,7 @@ import SwiftUI
func ProfileName(pubkey: String, profile: Profile?) -> some View {
Text(String(Profile.displayName(profile: profile, pubkey: pubkey)))
- .foregroundColor(hex_to_rgb(pubkey))
+ //.foregroundColor(hex_to_rgb(pubkey))
.bold()
}
diff --git a/damus/Views/ProfileView.swift b/damus/Views/ProfileView.swift
@@ -7,20 +7,70 @@
import SwiftUI
+enum ProfileTab: Hashable {
+ case posts
+ case following
+}
+
struct ProfileView: View {
- let profile: Profile? = nil
+ let pool: RelayPool
+ @State private var selected_tab: ProfileTab = .posts
+
+ @EnvironmentObject var profile: ProfileModel
+ @EnvironmentObject var profiles: Profiles
+
+ var TopSection: some View {
+ HStack(alignment: .top) {
+ let data = profile.pubkey.flatMap { profiles.lookup(id: $0) }
+ ProfilePicView(picture: data?.picture, size: 64, highlight: .custom(Color.black, 4))
+ //.border(Color.blue)
+ VStack(alignment: .leading) {
+ if let pubkey = profile.pubkey {
+ ProfileName(pubkey: pubkey, profile: data)
+ .font(.title)
+ //.border(Color.green)
+ }
+ Text(data?.about ?? "")
+ //.border(Color.red)
+ }
+ //.border(Color.purple)
+ //Spacer()
+ }
+ //.border(Color.indigo)
+ }
var body: some View {
- VStack {
- ProfilePicView(picture: profile?.picture, size: 64, highlight: .custom(Color.black, 4))
- //ProfileName(pubkey: <#T##String#>, profile: <#T##Profile?#>)
+ VStack(alignment: .leading) {
+ TopSection
+ Picker("", selection: $selected_tab) {
+ Text("Posts").tag(ProfileTab.posts)
+ Text("Following").tag(ProfileTab.following)
+ }
+ .pickerStyle(SegmentedPickerStyle())
+
+ Divider()
+
+ Group {
+ switch(selected_tab) {
+ case .posts:
+ TimelineView(events: $profile.events, pool: pool)
+ .environmentObject(profiles)
+ case .following:
+ Text("Following")
+ }
+ }
+ .frame(maxHeight: .infinity, alignment: .topLeading)
}
+ //.border(Color.white)
+ .frame(maxWidth: .infinity, alignment: .topLeading)
.navigationBarTitle("Profile")
}
}
+/*
struct ProfileView_Previews: PreviewProvider {
static var previews: some View {
ProfileView()
}
}
+ */