Skip to content

Commit

Permalink
add quote reposting #940 #941 #942 #943
Browse files Browse the repository at this point in the history
  • Loading branch information
bryanmontz committed Aug 23, 2024
1 parent a1b1238 commit bd92471
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Removed the like and repost counts from the Main and Profile feeds.
- Replaced hard-coded color values.
- Show quoted notes in note cards.
- Added quote-reposting.

## [0.1.25] - 2024-08-21Z

Expand Down
11 changes: 11 additions & 0 deletions Nos/Assets/Localization/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -9221,6 +9221,17 @@
}
}
},
"quote" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Quote"
}
}
}
},
"readMore" : {
"extractionState" : "manual",
"localizations" : {
Expand Down
58 changes: 52 additions & 6 deletions Nos/Views/NoteComposer/NoteComposer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,32 @@ struct NoteComposer: View {

/// The note that the user is replying to, if any.
private var replyToNote: Event?

/// The id of a note the user is quoting, if any.
private let quotedNoteID: RawEventID?

/// The quoted note, if any.
@State private var quotedNote: Event?

init(initialContents: String? = nil, replyTo: Event? = nil, isPresented: Binding<Bool>) {
init(
initialContents: String? = nil,
replyTo: Event? = nil,
quotedNoteID: RawEventID? = nil,
isPresented: Binding<Bool>
) {
_isPresented = isPresented
self.initialContents = initialContents
self.replyToNote = replyTo
self.quotedNoteID = quotedNoteID
}

/// The minimum height of the NoteTextEditor.
///
/// We do this because editor won't expand to fill available space when it's in a ScrollView.
/// and we need it to because people try to tap below the text field bounds to paste if it doesn't
/// fill the screen.
/// And we need it to because people try to tap below the text field bounds to paste if it doesn't
/// fill the screen. We remove this minimum in the case that a user is quote-reposting another note.
var minimumEditorHeight: CGFloat {
max(scrollViewHeight - 12, 0)
quotedNote == nil ? max(scrollViewHeight - 12, 0) : 0
}

var body: some View {
Expand All @@ -76,6 +88,12 @@ struct NoteComposer: View {
.offset(y: 100)
.id(0)
}
if let quotedNote {
NoteCard(note: quotedNote, rendersQuotedNotes: false, showsActions: false)
.withStyledBorder()
.padding(.horizontal, 16)
.padding(.bottom, 16)
}
}
.onAppear {
Task {
Expand Down Expand Up @@ -145,7 +163,7 @@ struct NoteComposer: View {
},
trailing: ActionButton(title: .localizable.post, action: postAction)
.frame(height: 22)
.disabled(editingController.isEmpty || isUploadingImage)
.disabled((editingController.isEmpty && quotedNoteID == nil) || isUploadingImage)
.padding(.bottom, 3)
)
.onAppear {
Expand All @@ -154,6 +172,9 @@ struct NoteComposer: View {
}
analytics.showedNoteComposer()
}
.task {
loadQuotedNote()
}
}
.alert(unwrapping: $alert)
}
Expand Down Expand Up @@ -201,15 +222,40 @@ struct NoteComposer: View {
}
isPresented = false
}

private func loadQuotedNote() {
guard let quotedNoteID else {
return
}

@Dependency(\.persistenceController) var persistenceController
quotedNote = try? Event.findOrCreateStubBy(
id: quotedNoteID,
context: persistenceController.viewContext
)
}

private var postText: AttributedString {
var text = editingController.text ?? ""
if let noteLink = quotedNote?.bech32NoteID {
if !text.characters.isEmpty {
text += AttributedString("\n\n")
}
text += AttributedString("nostr:\(noteLink)")
}
return text
}

// swiftlint:disable:next function_body_length
private func publishPost() async {
guard let keyPair = currentUser.keyPair, let author = currentUser.author else {
Log.error("Cannot post without a keypair")
return
}

guard let text = editingController.text else {
let text = postText

guard !text.characters.isEmpty else {
Log.error("Tried to publish a post with empty text")
return
}
Expand Down
16 changes: 16 additions & 0 deletions Nos/Views/RepostButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ struct RepostButton: View {
@State private var tapped = false
@State private var shouldConfirmRepost = false
@State private var shouldConfirmDelete = false
@State private var showQuotedNoteComposer = false

/// Initializes a RepostButton object.
///
Expand Down Expand Up @@ -62,6 +63,9 @@ struct RepostButton: View {
Button(String(localized: .localizable.repost)) {
Task { await repostNote() }
}
Button(String(localized: .localizable.quote)) {
quoteNote()
}
Button(String(localized: .localizable.cancel), role: .cancel) {
tapped = false
}
Expand All @@ -71,6 +75,14 @@ struct RepostButton: View {
Task { await deleteReposts() }
}
}
.sheet(isPresented: $showQuotedNoteComposer, content: {
NoteComposer(quotedNoteID: note.identifier, isPresented: $showQuotedNoteComposer)
.environment(currentUser)
.interactiveDismissDisabled()
.onDisappear {
tapped = false
}
})
}

func buttonPressed() async {
Expand Down Expand Up @@ -112,6 +124,10 @@ struct RepostButton: View {
}
}

private func quoteNote() {
showQuotedNoteComposer = true
}

func deleteReposts() async {
reposts
.filter {
Expand Down

0 comments on commit bd92471

Please sign in to comment.