Browse Source

Merge branch 'master' into viewModel

profile-edit
Sam DuBois 2 years ago
committed by GitHub
parent
commit
f0d242a53e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 69
      README.md
  2. 3
      damus-c/damus.c
  3. 12
      damus.xcodeproj/project.pbxproj
  4. 14
      damus/Components/ImageCarousel.swift
  5. 1
      damus/Util/Keys.swift
  6. 1
      damus/Views/AddRelayView.swift
  7. 35
      damus/Views/PostView.swift
  8. 9
      damus/Views/ProfilePicView.swift
  9. 1
      damus/Views/ProfileView.swift
  10. 1
      damus/Views/SearchHomeView.swift
  11. 18
      damusTests/InvoiceTests.swift

69
README.md

@ -22,6 +22,75 @@ damus implements the following [Nostr Implementation Possibilities][nips]
[nip10]: https://github.com/nostr-protocol/nips/blob/master/10.md
[nip12]: https://github.com/nostr-protocol/nips/blob/master/12.md
## Getting Started on Damus
### Damus iOS
1) Get the Damus app on Testflight: https://testflight.apple.com/join/CLwjLxWl
#### ⚙️ Settings (gear icon, top right)
- Relays: You can add more relays to send your notes to by tapping the "+".
- Find more relays to add: https://nostr.info/relays/
- Public Key (pubkey): Your public, personal address and how people can find and tag you
- Secret Key: Your *private* key unique to you. Never share your private key publically and share with other clients at your own risk!
- Save your keys somewhere safe
- Log out
#### 🏠 Personal Feed (home icon, bottom navigation)
- Feed from everyone you follow
- Can post notes by tapping the blue + button
#### Notes (under 🏠 Personal Feed)
- Sending a Note is easy and it goes to both your 🏠 Personal and 🔍 Global Feeds
- To tag a user you must grab their pubkey:
1. Search their username in the search bar at the top of the 🔍 Global Feed and click their profile
2. Tap the 🔑 icon which will copy their pubkey to your clipboard
3. Go back to your 🏠 Personal Feed and tap the blue + button to compose your Note
4. Add @ direcly followed by the pubkey (e.g., `@npub1xtscya34g58tk0z605fvr788k263gsu6cy9x0mhnm87echrgufzsevkk5s`)
- You can also long-press a Note to grab their User ID aka pubkey or Note ID to link directly to a Note.
- Currently you can't delete your Notes in the iOS app
- Share images by pasting the image url which you can grab from imgbb, imgur, etc. (i.e., `(https://i.ibb.co/2SHZbwm/alpha60.jpg)`). Currently images only load for people you follow in the 🏠 Personal Feed. Images are not automatically loaded in 🔍 Global Feed
- Engaging with Notes
- 💬 Replying to a Note: Tap the chat icon underneath the note. This will show up in the users’ notifications and in your 🏠 Personal and 🔍 Global Feeds
- ♺ Reposts: Tap the repost icon which will show up in your 🏠 Personal and 🔍 Global Feeds
- ♡ Likes: Tap the heart icon. Users will not get a notification, and cannot see who liked their note (currently, web clients can see your pfp only)
- Formatting Notes (may not format as intended in other web clients)
- Italics: 1 asterisk `*italic*`
- Bold: 2 asterisk `**bold**`
- Strikethrough: 2 tildes `~~strikethrough~~`
- Code: 1 back-tick ``code``
#### 💬 Encrypted DMs (chat app, bottom navigation)
- Tap the chat icon and you'll notice there's nothing to see at first. Go to a user profile and tap the 💬 chat icon next to the follow button to begin a DM
#### 🔍 Global Feed (magnify glass, bottom navigation)
- View the Global Feed from all the relays you've added in ⚙️ Settings. Currently you can only search hashtags and user names and pubkeys
#### 🔔 Notifications
- All your notifications except 💬 DMs
#### 👤 Change Your Profile (PFP) and Bio
- Currently you can't change your pfp on the Damus app (coming soon!). Here's how to do it on other clients (do at your own risk)
1. Get the [Alby](https://getalby.com/) or [nos2x](https://chrome.google.com/webstore/detail/nos2x/kpgefcfmnafjgpblomihpgmejjdanjjp) browser extension (Chrome, Brave)
2. Go to https://damus.io/key to convert your nsec key (secret key in ⚙️ Settings) into a hex version
i. For Alby, right-click the extension, select Options and scroll to the Nostr section to enter your secret hex key
ii. For nos2x, right-click the extension, select Options, then and add the relay `wss://relay.damus.io` and select both read and write, click Save, then enter your secret hex key and click save
3. Visit https://metadata.nostr.com and your profile data should auto-populate from the extension. If not click the extension or refresh the page
4. Add your image using a hosting site like imgbb.com
#### ⚡️ Request Sats
(Sats or Satoshis are the smallest denomination of bitcoin)
**Alby (browser extension)**
- Get the [Alby](https://getalby.com/) browser extension and create your Alby address [yourname]@getalby.com
- Convert your Damus secret key from nsec to hex at https://damus.io/key then go to Settings in Alby and under the Nostr section at the bottom of the page add your private hex key
- Click the Alby extension > click Receive > enter the amount of Sats > click Get Invoice > click Copy > then paste into Damus
- Note: On Damus Web it will appear as a string of characters but on Damus iOS it will appear as a clickable image
**Zeus (mobile app)**
- Download [Zeus](https://zeusln.app/) app (iOS, Google, APK)
- Tap Get Started button > tap Connect a node > click on + sign (top right) > select Indhub > press Scan Lndhub QR > (from the Alby browser extension… click your account on the top left > click Manage Accounts > click 3-dot menu to right of your account and click Export Account to get a QR code then go back to Zeus app) > scan the QR Code and tap Save Node Config button
- To create an invoice tap Lightning > tap Receive > type in amount > tap Create Invoice > tap Copy Invoice > paste into a new Damus note
## Contributing
Contributors welcome! [Email patches][git-send-email] to jb55@jb55.com are preferred, but I accept PRs on github as well.

3
damus-c/damus.c

@ -211,6 +211,9 @@ static int parse_invoice(struct cursor *cur, struct block *block) {
const u8 *start, *end;
char *fail;
struct bolt11 *bolt11;
// optional
parse_str(cur, "lightning:");
start = cur->p;
if (!parse_str(cur, "lnbc"))

12
damus.xcodeproj/project.pbxproj

@ -1019,9 +1019,9 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = XK7H4JAB3D;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = damus/Info.plist;
@ -1039,7 +1039,7 @@
"$(inherited)",
"$(PROJECT_DIR)",
);
MARKETING_VERSION = 0.1.6;
MARKETING_VERSION = 0.1.7;
PRODUCT_BUNDLE_IDENTIFIER = com.jb55.damus2;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
@ -1058,9 +1058,9 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = XK7H4JAB3D;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = damus/Info.plist;
@ -1078,7 +1078,7 @@
"$(inherited)",
"$(PROJECT_DIR)",
);
MARKETING_VERSION = 0.1.6;
MARKETING_VERSION = 0.1.7;
PRODUCT_BUNDLE_IDENTIFIER = com.jb55.damus2;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;

14
damus/Components/ImageCarousel.swift

@ -17,10 +17,15 @@ struct ImageViewer: View {
VStack{
Text(url.lastPathComponent)
KFImage(url)
KFAnimatedImage(url)
.configure { view in
view.framePreloadCount = 3
}
.cacheOriginalImage()
.loadDiskFileSynchronously()
.scaleFactor(UIScreen.main.scale)
.fade(duration: 0.1)
.aspectRatio(contentMode: .fit)
.tabItem {
Text(url.absoluteString)
}
@ -41,10 +46,15 @@ struct ImageCarousel: View {
var body: some View {
TabView {
ForEach(urls, id: \.absoluteString) { url in
KFImage(url)
KFAnimatedImage(url)
.configure { view in
view.framePreloadCount = 3
}
.cacheOriginalImage()
.loadDiskFileSynchronously()
.scaleFactor(UIScreen.main.scale)
.fade(duration: 0.1)
.aspectRatio(contentMode: .fit)
.tabItem {
Text(url.absoluteString)
}

1
damus/Util/Keys.swift

@ -69,7 +69,6 @@ func generate_new_keypair() -> Keypair {
let key = try! secp256k1.Signing.PrivateKey()
let privkey = hex_encode(key.rawRepresentation)
let pubkey = hex_encode(Data(key.publicKey.xonly.bytes))
print("generating privkey:\(privkey) pubkey:\(pubkey)")
return Keypair(pubkey: pubkey, privkey: privkey)
}

1
damus/Views/AddRelayView.swift

@ -18,6 +18,7 @@ struct AddRelayView: View {
Form {
Section("Add Relay") {
TextField("wss://some.relay.com", text: $relay)
.autocorrectionDisabled(true)
.textInputAutocapitalization(.never)
}
}

35
damus/Views/PostView.swift

@ -15,8 +15,7 @@ enum NostrPostResult {
let POST_PLACEHOLDER = "Type your post here..."
struct PostView: View {
@State var post: String = POST_PLACEHOLDER
@State var new: Bool = true
@State var post: String = ""
let replying_to: NostrEvent?
@FocusState var focus: Bool
@ -50,7 +49,7 @@ struct PostView: View {
}
var is_post_empty: Bool {
return post == POST_PLACEHOLDER || post.allSatisfy { $0.isWhitespace }
return post.allSatisfy { $0.isWhitespace }
}
var body: some View {
@ -71,18 +70,17 @@ struct PostView: View {
}
.padding([.top, .bottom], 4)
TextEditor(text: $post)
.foregroundColor(self.post == POST_PLACEHOLDER ? .gray : .primary)
.focused($focus)
.textInputAutocapitalization(.sentences)
.onTapGesture {
handle_post_placeholder()
}
.onChange(of: post) { value in
handle_post_placeholder()
ZStack(alignment: .topLeading) {
TextEditor(text: $post)
.focused($focus)
.textInputAutocapitalization(.sentences)
if post.isEmpty {
Text(POST_PLACEHOLDER)
.padding(.top, 8)
.padding(.leading, 10)
.foregroundColor(Color(uiColor: .placeholderText))
}
}
}
.onAppear() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
@ -91,14 +89,5 @@ struct PostView: View {
}
.padding()
}
func handle_post_placeholder() {
guard new else {
return
}
new = false
post = post.replacingOccurrences(of: POST_PLACEHOLDER, with: "")
}
}

9
damus/Views/ProfilePicView.swift

@ -56,16 +56,19 @@ struct ProfilePicView: View {
Group {
let pic = picture ?? profiles.lookup(id: pubkey)?.picture ?? robohash(pubkey)
let url = URL(string: pic)
let processor = ResizingImageProcessor(referenceSize: CGSize(width: size, height: size))
KFImage.url(url)
KFAnimatedImage(url)
.configure { view in
view.framePreloadCount = 1
}
.placeholder { _ in
Placeholder
}
.setProcessor(processor)
.cacheOriginalImage()
.scaleFactor(UIScreen.main.scale)
.loadDiskFileSynchronously()
.fade(duration: 0.1)
.frame(width: size, height: size)
.clipShape(Circle())
.overlay(Circle().stroke(highlight_color(highlight), lineWidth: pfp_line_width(highlight)))
}

1
damus/Views/ProfileView.swift

@ -88,6 +88,7 @@ struct ProfileView: View {
@StateObject var followers: FollowersModel
@Environment(\.dismiss) var dismiss
@Environment(\.colorScheme) var colorScheme
//@EnvironmentObject var profile: ProfileModel

1
damus/Views/SearchHomeView.swift

@ -19,6 +19,7 @@ struct SearchHomeView: View {
TextField("", text: $search)
.padding(8)
.padding(.leading, 35)
.autocorrectionDisabled(true)
.textInputAutocapitalization(.never)
Label("", systemImage: "xmark.square")
.padding(EdgeInsets(top: 0.0, leading: 0.0, bottom: 0.0, trailing: 10.0))

18
damusTests/InvoiceTests.swift

@ -34,6 +34,24 @@ final class InvoiceTests: XCTestCase {
XCTAssertEqual(invoice.string, invstr)
}
func testParseInvoiceWithPrefix() throws {
let invstr = "lightning:lnbc100n1p357sl0sp5t9n56wdztun39lgdqlr30xqwksg3k69q4q2rkr52aplujw0esn0qpp5mrqgljk62z20q4nvgr6lzcyn6fhylzccwdvu4k77apg3zmrkujjqdpzw35xjueqd9ejqcfqv3jhxcmjd9c8g6t0dcxqyjw5qcqpjrzjqt56h4gvp5yx36u2uzqa6qwcsk3e2duunfxppzj9vhypc3wfe2wswz607uqq3xqqqsqqqqqqqqqqqlqqyg9qyysgqagx5h20aeulj3gdwx3kxs8u9f4mcakdkwuakasamm9562ffyr9en8yg20lg0ygnr9zpwp68524kmda0t5xp2wytex35pu8hapyjajxqpsql29r"
let parsed = parse_mentions(content: invstr, tags: [])
XCTAssertNotNil(parsed)
XCTAssertEqual(parsed.count, 1)
XCTAssertNotNil(parsed[0].is_invoice)
}
func testParseInvoiceWithPrefixCapitalized() throws {
let invstr = "LIGHTNING:LNBC100N1P357SL0SP5T9N56WDZTUN39LGDQLR30XQWKSG3K69Q4Q2RKR52APLUJW0ESN0QPP5MRQGLJK62Z20Q4NVGR6LZCYN6FHYLZCCWDVU4K77APG3ZMRKUJJQDPZW35XJUEQD9EJQCFQV3JHXCMJD9C8G6T0DCXQYJW5QCQPJRZJQT56H4GVP5YX36U2UZQA6QWCSK3E2DUUNFXPPZJ9VHYPC3WFE2WSWZ607UQQ3XQQQSQQQQQQQQQQQLQQYG9QYYSGQAGX5H20AEULJ3GDWX3KXS8U9F4MCAKDKWUAKASAMM9562FFYR9EN8YG20LG0YGNR9ZPWP68524KMDA0T5XP2WYTEX35PU8HAPYJAJXQPSQL29R"
let parsed = parse_mentions(content: invstr, tags: [])
XCTAssertNotNil(parsed)
XCTAssertEqual(parsed.count, 1)
XCTAssertNotNil(parsed[0].is_invoice)
}
func testParseInvoice() throws {
let invstr = "lnbc100n1p357sl0sp5t9n56wdztun39lgdqlr30xqwksg3k69q4q2rkr52aplujw0esn0qpp5mrqgljk62z20q4nvgr6lzcyn6fhylzccwdvu4k77apg3zmrkujjqdpzw35xjueqd9ejqcfqv3jhxcmjd9c8g6t0dcxqyjw5qcqpjrzjqt56h4gvp5yx36u2uzqa6qwcsk3e2duunfxppzj9vhypc3wfe2wswz607uqq3xqqqsqqqqqqqqqqqlqqyg9qyysgqagx5h20aeulj3gdwx3kxs8u9f4mcakdkwuakasamm9562ffyr9en8yg20lg0ygnr9zpwp68524kmda0t5xp2wytex35pu8hapyjajxqpsql29r"
let parsed = parse_mentions(content: invstr, tags: [])

Loading…
Cancel
Save