Compare commits

..

1 Commits

Author SHA1 Message Date
3a331f6a05 Hide end previewables when hashtags are present
Changelog-Fixed: Hide end previewables when hashtags are present
Signed-off-by: Terry Yiu <git@tyiu.xyz>
2025-05-19 21:51:39 -04:00
6 changed files with 65 additions and 93 deletions

View File

@@ -1,51 +1,3 @@
## [1.14] - 2025-05-25
### Added
- Added safety reminder to wallets with higher balance (Daniel DAquino)
- Added one-click Coinos wallet setup (Daniel DAquino)
- Add notification setting to hide hellthreads (Terry Yiu)
- Added separated first aid option for relay lists that does not need a contact list reset (Daniel DAquino)
- Added NIP-65 relay list support (Daniel DAquino)
- Added Unicode 16 emoji reactions for iOS 18.4+ by upgrading EmojiPicker (Terry Yiu)
- Added a search interface to the settings screen (SanjaySiddharth)
- Added view introducing users to Zaps (ericholguin)
- Added new wallet view with balance and transactions list (ericholguin)
- Added copy technical info button to user visible errors, so that users can more easily share errors with developers (Daniel DAquino)
- Add dismiss button to wallet high balance reminders (Daniel DAquino)
- Zap receiver information now included for outgoing zaps (Daniel DAquino)
- Added inline note rendering of invoices to pull up wallet selector sheet (Terry Yiu)
- Added route to profile page from wallet tx list (ericholguin)
### Changed
- Added additional information on top of blurred images (SanjaySiddharth)
- Improved robustness of relay list handling (Daniel DAquino)
- Updated image cache for better stability (Daniel DAquino)
- Improved integration with Nostr Wallet Connect wallets (ericholguin)
- Added relay connectivity information to NWC settings (Daniel DAquino)
- Improved handling around NWC responses (Daniel DAquino)
- Added more human visible errors on NWC wallets to aid with troubleshooting (Daniel DAquino)
- Re-enabled note zaps as permitted by the new App Store guidelines (Daniel DAquino)
### Fixed
- Hide future notes from timeline (Terry Yiu)
- Fixed issue where profiles with a NIP-65 relay list would not display on Damus (Daniel DAquino)
- Fix quote notes to include missing q tag (Terry Yiu)
- Fixed issue where the side menu would close when copying the npub (SanjaySiddharth)
- Fixed issue where cached images would be backed up to iCloud (Daniel DAquino)
- Optimized classify_url function (Terry Yiu)
- Fixed note rendering for those that contain previewable items or leading and trailing whitespaces (Terry Yiu)
- Fixed issue where some videos would become unplayable after some time using the app (Daniel DAquino)
[1.14]: https://github.com/damus-io/damus/releases/tag/v1.14
## [1.13.1] - 2025-03-21
### Fixed

View File

@@ -6419,7 +6419,7 @@
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
MACOSX_DEPLOYMENT_TARGET = 12.3;
MARKETING_VERSION = 1.15;
MARKETING_VERSION = 1.14;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
@@ -6484,7 +6484,7 @@
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
MACOSX_DEPLOYMENT_TARGET = 12.3;
MARKETING_VERSION = 1.15;
MARKETING_VERSION = 1.14;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;

View File

@@ -117,6 +117,10 @@ func render_blocks(blocks bs: Blocks, profiles: Profiles, can_hide_last_previewa
}
hide_text_index = i
} else if case .text(let txt) = block, txt.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
// We should hide whitespace at the end sequence.
hide_text_index = i
} else if case .hashtag = block {
// We should keep hashtags at the end sequence but hide all the other previewables around it.
hide_text_index = i
} else {
break
@@ -149,7 +153,16 @@ func render_blocks(blocks bs: Blocks, profiles: Profiles, can_hide_last_previewa
// No need to show the text representation of the block if the only previewables are the sequence of them
// found at the end of the content.
// This is to save unnecessary use of screen space.
// The only exception is that if there are hashtags embedded in the end sequence, which is not uncommon,
// then we still want to show those hashtags but hide everything else that is previewable in the end sequence.
if ind >= hide_text_index {
if case .text(let txt) = block, txt.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
if case .hashtag = blocks[safe: ind+1] {
return str + CompatibleText(stringLiteral: reduce_text_block(ind: ind, hide_text_index: -1, txt: txt))
}
} else if case .hashtag(let htag) = block {
return str + hashtag_str(htag)
}
return str
}
}

