Merge branch 'master' into profile-markdown
This commit is contained in:
@@ -17,9 +17,31 @@ struct AddRelayView: View {
|
||||
VStack(alignment: .leading) {
|
||||
Form {
|
||||
Section("Add Relay") {
|
||||
TextField("wss://some.relay.com", text: $relay)
|
||||
.autocorrectionDisabled(true)
|
||||
.textInputAutocapitalization(.never)
|
||||
ZStack(alignment: .leading) {
|
||||
HStack{
|
||||
TextField("wss://some.relay.com", text: $relay)
|
||||
.padding(2)
|
||||
.padding(.leading, 25)
|
||||
.autocorrectionDisabled(true)
|
||||
.textInputAutocapitalization(.never)
|
||||
|
||||
Label("", systemImage: "xmark.circle.fill")
|
||||
.foregroundColor(.blue)
|
||||
.padding(.trailing, -25.0)
|
||||
.opacity((relay == "") ? 0.0 : 1.0)
|
||||
.onTapGesture {
|
||||
self.relay = ""
|
||||
}
|
||||
}
|
||||
|
||||
Label("", systemImage: "doc.on.clipboard")
|
||||
.padding(.leading, -10)
|
||||
.onTapGesture {
|
||||
if let pastedrelay = UIPasteboard.general.string {
|
||||
self.relay = pastedrelay
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ struct ChatView: View {
|
||||
}
|
||||
}
|
||||
|
||||
NoteContentView(privkey: damus_state.keypair.privkey, event: event, profiles: damus_state.profiles, show_images: should_show_images(contacts: damus_state.contacts, ev: event), artifacts: .just_content(event.content), size: .normal)
|
||||
NoteContentView(privkey: damus_state.keypair.privkey, event: event, profiles: damus_state.profiles, show_images: should_show_images(contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey), artifacts: .just_content(event.content), size: .normal)
|
||||
|
||||
if is_active || next_ev == nil || next_ev!.pubkey != event.pubkey {
|
||||
let bar = make_actionbar_model(ev: event, damus: damus_state)
|
||||
|
||||
@@ -102,12 +102,12 @@ struct ConfigView: View {
|
||||
.navigationTitle("Settings")
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
.alert("Logout", isPresented: $confirm_logout) {
|
||||
Button("Logout") {
|
||||
notify(.logout, ())
|
||||
}
|
||||
Button("Cancel") {
|
||||
confirm_logout = false
|
||||
}
|
||||
Button("Logout") {
|
||||
notify(.logout, ())
|
||||
}
|
||||
} message: {
|
||||
Text("Make sure your nsec account key is saved before you logout or you will lose access to this account")
|
||||
}
|
||||
|
||||
@@ -21,7 +21,9 @@ struct DMView: View {
|
||||
Spacer()
|
||||
}
|
||||
|
||||
NoteContentView(privkey: damus_state.keypair.privkey, event: event, profiles: damus_state.profiles, show_images: should_show_images(contacts: damus_state.contacts, ev: event), artifacts: .just_content(event.get_content(damus_state.keypair.privkey)), size: .normal)
|
||||
let should_show_img = should_show_images(contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey)
|
||||
|
||||
NoteContentView(privkey: damus_state.keypair.privkey, event: event, profiles: damus_state.profiles, show_images: should_show_img, artifacts: .just_content(event.get_content(damus_state.keypair.privkey)), size: .normal)
|
||||
.foregroundColor(is_ours ? Color.white : Color.primary)
|
||||
.padding(10)
|
||||
.background(is_ours ? Color.accentColor : Color.secondary.opacity(0.15))
|
||||
|
||||
@@ -247,7 +247,9 @@ struct EventView: View {
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
|
||||
NoteContentView(privkey: damus.keypair.privkey, event: event, profiles: damus.profiles, show_images: should_show_images(contacts: damus.contacts, ev: event), artifacts: .just_content(content), size: self.size)
|
||||
let should_show_img = should_show_images(contacts: damus.contacts, ev: event, our_pubkey: damus.pubkey)
|
||||
|
||||
NoteContentView(privkey: damus.keypair.privkey, event: event, profiles: damus.profiles, show_images: should_show_img, artifacts: .just_content(content), size: self.size)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.allowsHitTesting(!embedded)
|
||||
|
||||
@@ -309,7 +311,10 @@ struct EventView: View {
|
||||
}
|
||||
|
||||
// blame the porn bots for this code
|
||||
func should_show_images(contacts: Contacts, ev: NostrEvent) -> Bool {
|
||||
func should_show_images(contacts: Contacts, ev: NostrEvent, our_pubkey: String) -> Bool {
|
||||
if ev.pubkey == our_pubkey {
|
||||
return true
|
||||
}
|
||||
if contacts.is_in_friendosphere(ev.pubkey) {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ struct FollowUserView: View {
|
||||
static let markdown = Markdown()
|
||||
|
||||
var body: some View {
|
||||
HStack(alignment: .top) {
|
||||
HStack {
|
||||
let pmodel = ProfileModel(pubkey: target.pubkey, damus: damus_state)
|
||||
let followers = FollowersModel(damus_state: damus_state, target: target.pubkey)
|
||||
let pv = ProfileView(damus_state: damus_state, profile: pmodel, followers: followers)
|
||||
@@ -27,6 +27,8 @@ struct FollowUserView: View {
|
||||
ProfileName(pubkey: target.pubkey, profile: profile, contacts: damus_state.contacts, show_friend_confirmed: false)
|
||||
if let about = profile?.about {
|
||||
Text(FollowUserView.markdown.process(about))
|
||||
.lineLimit(3)
|
||||
.font(.footnote)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +55,7 @@ struct FollowersView: View {
|
||||
FollowUserView(target: .pubkey(pk), damus_state: damus_state)
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.navigationBarTitle("\(Profile.displayName(profile: profile, pubkey: whos))'s Followers")
|
||||
.onAppear {
|
||||
@@ -80,6 +83,7 @@ struct FollowingView: View {
|
||||
FollowUserView(target: .pubkey(pk), damus_state: damus_state)
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.onAppear {
|
||||
following.subscribe()
|
||||
|
||||
@@ -76,14 +76,11 @@ struct TabBar: View {
|
||||
VStack {
|
||||
Divider()
|
||||
HStack {
|
||||
TabButton(timeline: .home, img: "house", selected: $selected, new_events: $new_events, action: action)
|
||||
TabButton(timeline: .dms, img: "bubble.left.and.bubble.right", selected: $selected, new_events: $new_events, action: action)
|
||||
TabButton(timeline: .search, img: "magnifyingglass.circle", selected: $selected, new_events: $new_events, action: action)
|
||||
TabButton(timeline: .notifications, img: "bell", selected: $selected, new_events: $new_events, action: action)
|
||||
TabButton(timeline: .home, img: "house", selected: $selected, new_events: $new_events, action: action).keyboardShortcut("1")
|
||||
TabButton(timeline: .dms, img: "bubble.left.and.bubble.right", selected: $selected, new_events: $new_events, action: action).keyboardShortcut("2")
|
||||
TabButton(timeline: .search, img: "magnifyingglass.circle", selected: $selected, new_events: $new_events, action: action).keyboardShortcut("3")
|
||||
TabButton(timeline: .notifications, img: "bell", selected: $selected, new_events: $new_events, action: action).keyboardShortcut("4")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -6,14 +6,16 @@
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import LinkPresentation
|
||||
|
||||
struct NoteArtifacts {
|
||||
let content: String
|
||||
let images: [URL]
|
||||
let invoices: [Invoice]
|
||||
let links: [URL]
|
||||
|
||||
static func just_content(_ content: String) -> NoteArtifacts {
|
||||
NoteArtifacts(content: content, images: [], invoices: [])
|
||||
NoteArtifacts(content: content, images: [], invoices: [], links: [])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +23,7 @@ func render_note_content(ev: NostrEvent, profiles: Profiles, privkey: String?) -
|
||||
let blocks = ev.blocks(privkey)
|
||||
var invoices: [Invoice] = []
|
||||
var img_urls: [URL] = []
|
||||
var link_urls: [URL] = []
|
||||
let txt = blocks.reduce("") { str, block in
|
||||
switch block {
|
||||
case .mention(let m):
|
||||
@@ -33,14 +36,20 @@ func render_note_content(ev: NostrEvent, profiles: Profiles, privkey: String?) -
|
||||
invoices.append(invoice)
|
||||
return str
|
||||
case .url(let url):
|
||||
|
||||
// Handle Image URLs
|
||||
if is_image_url(url) {
|
||||
// Append Image
|
||||
img_urls.append(url)
|
||||
} else {
|
||||
link_urls.append(url)
|
||||
}
|
||||
return str + url.absoluteString
|
||||
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
||||
return NoteArtifacts(content: txt, images: img_urls, invoices: invoices)
|
||||
return NoteArtifacts(content: txt, images: img_urls, invoices: invoices, links: link_urls)
|
||||
}
|
||||
|
||||
func is_image_url(_ url: URL) -> Bool {
|
||||
@@ -57,6 +66,7 @@ struct NoteContentView: View {
|
||||
|
||||
@State var artifacts: NoteArtifacts
|
||||
|
||||
@State var metaData: LPLinkMetadata? = nil
|
||||
let size: EventViewKind
|
||||
|
||||
func MainContent() -> some View {
|
||||
@@ -66,16 +76,33 @@ struct NoteContentView: View {
|
||||
|
||||
if show_images && artifacts.images.count > 0 {
|
||||
ImageCarousel(urls: artifacts.images)
|
||||
} else if !show_images && artifacts.images.count > 0 {
|
||||
ImageCarousel(urls: artifacts.images)
|
||||
.blur(radius: 10)
|
||||
.overlay {
|
||||
Rectangle()
|
||||
.opacity(0.50)
|
||||
}
|
||||
.cornerRadius(10)
|
||||
}
|
||||
if artifacts.invoices.count > 0 {
|
||||
InvoicesView(invoices: artifacts.invoices)
|
||||
.frame(width: 200)
|
||||
}
|
||||
|
||||
if show_images, self.metaData != nil {
|
||||
LinkViewRepresentable(metadata: self.metaData)
|
||||
} else {
|
||||
ForEach(artifacts.links, id:\.self) { link in
|
||||
LinkViewRepresentable(url: link)
|
||||
.frame(height: 50)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
MainContent()
|
||||
.animation(.easeInOut, value: metaData)
|
||||
.onAppear() {
|
||||
self.artifacts = render_note_content(ev: event, profiles: profiles, privkey: privkey)
|
||||
}
|
||||
@@ -95,6 +122,28 @@ struct NoteContentView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.task {
|
||||
if show_images, artifacts.links.count == 1 {
|
||||
|
||||
self.metaData = await getMetaData(for: artifacts.links.first!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func getMetaData(for url: URL) async -> LPLinkMetadata? {
|
||||
// iOS 15 is crashing for some reason
|
||||
guard #available(iOS 16, *) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let provider = LPMetadataProvider()
|
||||
|
||||
do {
|
||||
return try await provider.startFetchingMetadata(for: url)
|
||||
} catch {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +169,7 @@ struct NoteContentView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let state = test_damus_state()
|
||||
let content = "hi there https://jb55.com/s/Oct12-150217.png 5739a762ef6124dd.jpg"
|
||||
let artifacts = NoteArtifacts(content: content, images: [], invoices: [])
|
||||
let artifacts = NoteArtifacts(content: content, images: [], invoices: [], links: [])
|
||||
NoteContentView(privkey: "", event: NostrEvent(content: content, pubkey: "pk"), profiles: state.profiles, show_images: true, artifacts: artifacts, size: .normal)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ func PostButton(action: @escaping () -> ()) -> some View {
|
||||
radius: 3,
|
||||
x: 3,
|
||||
y: 3)
|
||||
.keyboardShortcut("n", modifiers: [.command, .shift])
|
||||
}
|
||||
|
||||
func PostButtonContainer(action: @escaping () -> ()) -> some View {
|
||||
|
||||
@@ -119,6 +119,8 @@ struct ProfileView: View {
|
||||
@StateObject var profile: ProfileModel
|
||||
@StateObject var followers: FollowersModel
|
||||
@State private var showingEditProfile = false
|
||||
@State var showingSelectWallet: Bool = false
|
||||
@State var inv: String = ""
|
||||
@State var is_zoomed: Bool = false
|
||||
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@@ -126,9 +128,14 @@ struct ProfileView: View {
|
||||
|
||||
//@EnvironmentObject var profile: ProfileModel
|
||||
|
||||
func LNButton(_ url: URL, profile: Profile) -> some View {
|
||||
func LNButton(lud06: String?, lud16: String?, profile: Profile) -> some View {
|
||||
Button(action: {
|
||||
UIApplication.shared.open(url)
|
||||
if let l = lud06 {
|
||||
inv = l
|
||||
} else {
|
||||
inv = lud16 ?? ""
|
||||
}
|
||||
showingSelectWallet = true
|
||||
}) {
|
||||
Image(systemName: "bolt.circle")
|
||||
.symbolRenderingMode(.palette)
|
||||
@@ -138,9 +145,11 @@ struct ProfileView: View {
|
||||
Button {
|
||||
UIPasteboard.general.string = profile.lnurl ?? ""
|
||||
} label: {
|
||||
Label("Copy LNUrl", systemImage: "doc.on.doc")
|
||||
Label("Copy LNURL", systemImage: "doc.on.doc")
|
||||
}
|
||||
}
|
||||
}.sheet(isPresented: $showingSelectWallet, onDismiss: {showingSelectWallet = false}) {
|
||||
SelectWalletView(showingSelectWallet: $showingSelectWallet, invoice: $inv)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,10 +181,10 @@ struct ProfileView: View {
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
|
||||
if let profile = data {
|
||||
if let lnuri = profile.lightning_uri {
|
||||
LNButton(lnuri, profile: profile)
|
||||
if (profile.lud06 != nil || profile.lud16 != nil) {
|
||||
LNButton(lud06: profile.lud06, lud16: profile.lud16, profile: profile)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
92
damus/Views/SelectWalletView.swift
Normal file
92
damus/Views/SelectWalletView.swift
Normal file
@@ -0,0 +1,92 @@
|
||||
//
|
||||
// SelectWalletView.swift
|
||||
// damus
|
||||
//
|
||||
// Created by Suhail Saqan on 12/22/22.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct WalletItem : Decodable, Identifiable, Hashable {
|
||||
var id: Int
|
||||
var name : String
|
||||
var link : String
|
||||
var appStoreLink : String
|
||||
var image: String
|
||||
}
|
||||
|
||||
struct SelectWalletView: View {
|
||||
@Binding var showingSelectWallet: Bool
|
||||
@Binding var invoice: String
|
||||
@Environment(\.openURL) private var openURL
|
||||
@State var invoice_copied: Bool = false
|
||||
|
||||
let generator = UIImpactFeedbackGenerator(style: .light)
|
||||
|
||||
let walletItems = try! JSONDecoder().decode([WalletItem].self, from: Constants.WALLETS)
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
Form {
|
||||
Section("Copy invoice") {
|
||||
HStack {
|
||||
Text(invoice).font(.body)
|
||||
.lineLimit(2)
|
||||
.truncationMode(.tail)
|
||||
|
||||
Spacer()
|
||||
|
||||
Image(systemName: self.invoice_copied ? "checkmark.circle" : "doc.on.doc").foregroundColor(.blue)
|
||||
}.clipShape(RoundedRectangle(cornerRadius: 5)).onTapGesture {
|
||||
UIPasteboard.general.string = invoice
|
||||
self.invoice_copied = true
|
||||
generator.impactOccurred()
|
||||
}
|
||||
}
|
||||
Section("Select a lightning wallet"){
|
||||
List{
|
||||
Button() {
|
||||
if let url = URL(string: "lightning:\(invoice)"), UIApplication.shared.canOpenURL(url) {
|
||||
openURL(url)
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
Text("Default Wallet").font(.body).foregroundColor(.blue)
|
||||
}
|
||||
}.buttonStyle(.plain)
|
||||
ForEach(walletItems, id: \.self) { wallet in
|
||||
Button() {
|
||||
if let url = URL(string: "\(wallet.link)\(invoice)"), UIApplication.shared.canOpenURL(url) {
|
||||
print("opening wallet url \(url)")
|
||||
openURL(url)
|
||||
} else {
|
||||
if let url = URL(string: wallet.appStoreLink), UIApplication.shared.canOpenURL(url) {
|
||||
openURL(url)
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
Image(wallet.image).resizable().frame(width: 32.0, height: 32.0,alignment: .center).cornerRadius(5)
|
||||
Text(wallet.name).font(.body)
|
||||
}
|
||||
}.buttonStyle(.plain)
|
||||
}
|
||||
}.padding(.vertical, 2.5)
|
||||
}
|
||||
}.navigationBarTitle(Text("Pay the lightning invoice"), displayMode: .inline).navigationBarItems(trailing: Button(action: {
|
||||
self.showingSelectWallet = false
|
||||
}) {
|
||||
Text("Done").bold()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SelectWalletView_Previews: PreviewProvider {
|
||||
@State static var show: Bool = true
|
||||
@State static var invoice: String = ""
|
||||
|
||||
static var previews: some View {
|
||||
SelectWalletView(showingSelectWallet: $show, invoice: $invoice)
|
||||
}
|
||||
}
|
||||
@@ -245,7 +245,7 @@ struct ThreadV2View: View {
|
||||
.buttonStyle(.plain)
|
||||
.onAppear {
|
||||
// TODO: find another solution to prevent layout shifting and layout blocking on large responses
|
||||
reader.scrollTo("main", anchor: .center)
|
||||
reader.scrollTo("main", anchor: .bottom)
|
||||
}
|
||||
}
|
||||
}.background(GeometryReader { geometry in
|
||||
|
||||
Reference in New Issue
Block a user