Browse Source

Merge branch 'master' into profile-markdown

post-button-style
Lio李歐 2 years ago
committed by GitHub
parent
commit
a94b698e9d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 30
      CHANGELOG.md
  2. 12
      damus.xcodeproj/project.pbxproj
  3. 21
      damus/Assets.xcassets/bluewallet.imageset/Contents.json
  4. BIN
      damus/Assets.xcassets/bluewallet.imageset/bluewallet.png
  5. 21
      damus/Assets.xcassets/cashapp.imageset/Contents.json
  6. BIN
      damus/Assets.xcassets/cashapp.imageset/cashapp.png
  7. 21
      damus/Assets.xcassets/lnlink.imageset/Contents.json
  8. BIN
      damus/Assets.xcassets/lnlink.imageset/lnlink.png
  9. 21
      damus/Assets.xcassets/muun.imageset/Contents.json
  10. BIN
      damus/Assets.xcassets/muun.imageset/muun.png
  11. 21
      damus/Assets.xcassets/phoenix.imageset/Contents.json
  12. BIN
      damus/Assets.xcassets/phoenix.imageset/phoenix.png
  13. 21
      damus/Assets.xcassets/strike.imageset/Contents.json
  14. BIN
      damus/Assets.xcassets/strike.imageset/strike.png
  15. 21
      damus/Assets.xcassets/walletofsatoshi.imageset/Contents.json
  16. BIN
      damus/Assets.xcassets/walletofsatoshi.imageset/walletofsatoshi.png
  17. 21
      damus/Assets.xcassets/zebedee.imageset/Contents.json
  18. BIN
      damus/Assets.xcassets/zebedee.imageset/zebedee.png
  19. 21
      damus/Assets.xcassets/zeusln.imageset/Contents.json
  20. BIN
      damus/Assets.xcassets/zeusln.imageset/zeus.png
  21. 6
      damus/Components/ImageCarousel.swift
  22. 38
      damus/Components/InvoiceView.swift
  23. 3
      damus/ContentView.swift
  24. 13
      damus/Info.plist
  25. 2
      damus/Nostr/RelayPool.swift
  26. 15
      damus/Util/Constants.swift
  27. 39
      damus/Util/LinkView.swift
  28. 6
      damus/Util/Parser.swift
  29. 28
      damus/Views/AddRelayView.swift
  30. 2
      damus/Views/ChatView.swift
  31. 6
      damus/Views/ConfigView.swift
  32. 4
      damus/Views/DMView.swift
  33. 9
      damus/Views/EventView.swift
  34. 6
      damus/Views/FollowingView.swift
  35. 11
      damus/Views/MainTabView.swift
  36. 59
      damus/Views/NoteContentView.swift
  37. 1
      damus/Views/PostButton.swift
  38. 21
      damus/Views/ProfileView.swift
  39. 92
      damus/Views/SelectWalletView.swift
  40. 2
      damus/Views/ThreadV2View.swift
  41. 8
      damusTests/ReplyTests.swift
  42. 14
      devtools/changelog.py

30
CHANGELOG.md

@ -1,3 +1,33 @@
## [0.1.8-6] - 2022-12-28
### Added
- Lightning wallet selector (Suhail Saqan)
- Cmd-{1,2,3,4} to switch between tabs on MacOS (Jonathan Milligan)
- Shift-Cmd-N to create a post on MacOS (Jonathan Milligan)
- Link Previews! (Sam DuBois)
- Added paste and delete buttons to add relay field (Suhail Saqan)
### Changed
- Blur and opaque non-friend images rather than only display the link (Sam DuBois)
- Remove URLs in content text when image is displayed (Sam DuBois)
- Show non-image URLs as clickable link views (Sam DuBois)
- Adjusted Pay button on invoices. (Sam DuBois)
### Fixed
- Fix crash with @ sign in some posts (Pablo Fernandez)
- Swapped order of Logout and Cancel alert buttons (Terry Yiu)
- Fixed padding issue on tabbar on some devices (Sam DuBois)
- Fix post button moving after selecting from search result (OlegAba)
- Don't show white background on images in dark mode (William Casarin)
[0.1.8-6]: https://github.com/damus-io/damus/releases/tag/v0.1.8-6
## [0.1.8-5] - 2022-12-27
### Added

