Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
William Casarin
2022-04-30 10:37:29 -07:00
parent ce989450f4
commit a88324333b
12 changed files with 289 additions and 68 deletions

View File

@@ -1,98 +1,116 @@
{
"images" : [
{
"size" : "20x20",
"idiom": "iphone",
"filename" : "damus2-20@2x.png",
"scale": "2x"
},
{
"size" : "20x20",
"idiom": "iphone",
"filename" : "damus2-20@3x.png",
"scale": "3x"
},
{
"size" : "20x20",
"idiom": "ipad",
"filename" : "damus2-20.png",
"scale": "1x"
},
{
"size" : "20x20",
"idiom": "ipad",
"filename" : "damus2-20@2x.png",
"scale": "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"scale" : "2x",
"size" : "20x20"
"filename" : "damus2-29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"scale" : "3x",
"size" : "20x20"
"filename" : "damus2-29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"scale" : "2x",
"size" : "29x29"
"filename" : "damus2-40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"scale" : "3x",
"size" : "29x29"
"filename" : "damus2-40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"scale" : "2x",
"size" : "40x40"
"filename" : "damus2-60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"scale" : "3x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
"filename" : "damus2-60@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"scale" : "1x",
"size" : "20x20"
"filename" : "damus2-29.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"scale" : "2x",
"size" : "20x20"
"filename" : "damus2-29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"scale" : "1x",
"size" : "29x29"
"filename" : "damus2-40.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"scale" : "2x",
"size" : "29x29"
"filename" : "damus2-40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"scale" : "1x",
"size" : "40x40"
"filename" : "damus2-76.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"scale" : "2x",
"size" : "40x40"
"filename" : "damus2-76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"scale" : "1x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "76x76"
},
{
"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"
}
}

View File

@@ -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()
}

View File

@@ -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
}
}

View File

@@ -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)
}
}
}
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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
}

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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()
}

View File

@@ -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()
}
}
*/