Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
dddb95e0b3
|
|||
|
bf333e55b8
|
@@ -18,7 +18,7 @@ struct NotificationExtensionState: HeadlessDamusState {
|
||||
let lnurls: LNUrls
|
||||
|
||||
init?() {
|
||||
guard let ndb = Ndb(owns_db_file: false) else { return nil }
|
||||
guard let ndb = try? Ndb(owns_db_file: false) else { return nil }
|
||||
self.ndb = ndb
|
||||
|
||||
guard let keypair = get_saved_keypair() else { return nil }
|
||||
|
||||
@@ -207,7 +207,7 @@
|
||||
4C64987C286D03E000EAE2B3 /* DirectMessagesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C64987B286D03E000EAE2B3 /* DirectMessagesView.swift */; };
|
||||
4C64987E286D082C00EAE2B3 /* DirectMessagesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C64987D286D082C00EAE2B3 /* DirectMessagesModel.swift */; };
|
||||
4C649881286E0EE300EAE2B3 /* secp256k1 in Frameworks */ = {isa = PBXBuildFile; productRef = 4C649880286E0EE300EAE2B3 /* secp256k1 */; };
|
||||
4C684A552A7E91FE005E6031 /* LongPostTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C684A542A7E91FE005E6031 /* LongPostTests.swift */; };
|
||||
4C684A552A7E91FE005E6031 /* LargeEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C684A542A7E91FE005E6031 /* LargeEventTests.swift */; };
|
||||
4C684A572A7FFAE6005E6031 /* UrlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C684A562A7FFAE6005E6031 /* UrlTests.swift */; };
|
||||
4C687C212A5F7ED00092C550 /* DamusBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C687C202A5F7ED00092C550 /* DamusBackground.swift */; };
|
||||
4C687C242A5FA86D0092C550 /* SearchHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C687C232A5FA86D0092C550 /* SearchHeaderView.swift */; };
|
||||
@@ -642,6 +642,7 @@
|
||||
82D6FB6C2CD99F7900C925F4 /* DamusPurpleURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7ADD3DD2B53854300F104C4 /* DamusPurpleURL.swift */; };
|
||||
82D6FB6D2CD99F7900C925F4 /* DamusPurpleEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72341182B6864F200E1E135 /* DamusPurpleEnvironment.swift */; };
|
||||
82D6FB6E2CD99F7900C925F4 /* PurpleStoreKitManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7100C5D2B7709ED00C59298 /* PurpleStoreKitManager.swift */; };
|
||||
82D6FB6F2CD99F7900C925F4 /* CameraService+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA37598F2ABCCEBA0018D73B /* CameraService+Extensions.swift */; };
|
||||
82D6FB702CD99F7900C925F4 /* ImageResizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA3759892ABCCDE30018D73B /* ImageResizer.swift */; };
|
||||
82D6FB712CD99F7900C925F4 /* PhotoCaptureProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA37598B2ABCCE500018D73B /* PhotoCaptureProcessor.swift */; };
|
||||
82D6FB722CD99F7900C925F4 /* VideoCaptureProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA37598C2ABCCE500018D73B /* VideoCaptureProcessor.swift */; };
|
||||
@@ -932,6 +933,7 @@
|
||||
BA37598A2ABCCDE40018D73B /* ImageResizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA3759892ABCCDE30018D73B /* ImageResizer.swift */; };
|
||||
BA37598D2ABCCE500018D73B /* PhotoCaptureProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA37598B2ABCCE500018D73B /* PhotoCaptureProcessor.swift */; };
|
||||
BA37598E2ABCCE500018D73B /* VideoCaptureProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA37598C2ABCCE500018D73B /* VideoCaptureProcessor.swift */; };
|
||||
BA3759922ABCCEBA0018D73B /* CameraService+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA37598F2ABCCEBA0018D73B /* CameraService+Extensions.swift */; };
|
||||
BA3759932ABCCEBA0018D73B /* CameraModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA3759902ABCCEBA0018D73B /* CameraModel.swift */; };
|
||||
BA3759942ABCCEBA0018D73B /* CameraService.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA3759912ABCCEBA0018D73B /* CameraService.swift */; };
|
||||
BA3759972ABCCF360018D73B /* CameraPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA3759962ABCCF360018D73B /* CameraPreview.swift */; };
|
||||
@@ -1207,6 +1209,7 @@
|
||||
D73E5E882C6A97F4007EB227 /* StoreObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74F430B2B23FB9B00425B75 /* StoreObserver.swift */; };
|
||||
D73E5E892C6A97F4007EB227 /* DamusPurpleURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7ADD3DD2B53854300F104C4 /* DamusPurpleURL.swift */; };
|
||||
D73E5E8A2C6A97F4007EB227 /* PurpleStoreKitManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7100C5D2B7709ED00C59298 /* PurpleStoreKitManager.swift */; };
|
||||
D73E5E8D2C6A97F4007EB227 /* CameraService+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA37598F2ABCCEBA0018D73B /* CameraService+Extensions.swift */; };
|
||||
D73E5E8E2C6A97F4007EB227 /* ImageResizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA3759892ABCCDE30018D73B /* ImageResizer.swift */; };
|
||||
D73E5E8F2C6A97F4007EB227 /* PhotoCaptureProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA37598B2ABCCE500018D73B /* PhotoCaptureProcessor.swift */; };
|
||||
D73E5E902C6A97F4007EB227 /* VideoCaptureProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA37598C2ABCCE500018D73B /* VideoCaptureProcessor.swift */; };
|
||||
@@ -2173,7 +2176,7 @@
|
||||
4C64305B2A945AFF00B0C0E9 /* MusicController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MusicController.swift; sourceTree = "<group>"; };
|
||||
4C64987B286D03E000EAE2B3 /* DirectMessagesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DirectMessagesView.swift; sourceTree = "<group>"; };
|
||||
4C64987D286D082C00EAE2B3 /* DirectMessagesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DirectMessagesModel.swift; sourceTree = "<group>"; };
|
||||
4C684A542A7E91FE005E6031 /* LongPostTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongPostTests.swift; sourceTree = "<group>"; };
|
||||
4C684A542A7E91FE005E6031 /* LargeEventTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeEventTests.swift; sourceTree = "<group>"; };
|
||||
4C684A562A7FFAE6005E6031 /* UrlTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UrlTests.swift; sourceTree = "<group>"; };
|
||||
4C687C202A5F7ED00092C550 /* DamusBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusBackground.swift; sourceTree = "<group>"; };
|
||||
4C687C232A5FA86D0092C550 /* SearchHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchHeaderView.swift; sourceTree = "<group>"; };
|
||||
@@ -2439,6 +2442,7 @@
|
||||
BA3759892ABCCDE30018D73B /* ImageResizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageResizer.swift; sourceTree = "<group>"; };
|
||||
BA37598B2ABCCE500018D73B /* PhotoCaptureProcessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoCaptureProcessor.swift; sourceTree = "<group>"; };
|
||||
BA37598C2ABCCE500018D73B /* VideoCaptureProcessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoCaptureProcessor.swift; sourceTree = "<group>"; };
|
||||
BA37598F2ABCCEBA0018D73B /* CameraService+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CameraService+Extensions.swift"; sourceTree = "<group>"; };
|
||||
BA3759902ABCCEBA0018D73B /* CameraModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraModel.swift; sourceTree = "<group>"; };
|
||||
BA3759912ABCCEBA0018D73B /* CameraService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraService.swift; sourceTree = "<group>"; };
|
||||
BA3759962ABCCF360018D73B /* CameraPreview.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraPreview.swift; sourceTree = "<group>"; };
|
||||
@@ -3715,7 +3719,7 @@
|
||||
4C19AE542A5D977400C90DB7 /* HashtagTests.swift */,
|
||||
3AAC7A012A60FE72002B50DF /* LocalizationUtilTests.swift */,
|
||||
D78525242A7B2EA4002FA637 /* NoteContentViewTests.swift */,
|
||||
4C684A542A7E91FE005E6031 /* LongPostTests.swift */,
|
||||
4C684A542A7E91FE005E6031 /* LargeEventTests.swift */,
|
||||
4C684A562A7FFAE6005E6031 /* UrlTests.swift */,
|
||||
D7DEEF2E2A8C021E00E0C99F /* NostrEventTests.swift */,
|
||||
D71DC1EB2A9129C3006E207C /* PostViewTests.swift */,
|
||||
@@ -3889,6 +3893,7 @@
|
||||
children = (
|
||||
BA3759902ABCCEBA0018D73B /* CameraModel.swift */,
|
||||
BA3759912ABCCEBA0018D73B /* CameraService.swift */,
|
||||
BA37598F2ABCCEBA0018D73B /* CameraService+Extensions.swift */,
|
||||
BA3759892ABCCDE30018D73B /* ImageResizer.swift */,
|
||||
BA37598B2ABCCE500018D73B /* PhotoCaptureProcessor.swift */,
|
||||
BA37598C2ABCCE500018D73B /* VideoCaptureProcessor.swift */,
|
||||
@@ -4571,6 +4576,7 @@
|
||||
4C363A8428233689006E126D /* Parser.swift in Sources */,
|
||||
3AAA95CA298DF87B00F3D526 /* TranslationService.swift in Sources */,
|
||||
4CE4F9E328528C5200C00DD9 /* AddRelayView.swift in Sources */,
|
||||
BA3759922ABCCEBA0018D73B /* CameraService+Extensions.swift in Sources */,
|
||||
D74F430C2B23FB9B00425B75 /* StoreObserver.swift in Sources */,
|
||||
4C363A9A28283854006E126D /* Reply.swift in Sources */,
|
||||
BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */,
|
||||
@@ -4972,7 +4978,7 @@
|
||||
D753CEAA2BE9DE04001C3A5D /* MutingTests.swift in Sources */,
|
||||
3A3040F329A91366008A0F29 /* ProfileViewTests.swift in Sources */,
|
||||
4CF0ABDC2981A19E00D66079 /* ListTests.swift in Sources */,
|
||||
4C684A552A7E91FE005E6031 /* LongPostTests.swift in Sources */,
|
||||
4C684A552A7E91FE005E6031 /* LargeEventTests.swift in Sources */,
|
||||
E02B54182B4DFADA0077FF42 /* Bech32ObjectTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -5199,6 +5205,7 @@
|
||||
82D6FB6C2CD99F7900C925F4 /* DamusPurpleURL.swift in Sources */,
|
||||
82D6FB6D2CD99F7900C925F4 /* DamusPurpleEnvironment.swift in Sources */,
|
||||
82D6FB6E2CD99F7900C925F4 /* PurpleStoreKitManager.swift in Sources */,
|
||||
82D6FB6F2CD99F7900C925F4 /* CameraService+Extensions.swift in Sources */,
|
||||
82D6FB702CD99F7900C925F4 /* ImageResizer.swift in Sources */,
|
||||
82D6FB712CD99F7900C925F4 /* PhotoCaptureProcessor.swift in Sources */,
|
||||
82D6FB722CD99F7900C925F4 /* VideoCaptureProcessor.swift in Sources */,
|
||||
@@ -5589,6 +5596,7 @@
|
||||
D73E5E882C6A97F4007EB227 /* StoreObserver.swift in Sources */,
|
||||
D73E5E892C6A97F4007EB227 /* DamusPurpleURL.swift in Sources */,
|
||||
D73E5E8A2C6A97F4007EB227 /* PurpleStoreKitManager.swift in Sources */,
|
||||
D73E5E8D2C6A97F4007EB227 /* CameraService+Extensions.swift in Sources */,
|
||||
D73E5E8E2C6A97F4007EB227 /* ImageResizer.swift in Sources */,
|
||||
D78F080E2D7F78EF00FC6C75 /* Request.swift in Sources */,
|
||||
D73E5E8F2C6A97F4007EB227 /* PhotoCaptureProcessor.swift in Sources */,
|
||||
@@ -6846,7 +6854,7 @@
|
||||
repositoryURL = "https://github.com/tyiu/EmojiPicker.git";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 0.2.0;
|
||||
minimumVersion = 0.1.1;
|
||||
};
|
||||
};
|
||||
4C06670228FC7EC500038D2A /* XCRemoteSwiftPackageReference "Kingfisher" */ = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"originHash" : "06318d35ee2e6bd681b95591e67da33a9461b48a3c652e58bd9d1a6f0d82bdac",
|
||||
"originHash" : "085cf0f645323bf77edb52886489bf77b309a0a2d2b78a54beaf8520b540d596",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "codescanner",
|
||||
@@ -22,8 +22,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/tyiu/EmojiKit",
|
||||
"state" : {
|
||||
"revision" : "47a4b1402de26be0299dcb4d667c1faaf21a7874",
|
||||
"version" : "0.2.0"
|
||||
"revision" : "05805f72d63a6d6a2d7dc7fe14abd37c1317b11a",
|
||||
"version" : "0.1.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -31,8 +31,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/tyiu/EmojiPicker.git",
|
||||
"state" : {
|
||||
"revision" : "3f48903721eae223238ff0af17c22d6373d33813",
|
||||
"version" : "0.2.0"
|
||||
"revision" : "0c28b4a1a6b8840cf2580bda59517f6d0a733dc8",
|
||||
"version" : "0.1.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
|
Before Width: | Height: | Size: 216 B After Width: | Height: | Size: 216 B |
@@ -94,12 +94,12 @@ struct SelectableText: View {
|
||||
case show_mute_word_view(highlighted_text: String)
|
||||
|
||||
func should_show_highlight_post_view() -> Bool {
|
||||
guard case .show_highlight_post_view = self else { return false }
|
||||
guard case .show_highlight_post_view(let highlighted_text) = self else { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
func should_show_mute_word_view() -> Bool {
|
||||
guard case .show_mute_word_view = self else { return false }
|
||||
guard case .show_mute_word_view(let highlighted_text) = self else { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -119,23 +119,16 @@ struct SelectableText: View {
|
||||
fileprivate class TextView: UITextView {
|
||||
var postHighlight: (String) -> Void
|
||||
var muteWord: (String) -> Void
|
||||
private let enableHighlighting: Bool
|
||||
|
||||
init(frame: CGRect, textContainer: NSTextContainer?, postHighlight: @escaping (String) -> Void, muteWord: @escaping (String) -> Void, enableHighlighting: Bool) {
|
||||
init(frame: CGRect, textContainer: NSTextContainer?, postHighlight: @escaping (String) -> Void, muteWord: @escaping (String) -> Void) {
|
||||
self.postHighlight = postHighlight
|
||||
self.muteWord = muteWord
|
||||
self.enableHighlighting = enableHighlighting
|
||||
|
||||
super.init(frame: frame, textContainer: textContainer)
|
||||
|
||||
if enableHighlighting {
|
||||
self.delegate = self
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
|
||||
if action == #selector(highlightText(_:)) {
|
||||
@@ -149,44 +142,23 @@ fileprivate class TextView: UITextView {
|
||||
return super.canPerformAction(action, withSender: sender)
|
||||
}
|
||||
|
||||
private func getSelectedText() -> String? {
|
||||
func getSelectedText() -> String? {
|
||||
guard let selectedRange = self.selectedTextRange else { return nil }
|
||||
return self.text(in: selectedRange)
|
||||
}
|
||||
|
||||
@objc private func highlightText(_ sender: Any?) {
|
||||
@objc public func highlightText(_ sender: Any?) {
|
||||
guard let selectedText = self.getSelectedText() else { return }
|
||||
self.postHighlight(selectedText)
|
||||
}
|
||||
|
||||
@objc private func muteText(_ sender: Any?) {
|
||||
@objc public func muteText(_ sender: Any?) {
|
||||
guard let selectedText = self.getSelectedText() else { return }
|
||||
self.muteWord(selectedText)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension TextView: UITextViewDelegate {
|
||||
func textView(_ textView: UITextView, editMenuForTextIn range: NSRange, suggestedActions: [UIMenuElement]) -> UIMenu? {
|
||||
guard enableHighlighting,
|
||||
let selectedTextRange = self.selectedTextRange,
|
||||
let selectedText = self.text(in: selectedTextRange),
|
||||
!selectedText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let highlightAction = UIAction(title: NSLocalizedString("Highlight", comment: "Context menu action to highlight the selected text as context to draft a new note."), image: UIImage(systemName: "highlighter")) { [weak self] _ in
|
||||
self?.postHighlight(selectedText)
|
||||
}
|
||||
|
||||
let muteAction = UIAction(title: NSLocalizedString("Mute", comment: "Context menu action to mute the selected word."), image: UIImage(systemName: "speaker.slash")) { [weak self] _ in
|
||||
self?.muteWord(selectedText)
|
||||
}
|
||||
|
||||
return UIMenu(children: suggestedActions + [highlightAction, muteAction])
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate struct TextViewRepresentable: UIViewRepresentable {
|
||||
|
||||
let attributedString: AttributedString
|
||||
@@ -200,7 +172,7 @@ fileprivate struct TextViewRepresentable: UIViewRepresentable {
|
||||
@Binding var height: CGFloat
|
||||
|
||||
func makeUIView(context: UIViewRepresentableContext<Self>) -> TextView {
|
||||
let view = TextView(frame: .zero, textContainer: nil, postHighlight: postHighlight, muteWord: muteWord, enableHighlighting: enableHighlighting)
|
||||
let view = TextView(frame: .zero, textContainer: nil, postHighlight: postHighlight, muteWord: muteWord)
|
||||
view.isEditable = false
|
||||
view.dataDetectorTypes = .all
|
||||
view.isSelectable = true
|
||||
@@ -211,6 +183,11 @@ fileprivate struct TextViewRepresentable: UIViewRepresentable {
|
||||
view.textContainerInset.right = 1.0
|
||||
view.textAlignment = textAlignment
|
||||
|
||||
let menuController = UIMenuController.shared
|
||||
let highlightItem = UIMenuItem(title: "Highlight", action: #selector(view.highlightText(_:)))
|
||||
let muteItem = UIMenuItem(title: "Mute", action: #selector(view.muteText(_:)))
|
||||
menuController.menuItems = self.enableHighlighting ? [highlightItem, muteItem] : []
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
|
||||
@@ -632,7 +632,7 @@ struct ContentView: View {
|
||||
|
||||
func handleNotification(notification: LossyLocalNotification) {
|
||||
Log.info("ContentView is handling a notification", for: .push_notifications)
|
||||
guard damus_state != nil else {
|
||||
guard let damus_state else {
|
||||
// This should never happen because `listenAndHandleLocalNotifications` is called after damus state is initialized in `onAppear`
|
||||
assertionFailure("DamusState not loaded when ContentView (new handler) was handling a notification")
|
||||
Log.error("DamusState not loaded when ContentView (new handler) was handling a notification", for: .push_notifications)
|
||||
@@ -1044,7 +1044,7 @@ func find_event_with_subid(state: DamusState, query query_: FindEvent, subid: St
|
||||
/// - naddr: the `naddr` address
|
||||
/// - callback: A function to handle the found event
|
||||
func naddrLookup(damus_state: DamusState, naddr: NAddr, callback: @escaping (NostrEvent?) -> ()) {
|
||||
let nostrKinds: [NostrKind]? = NostrKind(rawValue: naddr.kind).map { [$0] }
|
||||
var nostrKinds: [NostrKind]? = NostrKind(rawValue: naddr.kind).map { [$0] }
|
||||
|
||||
let filter = NostrFilter(kinds: nostrKinds, authors: [naddr.author])
|
||||
|
||||
@@ -1216,7 +1216,7 @@ extension LossyLocalNotification {
|
||||
case .nprofile(let nProfile):
|
||||
// TODO: Improve this by implementing a profile route that handles nprofiles with their relay hints.
|
||||
return .route(.ProfileByKey(pubkey: nProfile.author))
|
||||
case .nrelay:
|
||||
case .nrelay(let string):
|
||||
// We do not need to implement `nrelay` support, it has been deprecated.
|
||||
// See https://github.com/nostr-protocol/nips/blob/6e7a618e7f873bb91e743caacc3b09edab7796a0/BREAKING.md?plain=1#L21
|
||||
return .sheet(.error(ErrorView.UserPresentableError(
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// CameraService+Extensions.swift
|
||||
// damus
|
||||
//
|
||||
// Created by Suhail Saqan on 8/5/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
|
||||
extension AVCaptureVideoOrientation {
|
||||
init?(deviceOrientation: UIDeviceOrientation) {
|
||||
switch deviceOrientation {
|
||||
case .portrait: self = .portrait
|
||||
case .portraitUpsideDown: self = .portraitUpsideDown
|
||||
case .landscapeLeft: self = .landscapeRight
|
||||
case .landscapeRight: self = .landscapeLeft
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
|
||||
init?(interfaceOrientation: UIInterfaceOrientation) {
|
||||
switch interfaceOrientation {
|
||||
case .portrait: self = .portrait
|
||||
case .portraitUpsideDown: self = .portraitUpsideDown
|
||||
case .landscapeLeft: self = .landscapeLeft
|
||||
case .landscapeRight: self = .landscapeRight
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-10
@@ -47,16 +47,16 @@ enum MuteItem: Hashable, Equatable {
|
||||
// rhs is the item we want to check against (ie. the item in the mute list)
|
||||
|
||||
switch (lhs, rhs) {
|
||||
case (.user(let lhs_pubkey, _), .user(let rhs_pubkey, _)):
|
||||
return lhs_pubkey == rhs_pubkey && !rhs.is_expired()
|
||||
case (.hashtag(let lhs_hashtag, _), .hashtag(let rhs_hashtag, _)):
|
||||
return lhs_hashtag == rhs_hashtag && !rhs.is_expired()
|
||||
case (.word(let lhs_word, _), .word(let rhs_word, _)):
|
||||
return lhs_word == rhs_word && !rhs.is_expired()
|
||||
case (.thread(let lhs_thread, _), .thread(let rhs_thread, _)):
|
||||
return lhs_thread == rhs_thread && !rhs.is_expired()
|
||||
default:
|
||||
return false
|
||||
case (.user(let lhs_pubkey, _), .user(let rhs_pubkey, let rhs_expiration_date)):
|
||||
return lhs_pubkey == rhs_pubkey && !rhs.is_expired()
|
||||
case (.hashtag(let lhs_hashtag, _), .hashtag(let rhs_hashtag, let rhs_expiration_date)):
|
||||
return lhs_hashtag == rhs_hashtag && !rhs.is_expired()
|
||||
case (.word(let lhs_word, _), .word(let rhs_word, let rhs_expiration_date)):
|
||||
return lhs_word == rhs_word && !rhs.is_expired()
|
||||
case (.thread(let lhs_thread, _), .thread(let rhs_thread, let rhs_expiration_date)):
|
||||
return lhs_thread == rhs_thread && !rhs.is_expired()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,10 @@ func should_display_notification(state: HeadlessDamusState, event ev: NostrEvent
|
||||
return false
|
||||
}
|
||||
|
||||
if state.settings.hellthread_notifications_disabled && ev.is_hellthread(max_pubkeys: state.settings.hellthread_notification_max_pubkeys) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Don't show notifications that match mute list.
|
||||
if state.mutelist_manager.is_event_muted(ev) {
|
||||
return false
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
// Minimum threshold the hellthread pubkey tag count setting can go down to.
|
||||
let HELLTHREAD_MIN_PUBKEYS: Int = 6
|
||||
|
||||
// Maximum threshold the hellthread pubkey tag count setting can go up to.
|
||||
let HELLTHREAD_MAX_PUBKEYS: Int = 24
|
||||
|
||||
struct PushNotificationClient {
|
||||
let keypair: Keypair
|
||||
let settings: UserSettingsStore
|
||||
@@ -175,15 +181,33 @@ extension PushNotificationClient {
|
||||
}
|
||||
|
||||
struct NotificationSettings: Codable, Equatable {
|
||||
let zap_notifications_enabled: Bool
|
||||
let mention_notifications_enabled: Bool
|
||||
let repost_notifications_enabled: Bool
|
||||
let reaction_notifications_enabled: Bool
|
||||
let dm_notifications_enabled: Bool
|
||||
let only_notifications_from_following_enabled: Bool
|
||||
|
||||
let zap_notifications_enabled: Bool?
|
||||
let mention_notifications_enabled: Bool?
|
||||
let repost_notifications_enabled: Bool?
|
||||
let reaction_notifications_enabled: Bool?
|
||||
let dm_notifications_enabled: Bool?
|
||||
let only_notifications_from_following_enabled: Bool?
|
||||
let hellthread_notifications_disabled: Bool?
|
||||
let hellthread_notifications_max_pubkeys: Int?
|
||||
|
||||
static func from(json_data: Data) -> Self? {
|
||||
guard let decoded = try? JSONDecoder().decode(Self.self, from: json_data) else { return nil }
|
||||
|
||||
// Normalize hellthread_notifications_max_pubkeys in case
|
||||
// it goes beyond the expected range supported on the client.
|
||||
if let max_pubkeys = decoded.hellthread_notifications_max_pubkeys, max_pubkeys < HELLTHREAD_MIN_PUBKEYS || max_pubkeys > HELLTHREAD_MAX_PUBKEYS {
|
||||
return NotificationSettings(
|
||||
zap_notifications_enabled: decoded.zap_notifications_enabled,
|
||||
mention_notifications_enabled: decoded.mention_notifications_enabled,
|
||||
repost_notifications_enabled: decoded.repost_notifications_enabled,
|
||||
reaction_notifications_enabled: decoded.reaction_notifications_enabled,
|
||||
dm_notifications_enabled: decoded.dm_notifications_enabled,
|
||||
only_notifications_from_following_enabled: decoded.only_notifications_from_following_enabled,
|
||||
hellthread_notifications_disabled: decoded.hellthread_notifications_disabled,
|
||||
hellthread_notifications_max_pubkeys: max(min(HELLTHREAD_MAX_PUBKEYS, max_pubkeys), HELLTHREAD_MIN_PUBKEYS)
|
||||
)
|
||||
}
|
||||
|
||||
return decoded
|
||||
}
|
||||
|
||||
@@ -194,7 +218,9 @@ extension PushNotificationClient {
|
||||
repost_notifications_enabled: settings.repost_notification,
|
||||
reaction_notifications_enabled: settings.like_notification,
|
||||
dm_notifications_enabled: settings.dm_notification,
|
||||
only_notifications_from_following_enabled: settings.notification_only_from_following
|
||||
only_notifications_from_following_enabled: settings.notification_only_from_following,
|
||||
hellthread_notifications_disabled: settings.hellthread_notifications_disabled,
|
||||
hellthread_notifications_max_pubkeys: settings.hellthread_notification_max_pubkeys
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -160,7 +160,13 @@ class UserSettingsStore: ObservableObject {
|
||||
|
||||
@Setting(key: "notification_only_from_following", default_value: false)
|
||||
var notification_only_from_following: Bool
|
||||
|
||||
|
||||
@Setting(key: "hellthread_notifications_disabled", default_value: false)
|
||||
var hellthread_notifications_disabled: Bool
|
||||
|
||||
@Setting(key: "hellthread_notification_max_pubkeys", default_value: DEFAULT_HELLTHREAD_MAX_PUBKEYS)
|
||||
var hellthread_notification_max_pubkeys: Int
|
||||
|
||||
@Setting(key: "translate_dms", default_value: false)
|
||||
var translate_dms: Bool
|
||||
|
||||
|
||||
@@ -209,7 +209,7 @@ enum Route: Hashable {
|
||||
case .Search(let search):
|
||||
hasher.combine("search")
|
||||
hasher.combine(search.search)
|
||||
case .NDBSearch:
|
||||
case .NDBSearch(let results):
|
||||
hasher.combine("results")
|
||||
case .EULA:
|
||||
hasher.combine("eula")
|
||||
|
||||
@@ -74,7 +74,7 @@ class LoadableNostrEventViewModel: ObservableObject {
|
||||
case .zap, .zap_request:
|
||||
guard let zap = await get_zap(from: ev, state: damus_state) else { return .not_found }
|
||||
return .loaded(route: Route.Zaps(target: zap.target))
|
||||
case .contacts, .metadata, .delete, .boost, .chat, .mute_list, .list_deprecated, .draft, .longform, .nwc_request, .nwc_response, .http_auth, .status:
|
||||
case .contacts, .metadata, .delete, .boost, .chat, .mute_list, .list_deprecated, .draft, .longform, .zap, .zap_request, .nwc_request, .nwc_response, .http_auth, .status:
|
||||
return .unknown_or_unsupported_kind
|
||||
}
|
||||
case .naddr(let naddr):
|
||||
|
||||
@@ -86,10 +86,10 @@ struct DamusAppNotificationView: View {
|
||||
Task {
|
||||
do {
|
||||
let url = try await damus_state.purple.generate_verified_ln_checkout_link(product_template_name: product_template_name)
|
||||
self.open_url(url: url)
|
||||
await self.open_url(url: url)
|
||||
}
|
||||
catch {
|
||||
self.open_url(url: damus_state.purple.environment.purple_landing_page_url().appendingPathComponent("checkout"))
|
||||
await self.open_url(url: damus_state.purple.environment.purple_landing_page_url().appendingPathComponent("checkout"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,15 +9,27 @@ import SwiftUI
|
||||
|
||||
class NotificationFilter: ObservableObject, Equatable {
|
||||
@Published var state: NotificationFilterState
|
||||
@Published var fine_filter: FriendFilter
|
||||
|
||||
@Published var friend_filter: FriendFilter
|
||||
@Published var hellthread_notifications_disabled: Bool
|
||||
@Published var hellthread_notification_max_pubkeys: Int
|
||||
|
||||
static func == (lhs: NotificationFilter, rhs: NotificationFilter) -> Bool {
|
||||
return lhs.state == rhs.state && lhs.fine_filter == rhs.fine_filter
|
||||
return lhs.state == rhs.state
|
||||
&& lhs.friend_filter == rhs.friend_filter
|
||||
&& lhs.hellthread_notifications_disabled == rhs.hellthread_notifications_disabled
|
||||
&& lhs.hellthread_notification_max_pubkeys == rhs.hellthread_notification_max_pubkeys
|
||||
}
|
||||
|
||||
init(state: NotificationFilterState = .all, fine_filter: FriendFilter = .all) {
|
||||
init(
|
||||
state: NotificationFilterState = .all,
|
||||
friend_filter: FriendFilter = .all,
|
||||
hellthread_notifications_disabled: Bool = false,
|
||||
hellthread_notification_max_pubkeys: Int = DEFAULT_HELLTHREAD_MAX_PUBKEYS
|
||||
) {
|
||||
self.state = state
|
||||
self.fine_filter = fine_filter
|
||||
self.friend_filter = friend_filter
|
||||
self.hellthread_notifications_disabled = hellthread_notifications_disabled
|
||||
self.hellthread_notification_max_pubkeys = hellthread_notification_max_pubkeys
|
||||
}
|
||||
|
||||
func filter(contacts: Contacts, items: [NotificationItem]) -> [NotificationItem] {
|
||||
@@ -26,8 +38,11 @@ class NotificationFilter: ObservableObject, Equatable {
|
||||
if !self.state.filter(item) {
|
||||
return
|
||||
}
|
||||
|
||||
if let item = item.filter({ self.fine_filter.filter(contacts: contacts, pubkey: $0.pubkey) }) {
|
||||
|
||||
if let item = item.filter({ ev in
|
||||
self.friend_filter.filter(contacts: contacts, pubkey: ev.pubkey) &&
|
||||
(!hellthread_notifications_disabled || !ev.is_hellthread(max_pubkeys: hellthread_notification_max_pubkeys))
|
||||
}) {
|
||||
acc.append(item)
|
||||
}
|
||||
}
|
||||
@@ -65,7 +80,9 @@ struct NotificationsView: View {
|
||||
NotificationTab(
|
||||
NotificationFilter(
|
||||
state: .all,
|
||||
fine_filter: filter.fine_filter
|
||||
friend_filter: filter.friend_filter,
|
||||
hellthread_notifications_disabled: state.settings.hellthread_notifications_disabled,
|
||||
hellthread_notification_max_pubkeys: state.settings.hellthread_notification_max_pubkeys
|
||||
)
|
||||
)
|
||||
.tag(NotificationFilterState.all)
|
||||
@@ -73,7 +90,9 @@ struct NotificationsView: View {
|
||||
NotificationTab(
|
||||
NotificationFilter(
|
||||
state: .zaps,
|
||||
fine_filter: filter.fine_filter
|
||||
friend_filter: filter.friend_filter,
|
||||
hellthread_notifications_disabled: state.settings.hellthread_notifications_disabled,
|
||||
hellthread_notification_max_pubkeys: state.settings.hellthread_notification_max_pubkeys
|
||||
)
|
||||
)
|
||||
.tag(NotificationFilterState.zaps)
|
||||
@@ -81,7 +100,9 @@ struct NotificationsView: View {
|
||||
NotificationTab(
|
||||
NotificationFilter(
|
||||
state: .replies,
|
||||
fine_filter: filter.fine_filter
|
||||
friend_filter: filter.friend_filter,
|
||||
hellthread_notifications_disabled: state.settings.hellthread_notifications_disabled,
|
||||
hellthread_notification_max_pubkeys: state.settings.hellthread_notification_max_pubkeys
|
||||
)
|
||||
)
|
||||
.tag(NotificationFilterState.replies)
|
||||
@@ -98,20 +119,20 @@ struct NotificationsView: View {
|
||||
}
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
if would_filter_non_friends_from_notifications(contacts: state.contacts, state: filter_state, items: self.notifications.notifications) {
|
||||
FriendsButton(filter: $filter.fine_filter)
|
||||
FriendsButton(filter: $filter.friend_filter)
|
||||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: filter.fine_filter) { val in
|
||||
.onChange(of: filter.friend_filter) { val in
|
||||
state.settings.friend_filter = val
|
||||
self.subtitle = filter.fine_filter.description()
|
||||
self.subtitle = filter.friend_filter.description()
|
||||
}
|
||||
.onChange(of: filter_state) { val in
|
||||
filter.state = val
|
||||
}
|
||||
.onAppear {
|
||||
self.filter.fine_filter = state.settings.friend_filter
|
||||
self.subtitle = filter.fine_filter.description()
|
||||
self.filter.friend_filter = state.settings.friend_filter
|
||||
self.subtitle = filter.friend_filter.description()
|
||||
filter.state = filter_state
|
||||
}
|
||||
.safeAreaInset(edge: .top, spacing: 0) {
|
||||
|
||||
@@ -763,7 +763,7 @@ func load_draft_for_post(drafts: Drafts, action: PostAction) -> DraftArtifacts?
|
||||
}
|
||||
// If there are no exact matches to the highlight, try to load a draft for the same highlight source
|
||||
// We do this to improve UX, because we don't want to leave the post view blank if they only selected a slightly different piece of text from before.
|
||||
let other_matches = drafts.highlights
|
||||
var other_matches = drafts.highlights
|
||||
.filter { $0.key.source == highlight.source }
|
||||
// It's not an exact match, so there is no way of telling which one is the preferred draft. So just load the first one we found.
|
||||
return other_matches.first?.value
|
||||
|
||||
@@ -287,7 +287,7 @@ struct EditPictureControl: View {
|
||||
|
||||
var accessibility_value: String? {
|
||||
if style.first_time_setup {
|
||||
if model.current_image_url != nil {
|
||||
if let current_image_url = model.current_image_url {
|
||||
switch self.model.context {
|
||||
case .normal:
|
||||
return NSLocalizedString("Image is setup", comment: "Accessibility value on image control")
|
||||
|
||||
@@ -297,7 +297,7 @@ fileprivate struct ProfileActionSheetZapButton: View {
|
||||
.foregroundColor(Color.primary)
|
||||
.profile_button_style(scheme: colorScheme)
|
||||
case .zap_success:
|
||||
Image("checkmark-damus")
|
||||
Image("checkmark")
|
||||
.foregroundColor(Color.green)
|
||||
.profile_button_style(scheme: colorScheme)
|
||||
case .zap_failure:
|
||||
|
||||
@@ -57,14 +57,11 @@ struct QRScanNSECView: View {
|
||||
}
|
||||
}
|
||||
|
||||
struct QRScanNSECView_Previews: PreviewProvider {
|
||||
@State static var showQR = true
|
||||
@State static var privKeyFound = false
|
||||
@State static var shouldSaveKey = true
|
||||
|
||||
static var previews: some View {
|
||||
QRScanNSECView(showQR: $showQR,
|
||||
privKeyFound: $privKeyFound,
|
||||
codeScannerCompletion: { _ in })
|
||||
}
|
||||
#Preview {
|
||||
@State var showQR = true
|
||||
@State var privKeyFound = false
|
||||
@State var shouldSaveKey = true
|
||||
return QRScanNSECView(showQR: $showQR,
|
||||
privKeyFound: $privKeyFound,
|
||||
codeScannerCompletion: { _ in })
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ struct NotificationSettingsView: View {
|
||||
@State var notification_preferences_sync_state: PreferencesSyncState = .undefined
|
||||
|
||||
@Environment(\.dismiss) var dismiss
|
||||
|
||||
|
||||
func indicator_binding(_ val: NewEventsBits) -> Binding<Bool> {
|
||||
return Binding.init(get: {
|
||||
(settings.notification_indicators & val.rawValue) > 0
|
||||
@@ -28,7 +28,15 @@ struct NotificationSettingsView: View {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
var hellthread_notification_max_pubkeys_binding: Binding<Double> {
|
||||
Binding<Double>(get: {
|
||||
return Double(settings.hellthread_notification_max_pubkeys)
|
||||
}, set: {
|
||||
settings.hellthread_notification_max_pubkeys = Int($0)
|
||||
})
|
||||
}
|
||||
|
||||
func try_to_set_notifications_mode(new_value: UserSettingsStore.NotificationsMode) {
|
||||
notification_mode_setting_error = nil
|
||||
if new_value == .push {
|
||||
@@ -111,7 +119,24 @@ struct NotificationSettingsView: View {
|
||||
}
|
||||
|
||||
// MARK: - View layout
|
||||
|
||||
|
||||
func hellthread_notification_settings_text() -> String {
|
||||
if !settings.hellthread_notifications_disabled {
|
||||
return NSLocalizedString("Hide notifications that tag many profiles", comment: "Label for notification settings toggle that hides notifications that tag many people.")
|
||||
}
|
||||
return pluralizedString(key: "hellthread_notifications_disabled", count: $settings.hellthread_notification_max_pubkeys.wrappedValue)
|
||||
}
|
||||
|
||||
var hellthread_notifications_max_pubkeys_view: some View {
|
||||
VStack(alignment: .leading) {
|
||||
Slider(
|
||||
value: self.notification_preference_binding(hellthread_notification_max_pubkeys_binding),
|
||||
in: Double(HELLTHREAD_MIN_PUBKEYS)...Double(HELLTHREAD_MAX_PUBKEYS),
|
||||
step: 1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
if settings.enable_push_notifications {
|
||||
@@ -175,6 +200,13 @@ struct NotificationSettingsView: View {
|
||||
.toggleStyle(.switch)
|
||||
Toggle(NSLocalizedString("Show only from users you follow", comment: "Setting to Show notifications only associated to users your follow"), isOn: self.notification_preference_binding($settings.notification_only_from_following))
|
||||
.toggleStyle(.switch)
|
||||
VStack {
|
||||
Toggle(hellthread_notification_settings_text(), isOn: self.notification_preference_binding($settings.hellthread_notifications_disabled))
|
||||
.toggleStyle(.switch)
|
||||
if settings.hellthread_notifications_disabled {
|
||||
hellthread_notifications_max_pubkeys_view
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Section(
|
||||
|
||||
@@ -50,6 +50,22 @@
|
||||
<string>Following</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>hellthread_notifications_disabled</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%#@HELLTHREAD_PROFILES@</string>
|
||||
<key>HELLTHREAD_PROFILES</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>one</key>
|
||||
<string>Hide notifications that tag more than %d profile</string>
|
||||
<key>other</key>
|
||||
<string>Hide notifications that tag more than %d profiles</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>imports_count</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
@@ -82,6 +98,22 @@
|
||||
<string>%2$@ and %1$d others reposted</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>quoted_reposts_count</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%#@QUOTE_REPOSTS@</string>
|
||||
<key>QUOTE_REPOSTS</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>one</key>
|
||||
<string>Quote</string>
|
||||
<key>other</key>
|
||||
<string>Quotes</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>reacted_tagged_in_3</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
@@ -242,22 +274,6 @@
|
||||
<string>Reposts</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>quoted_reposts_count</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%#@QUOTE_REPOSTS@</string>
|
||||
<key>QUOTE_REPOSTS</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>one</key>
|
||||
<string>Quote</string>
|
||||
<key>other</key>
|
||||
<string>Quotes</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>sats</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// LargeEventTests.swift
|
||||
// damusTests
|
||||
//
|
||||
// Created by William Casarin on 2023-08-05.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import damus
|
||||
|
||||
final class LargeEventTests: XCTestCase {
|
||||
|
||||
func testLongPost() throws {
|
||||
let json = "[\"EVENT\",\"subid\",\(test_failing_nostr_report)]"
|
||||
let resp = NostrResponse.owned_from_json(json: json)
|
||||
|
||||
XCTAssertNotNil(resp)
|
||||
guard let resp,
|
||||
case .event(let subid, let ev) = resp
|
||||
else {
|
||||
XCTAssertFalse(true)
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertEqual(subid, "subid")
|
||||
XCTAssertTrue(ev.should_show_event)
|
||||
XCTAssertTrue(!ev.too_big)
|
||||
XCTAssertTrue(should_show_event(state: test_damus_state, ev: ev))
|
||||
XCTAssertTrue(validate_event(ev: ev) == .ok)
|
||||
}
|
||||
|
||||
func testIsHellthread() throws {
|
||||
let json = "[\"EVENT\",\"subid\",\(test_failing_nostr_report)]"
|
||||
let resp = NostrResponse.owned_from_json(json: json)
|
||||
|
||||
XCTAssertNotNil(resp)
|
||||
guard let resp,
|
||||
case .event(let subid, let ev) = resp
|
||||
else {
|
||||
XCTAssertFalse(true)
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertEqual(subid, "subid")
|
||||
XCTAssertTrue(ev.should_show_event)
|
||||
XCTAssertTrue(ev.is_hellthread(max_pubkeys: 10))
|
||||
XCTAssertTrue(validate_event(ev: ev) == .ok)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,13 +17,16 @@ final class LocalizationUtilTests: XCTestCase {
|
||||
let keys = [
|
||||
["followers_count", "Followers", "Follower", "Followers"],
|
||||
["following_count", "Following", "Following", "Following"],
|
||||
["hellthread_notifications_disabled", "Hide notifications that tag more than 0 profiles", "Hide notifications that tag more than 1 profile", "Hide notifications that tag more than 2 profiles"],
|
||||
["imports_count", "Imports", "Import", "Imports"],
|
||||
["quoted_reposts_count", "Quotes", "Quote", "Quotes"],
|
||||
["reactions_count", "Reactions", "Reaction", "Reactions"],
|
||||
["relays_count", "Relays", "Relay", "Relays"],
|
||||
["reposts_count", "Reposts", "Repost", "Reposts"],
|
||||
["sats", "sats", "sat", "sats"],
|
||||
["zaps_count", "Zaps", "Zap", "Zaps"],
|
||||
["word_count", "0 Words", "1 Word", "2 Words"]
|
||||
["users_talking_about_it", "0 users talking about it", "1 user talking about it", "2 users talking about it"],
|
||||
["word_count", "0 Words", "1 Word", "2 Words"],
|
||||
["zaps_count", "Zaps", "Zap", "Zaps"]
|
||||
]
|
||||
|
||||
for key in keys {
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
//
|
||||
// LongPostTests.swift
|
||||
// damusTests
|
||||
//
|
||||
// Created by William Casarin on 2023-08-05.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import damus
|
||||
|
||||
final class LongPostTests: XCTestCase {
|
||||
|
||||
override func setUpWithError() throws {
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
override func tearDownWithError() throws {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
func testLongPost() throws {
|
||||
let contacts = Contacts(our_pubkey: test_keypair.pubkey)
|
||||
let json = "[\"EVENT\",\"subid\",\(test_failing_nostr_report)]"
|
||||
let resp = NostrResponse.owned_from_json(json: json)
|
||||
|
||||
XCTAssertNotNil(resp)
|
||||
guard let resp,
|
||||
case .event(let subid, let ev) = resp
|
||||
else {
|
||||
XCTAssertFalse(true)
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertEqual(subid, "subid")
|
||||
XCTAssertTrue(ev.should_show_event)
|
||||
XCTAssertTrue(!ev.too_big)
|
||||
XCTAssertTrue(should_show_event(state: test_damus_state, ev: ev))
|
||||
XCTAssertTrue(validate_event(ev: ev) == .ok )
|
||||
}
|
||||
|
||||
func testPerformanceExample() throws {
|
||||
// This is an example of a performance test case.
|
||||
self.measure {
|
||||
// Put the code you want to measure the time of here.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,6 +14,9 @@ import CryptoKit
|
||||
|
||||
let MAX_NOTE_SIZE: Int = 2 << 18
|
||||
|
||||
// Default threshold of the hellthread pubkey tag count setting if it is not set.
|
||||
let DEFAULT_HELLTHREAD_MAX_PUBKEYS: Int = 10
|
||||
|
||||
struct NdbStr {
|
||||
let note: NdbNote
|
||||
let str: UnsafePointer<CChar>
|
||||
@@ -299,6 +302,15 @@ extension NdbNote {
|
||||
return !too_big
|
||||
}
|
||||
|
||||
func is_hellthread(max_pubkeys: Int) -> Bool {
|
||||
switch known_kind {
|
||||
case .text, .boost, .like, .zap:
|
||||
Set(referenced_pubkeys).count > max_pubkeys
|
||||
default:
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
func get_blocks(keypair: Keypair) -> Blocks {
|
||||
return parse_note_content(content: .init(note: self, keypair: keypair))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user