12
damus.xcodeproj/project.pbxproj

@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
3165648B295B70D500C64604 /* LinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3165648A295B70D500C64604 /* LinkView.swift */; };
3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAE5294E69C000EE4006 /* EmptyTimelineView.swift */; };
3169CAED294FCCFC00EE4006 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAEC294FCCFC00EE4006 /* Constants.swift */; };
31D2E847295218AF006D67F8 /* Shimmer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D2E846295218AF006D67F8 /* Shimmer.swift */; };
@ -130,6 +131,7 @@
4CEE2AF9280B2EAC00AB5EEF /* PowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF8280B2EAC00AB5EEF /* PowView.swift */; };
4CEE2B02280B39E800AB5EEF /* EventActionBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */; };
6C7DE41F2955169800E66263 /* Vault in Frameworks */ = {isa = PBXBuildFile; productRef = 6C7DE41E2955169800E66263 /* Vault */; };
BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */; };
E990020F2955F837003BBC5A /* EditMetadataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E990020E2955F837003BBC5A /* EditMetadataView.swift */; };
E9E4ED0B295867B900DD7078 /* ThreadV2View.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */; };
/* End PBXBuildFile section */
@ -152,6 +154,7 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
3165648A295B70D500C64604 /* LinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkView.swift; sourceTree = "<group>"; };
3169CAE5294E69C000EE4006 /* EmptyTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyTimelineView.swift; sourceTree = "<group>"; };
3169CAEC294FCCFC00EE4006 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Constants.swift; path = damus/Util/Constants.swift; sourceTree = SOURCE_ROOT; };
31D2E846295218AF006D67F8 /* Shimmer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shimmer.swift; sourceTree = "<group>"; };
@ -309,6 +312,7 @@
4CEE2AF6280B2DEA00AB5EEF /* ProfileName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileName.swift; sourceTree = "<group>"; };
4CEE2AF8280B2EAC00AB5EEF /* PowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PowView.swift; sourceTree = "<group>"; };
4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventActionBar.swift; sourceTree = "<group>"; };
BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectWalletView.swift; sourceTree = "<group>"; };
E990020E2955F837003BBC5A /* EditMetadataView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditMetadataView.swift; sourceTree = "<group>"; };
E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadV2View.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -480,6 +484,7 @@
4C216F33286F5ACD00040376 /* DMView.swift */,
4C06670028FC7C5900038D2A /* RelayView.swift */,
E990020E2955F837003BBC5A /* EditMetadataView.swift */,
BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */,
E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */,
);
path = Views;
@ -517,6 +522,7 @@
4C90BD19283AA67F008EE7EF /* Bech32.swift */,
4C216F352870A9A700040376 /* InputDismissKeyboard.swift */,
3169CAEC294FCCFC00EE4006 /* Constants.swift */,
3165648A295B70D500C64604 /* LinkView.swift */,
);
path = Util;
sourceTree = "<group>";
@ -808,6 +814,7 @@
4C3AC79F2833115300E1F516 /* FollowButtonView.swift in Sources */,
4C3BEFD22819DB9B00B3DE84 /* ProfileModel.swift in Sources */,
4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */,
BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */,
3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */,
4C3EA64928FF597700C48A62 /* bech32.c in Sources */,
4C90BD162839DB54008EE7EF /* NostrMetadata.swift in Sources */,
@ -838,6 +845,7 @@
4C363AA428296DEE006E126D /* SearchModel.swift in Sources */,
4CEE2AF3280B25C500AB5EEF /* ProfilePicView.swift in Sources */,
4CEE2AF9280B2EAC00AB5EEF /* PowView.swift in Sources */,
3165648B295B70D500C64604 /* LinkView.swift in Sources */,
4C3BEFD42819DE8F00B3DE84 /* NostrKind.swift in Sources */,
4C3EA66028FF5E7700C48A62 /* node_id.c in Sources */,
4CE6DEE727F7A08100C66700 /* damusApp.swift in Sources */,
@ -1031,7 +1039,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 5;
CURRENT_PROJECT_VERSION = 7;
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
DEVELOPMENT_TEAM = XK7H4JAB3D;
ENABLE_PREVIEWS = YES;
@ -1070,7 +1078,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 5;
CURRENT_PROJECT_VERSION = 7;
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
DEVELOPMENT_TEAM = XK7H4JAB3D;
ENABLE_PREVIEWS = YES;