View File

@@ -79,7 +79,6 @@ struct PostView: View {
var autoSaveModel: AutoSaveIndicatorView.AutoSaveViewModel
@State var preUploadedMedia: [PreUploadedMedia] = []
@State var mediaUploadUnderProgress: MediaUpload? = nil
@StateObject var image_upload: ImageUploadModel = ImageUploadModel()
@StateObject var tagModel: TagModel = TagModel()
@@ -331,6 +330,11 @@ struct PostView: View {
PostButton
}
if let progress = image_upload.progress {
ProgressView(value: progress, total: 1.0)
.progressViewStyle(.linear)
}
Divider()
.foregroundColor(DamusColors.neutral3)
.padding(.top, 5)
@@ -342,7 +346,6 @@ struct PostView: View {
@discardableResult
func handle_upload(media: MediaUpload) async -> Bool {
mediaUploadUnderProgress = media
let uploader = damus_state.settings.default_media_uploader
let img = getImage(media: media)
@@ -351,7 +354,6 @@ struct PostView: View {
async let blurhash = calculate_blurhash(img: img)
let res = await image_upload.start(media: media, uploader: uploader, mediaType: .normal, keypair: damus_state.keypair)
mediaUploadUnderProgress = nil
switch res {
case .success(let url):
guard let url = URL(string: url) else {
@@ -399,13 +401,10 @@ struct PostView: View {
}
.id("post")
PVImageCarouselView(media: $uploadedMedias,
mediaUnderProgress: $mediaUploadUnderProgress,
imageUploadModel: image_upload,
deviceWidth: deviceSize.size.width)
.onChange(of: uploadedMedias) { media in
post_changed(post: post, media: media)
}
PVImageCarouselView(media: $uploadedMedias, deviceWidth: deviceSize.size.width)
.onChange(of: uploadedMedias) { media in
post_changed(post: post, media: media)
}
if case .quoting(let ev) = action {
BuilderEventView(damus: damus_state, event: ev)
@@ -621,8 +620,6 @@ struct PostView_Previews: PreviewProvider {
struct PVImageCarouselView: View {
@Binding var media: [UploadedMedia]
@Binding var mediaUnderProgress: MediaUpload?
@ObservedObject var imageUploadModel: ImageUploadModel
let deviceWidth: CGFloat
@@ -670,25 +667,6 @@ struct PVImageCarouselView: View {
.padding(.bottom, 35)
}
}
if let mediaUP = mediaUnderProgress, let progress = imageUploadModel.progress {
ZStack {
// Media under upload-progress
Image(uiImage: getImage(media: mediaUP))
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: media.count == 0 ? deviceWidth * 0.8 : 250, height: media.count == 0 ? 400 : 250)
.cornerRadius(10)
.opacity(0.3)
.padding()
// Circle showing progress on top of media
Circle()
.trim(from: 0, to: CGFloat(progress))
.stroke(Color.damusPurple, lineWidth: 5.0)
.rotationEffect(.degrees(-90))
.frame(width: 30, height: 30)
.padding()
}
}
}
.padding()
}

View File

@@ -12,19 +12,9 @@ struct QuoteRepostsView: View {
@ObservedObject var model: EventsModel
var body: some View {
TimelineView(events: model.events, loading: $model.loading, damus: damus_state, show_friend_icon: true, filter: ContentFilters.default_filters(damus_state: damus_state).filter(ev:)) {
ZStack(alignment: .leading) {
DamusBackground(maxHeight: 250)
.mask(LinearGradient(gradient: Gradient(colors: [.black, .black, .black, .clear]), startPoint: .top, endPoint: .bottom))
Text("Quotes", comment: "Navigation bar title for Quote Reposts view.")
.foregroundStyle(DamusLogoGradient.gradient)
.font(.title.bold())
.padding(.leading, 30)
.padding(.top, 30)
}
}
.ignoresSafeArea()
TimelineView<AnyView>(events: model.events, loading: $model.loading, damus: damus_state, show_friend_icon: true, filter: ContentFilters.default_filters(damus_state: damus_state).filter(ev:))
.padding(.bottom, tabHeight)
.navigationBarTitle(NSLocalizedString("Quotes", comment: "Navigation bar title for Quote Reposts view."))
.onAppear {
model.subscribe()
}

View File

@@ -291,6 +291,45 @@ class NoteContentViewTests: XCTestCase {
XCTAssertEqual(noteArtifactsSeparated.images[0].absoluteString, "https://damus.io/nothidden.png")
}
func testRenderBlocksWithPreviewableBlocksAtEndAreHiddenWhenHashtagsAreEmbedded() throws {
let noteId = test_note.id.bech32
let invoiceString = "lnbc100n1p357sl0sp5t9n56wdztun39lgdqlr30xqwksg3k69q4q2rkr52aplujw0esn0qpp5mrqgljk62z20q4nvgr6lzcyn6fhylzccwdvu4k77apg3zmrkujjqdpzw35xjueqd9ejqcfqv3jhxcmjd9c8g6t0dcxqyjw5qcqpjrzjqt56h4gvp5yx36u2uzqa6qwcsk3e2duunfxppzj9vhypc3wfe2wswz607uqq3xqqqsqqqqqqqqqqqlqqyg9qyysgqagx5h20aeulj3gdwx3kxs8u9f4mcakdkwuakasamm9562ffyr9en8yg20lg0ygnr9zpwp68524kmda0t5xp2wytex35pu8hapyjajxqpsql29r"
let content = " Check this out. \nhttps://hidden.tld/\nhttps://damus.io/hidden1.png\n\(invoiceString)\nhttps://damus.io/hidden2.png\nnostr:\(noteId)#hashtag1 #hashtag2 "
let note = try XCTUnwrap(NostrEvent(content: content, keypair: test_keypair))
let parsed: Blocks = parse_note_content(content: .init(note: note, keypair: test_keypair))
let testState = test_damus_state
let noteArtifactsSeparated: NoteArtifactsSeparated = render_blocks(blocks: parsed, profiles: testState.profiles, can_hide_last_previewable_refs: true)
let attributedText: AttributedString = noteArtifactsSeparated.content.attributed
let runs: AttributedString.Runs = attributedText.runs
let runArray: [AttributedString.Runs.Run] = Array(runs)
XCTAssertEqual(runArray.count, 4)
XCTAssertTrue(runArray[0].description.contains("Check this out."))
XCTAssertFalse(runArray[0].description.contains("https://hidden.tld/"))
XCTAssertFalse(runArray[0].description.contains("https://damus.io/hidden1.png"))
XCTAssertFalse(runArray[0].description.contains("lnbc100n:qpsql29r"))
XCTAssertFalse(runArray[0].description.contains("https://damus.io/hidden2.png"))
XCTAssertFalse(runArray[0].description.contains("note1qqq:qqn2l0z3"))
XCTAssertTrue(runArray[1].description.contains("#hashtag1"))
XCTAssertTrue(runArray[3].description.contains("#hashtag2"))
XCTAssertEqual(noteArtifactsSeparated.images.count, 2)
XCTAssertEqual(noteArtifactsSeparated.images[0].absoluteString, "https://damus.io/hidden1.png")
XCTAssertEqual(noteArtifactsSeparated.images[1].absoluteString, "https://damus.io/hidden2.png")
XCTAssertEqual(noteArtifactsSeparated.media.count, 2)
XCTAssertEqual(noteArtifactsSeparated.media[0].url.absoluteString, "https://damus.io/hidden1.png")
XCTAssertEqual(noteArtifactsSeparated.media[1].url.absoluteString, "https://damus.io/hidden2.png")
XCTAssertEqual(noteArtifactsSeparated.links.count, 1)
XCTAssertEqual(noteArtifactsSeparated.links[0].absoluteString, "https://hidden.tld/")
XCTAssertEqual(noteArtifactsSeparated.invoices.count, 1)
XCTAssertEqual(noteArtifactsSeparated.invoices[0].string, invoiceString)
}
/// Based on https://github.com/damus-io/damus/issues/1468
/// Tests whether a note content view correctly parses an image block when url in JSON content contains optional escaped slashes
func testParseImageBlockInContentWithEscapedSlashes() throws {