21
damus/Assets.xcassets/bluewallet.imageset/Contents.json

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "bluewallet.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

BIN
damus/Assets.xcassets/bluewallet.imageset/bluewallet.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

21
damus/Assets.xcassets/cashapp.imageset/Contents.json

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "cashapp.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

BIN
damus/Assets.xcassets/cashapp.imageset/cashapp.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

21
damus/Assets.xcassets/lnlink.imageset/Contents.json

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "lnlink.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

BIN
damus/Assets.xcassets/lnlink.imageset/lnlink.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 547 KiB

21
damus/Assets.xcassets/muun.imageset/Contents.json

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "muun.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

BIN
damus/Assets.xcassets/muun.imageset/muun.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

21
damus/Assets.xcassets/phoenix.imageset/Contents.json

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "phoenix.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

BIN
damus/Assets.xcassets/phoenix.imageset/phoenix.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

21
damus/Assets.xcassets/strike.imageset/Contents.json

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "strike.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

BIN
damus/Assets.xcassets/strike.imageset/strike.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

21
damus/Assets.xcassets/walletofsatoshi.imageset/Contents.json

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "walletofsatoshi.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

BIN
damus/Assets.xcassets/walletofsatoshi.imageset/walletofsatoshi.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

21
damus/Assets.xcassets/zebedee.imageset/Contents.json

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "zebedee.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

BIN
damus/Assets.xcassets/zebedee.imageset/zebedee.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

21
damus/Assets.xcassets/zeusln.imageset/Contents.json

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "zeus.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

BIN
damus/Assets.xcassets/zeusln.imageset/zeus.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

6
damus/Components/ImageCarousel.swift

@ -122,6 +122,7 @@ struct ImageCarousel: View {
TabView {
ForEach(urls, id: \.absoluteString) { url in
Rectangle()
.foregroundColor(Color.clear)
.overlay {
KFAnimatedImage(url)
.configure { view in
@ -136,6 +137,11 @@ struct ImageCarousel: View {
Text(url.absoluteString)
}
.id(url.absoluteString)
.contextMenu {
Button("Copy Image") {
UIPasteboard.general.string = url.absoluteString
}
}
}
}
}

38
damus/Components/InvoiceView.swift

@ -8,24 +8,38 @@
import SwiftUI
struct InvoiceView: View {
@Environment(\.colorScheme) var colorScheme
let invoice: Invoice
@State var showingSelectWallet: Bool = false
@State var inv: String = ""
var PayButton: some View {
Button("Pay") {
guard let url = URL(string: "lightning:" + invoice.string) else {
return
}
UIApplication.shared.open(url)
Button {
inv = invoice.string
showingSelectWallet = true
} label: {
RoundedRectangle(cornerRadius: 20)
.foregroundColor(colorScheme == .light ? .black : .white)
.overlay {
Text("Pay")
.fontWeight(.medium)
.foregroundColor(colorScheme == .light ? .white : .black)
}
}
//.buttonStyle(.bordered)
.onTapGesture {
// Temporary solution so that the "pay" button can be clicked (Yes we need an empty tap gesture)
}
.buttonStyle(.bordered)
}
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 20)
RoundedRectangle(cornerRadius: 10)
.foregroundColor(.secondary.opacity(0.1))
VStack(alignment: .trailing, spacing: 12) {
VStack(alignment: .leading, spacing: 12) {
HStack {
Label("", systemImage: "bolt.fill")
.foregroundColor(.orange)
@ -36,9 +50,13 @@ struct InvoiceView: View {
Text("\(invoice.amount / 1000) sats")
.font(.title)
PayButton
.zIndex(5.0)
.frame(height: 50)
.zIndex(10.0)
}
.padding()
.padding(30)
}
.sheet(isPresented: $showingSelectWallet, onDismiss: {showingSelectWallet = false}) {
SelectWalletView(showingSelectWallet: $showingSelectWallet, invoice: $inv)
}
}
}

3
damus/ContentView.swift

@ -88,7 +88,7 @@ struct ContentView: View {
self.active_sheet = .post
}
}
}
}.ignoresSafeArea(.keyboard, edges: .bottom)
}
.safeAreaInset(edge: .top) {
VStack(spacing: 0) {
@ -228,6 +228,7 @@ struct ContentView: View {
}
TabBar(new_events: $home.new_events, selected: $selected_timeline, action: switch_timeline)
.padding()
}
.onAppear() {
self.connect()

13
damus/Info.plist

@ -15,6 +15,19 @@
</array>
</dict>
</array>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>muun</string>
<string>zeusln</string>
<string>zebedee</string>
<string>lightning</string>
<string>squarecash</string>
<string>phoenix</string>
<string>lnlink</string>
<string>strike</string>
<string>bluewallet</string>
<string>walletofsatoshi</string>
</array>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>

2
damus/Nostr/RelayPool.swift

@ -190,6 +190,6 @@ class RelayPool {
func add_rw_relay(_ pool: RelayPool, _ url: String) {
let url_ = URL(string: url)!
try! pool.add_relay(url_, info: RelayInfo.rw)
try? pool.add_relay(url_, info: RelayInfo.rw)
}

15
damus/Util/Constants.swift

@ -24,4 +24,19 @@ public class Constants {
NostrEvent(id: UUID().description, content: "Hello World! This is so cool!", pubkey: "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"),
NostrEvent(id: UUID().description, content: "Bonjour Le Monde", pubkey: "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"),
]
// New url prefixes needed to be added to LSApplicationQueriesSchemes
static let WALLETS = """
[
{"id": 0, "name": "Strike", "link": "strike:", "appStoreLink": "https://apps.apple.com/us/app/strike-bitcoin-payments/id1488724463", "image": "strike"},
{"id": 1, "name": "Cash App", "link": "squarecash://", "appStoreLink": "https://apps.apple.com/us/app/cash-app/id711923939", "image": "cashapp"},
{"id": 2, "name": "Muun", "link": "muun:", "appStoreLink": "https://apps.apple.com/us/app/muun-wallet/id1482037683", "image": "muun"},
{"id": 3, "name": "Blue Wallet", "link": "bluewallet:lightning:", "appStoreLink": "https://apps.apple.com/us/app/bluewallet-bitcoin-wallet/id1376878040", "image": "bluewallet"},
{"id": 4, "name": "Wallet Of Satoshi", "link": "walletofsatoshi:lightning:", "appStoreLink": "https://apps.apple.com/us/app/wallet-of-satoshi/id1438599608", "image": "walletofsatoshi"},
{"id": 5, "name": "Zebedee", "link": "zebedee:lightning:", "appStoreLink": "https://apps.apple.com/us/app/zebedee-wallet/id1484394401", "image": "zebedee"},
{"id": 6, "name": "Zeus LN", "link": "zeusln:lightning:", "appStoreLink": "https://apps.apple.com/us/app/zeus-ln/id1456038895", "image": "zeusln"},
{"id": 7, "name": "LNLink", "link": "lnlink:lightning:", "appStoreLink": "https://testflight.apple.com/join/aNY4yuuZ", "image": "lnlink"},
{"id": 8, "name": "Phoenix", "link": "phoenix://", "appStoreLink": "https://apps.apple.com/us/app/phoenix-wallet/id1544097028", "image": "phoenix"},
]
""".data(using: .utf8)!
}

39
damus/Util/LinkView.swift

@ -0,0 +1,39 @@
//
// LinkView.swift
// damus
//
// Created by Sam DuBois on 12/27/22.
//
import SwiftUI
import LinkPresentation
class CustomLinkView: LPLinkView {
override var intrinsicContentSize: CGSize { CGSize(width: 0, height: super.intrinsicContentSize.height) }
}
struct LinkViewRepresentable: UIViewRepresentable {
typealias UIViewType = CustomLinkView
var metadata: LPLinkMetadata?
var url: URL?
func makeUIView(context: Context) -> CustomLinkView {
if let metadata {
let linkView = CustomLinkView(metadata: metadata)
return linkView
}
if let url {
let linkView = CustomLinkView(url: url)
return linkView
}
return CustomLinkView()
}
func updateUIView(_ uiView: CustomLinkView, context: Context) {
}
}

6
damus/Util/Parser.swift

@ -97,6 +97,12 @@ func parse_digit(_ p: Parser) -> Int? {
func parse_hex_char(_ p: Parser) -> Character? {
let ind = p.str.index(p.str.startIndex, offsetBy: p.pos)
// Check that we're within the bounds of p.str's length
if p.pos >= p.str.count {
return nil
}
if let c = p.str[ind].unicodeScalars.first {
// hex chars
let d = c.value

28
damus/Views/AddRelayView.swift

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

2
damus/Views/ChatView.swift

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

6
damus/Views/ConfigView.swift

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

4
damus/Views/DMView.swift

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

9
damus/Views/EventView.swift

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

6
damus/Views/FollowingView.swift

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

11
damus/Views/MainTabView.swift

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

59
damus/Views/NoteContentView.swift

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

1
damus/Views/PostButton.swift

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

21
damus/Views/ProfileView.swift

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

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

2
damus/Views/ThreadV2View.swift

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

8
damusTests/ReplyTests.swift

@ -53,6 +53,14 @@ class ReplyTests: XCTestCase {
XCTAssertEqual(blocks[0].is_text, content)
}
func testAtAtEnd() {
let content = "what @"
let blocks = parse_post_blocks(content: content)
XCTAssertEqual(blocks.count, 1)
XCTAssertEqual(blocks[0].is_text, "what @")
}
func testHashtagsInQuote() {
let content = "This is my \"#awesome post\""
let blocks = parse_post_blocks(content: content)

14
devtools/changelog.py

@ -24,7 +24,7 @@ sections = [
repo = 'damus-io/damus'
Entry = namedtuple("Entry", ["commit", "pullreq", "content", "section"])
Entry = namedtuple("Entry", ["commit", "pullreq", "content", "section", "author"])
Link = namedtuple("Link", ["ref", "content", "url"])
@ -46,14 +46,20 @@ def get_log_entries(commitrange):
commit = None
logs = git("log {commitrange}".format(commitrange=commitrange))
entries = []
author = ""
for l in logs.split('\n'):
m = re.match(r'^commit ([A-Fa-f0-9]{40})$', l)
a = re.match(r'^Author: ([^<]+)<.*$', l)
if m:
commit = m.group(1)
if a:
author = "(" + a.group(1)[:-1] + ")"
m = re.match(
r'^\s+Changelog-({}): (.*)$'.format("|".join(sections)), l, re.IGNORECASE)
if not m:
continue
@ -73,7 +79,7 @@ def get_log_entries(commitrange):
# pullreq = None
pullreq = None
e = Entry(commit, pullreq, m.group(2), m.group(1).lower())
e = Entry(commit, pullreq, m.group(2), m.group(1).lower(), author)
entries.append(e)
return entries
@ -112,9 +118,9 @@ def commit_date(commitsha):
template = Template("""<%def name="group(entries)">
% for e in entries:
% if e.pullreq is not None:
- ${e.content} ([#${e.pullreq}])
- ${e.content} ([#${e.pullreq}])
% else:
- ${e.content}
- ${e.content} ${e.author}
% endif
% endfor

Loading…
Cancel
Save