Compare commits

...

2 Commits

Author SHA1 Message Date
dc2c714192 Replace deprecated usage of UIMenuController with UITextViewDelegate
Signed-off-by: Terry Yiu <git@tyiu.xyz>
2025-04-12 18:17:36 -04:00
c4ed5ff6bc Fix some compiler warnings
Signed-off-by: Terry Yiu <git@tyiu.xyz>
2025-04-12 18:08:58 -04:00
15 changed files with 68 additions and 82 deletions

View File

@@ -18,7 +18,7 @@ struct NotificationExtensionState: HeadlessDamusState {
let lnurls: LNUrls
init?() {
guard let ndb = try? Ndb(owns_db_file: false) else { return nil }
guard let ndb = Ndb(owns_db_file: false) else { return nil }
self.ndb = ndb
guard let keypair = get_saved_keypair() else { return nil }

View File

@@ -642,7 +642,6 @@
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 */; };
@@ -933,7 +932,6 @@
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 */; };
@@ -1209,7 +1207,6 @@
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 */; };
@@ -2442,7 +2439,6 @@
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>"; };
@@ -3893,7 +3889,6 @@
children = (
BA3759902ABCCEBA0018D73B /* CameraModel.swift */,
BA3759912ABCCEBA0018D73B /* CameraService.swift */,
BA37598F2ABCCEBA0018D73B /* CameraService+Extensions.swift */,
BA3759892ABCCDE30018D73B /* ImageResizer.swift */,
BA37598B2ABCCE500018D73B /* PhotoCaptureProcessor.swift */,
BA37598C2ABCCE500018D73B /* VideoCaptureProcessor.swift */,
@@ -4576,7 +4571,6 @@
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 */,
@@ -5205,7 +5199,6 @@
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 */,
@@ -5596,7 +5589,6 @@
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 */,

View File

Before

Width:  |  Height:  |  Size: 216 B

After

Width:  |  Height:  |  Size: 216 B

View File

@@ -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(let highlighted_text) = self else { return false }
guard case .show_highlight_post_view = self else { return false }
return true
}
func should_show_mute_word_view() -> Bool {
guard case .show_mute_word_view(let highlighted_text) = self else { return false }
guard case .show_mute_word_view = self else { return false }
return true
}
@@ -119,16 +119,23 @@ 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) {
init(frame: CGRect, textContainer: NSTextContainer?, postHighlight: @escaping (String) -> Void, muteWord: @escaping (String) -> Void, enableHighlighting: Bool) {
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(_:)) {
@@ -142,23 +149,44 @@ fileprivate class TextView: UITextView {
return super.canPerformAction(action, withSender: sender)
}
func getSelectedText() -> String? {
private func getSelectedText() -> String? {
guard let selectedRange = self.selectedTextRange else { return nil }
return self.text(in: selectedRange)
}
@objc public func highlightText(_ sender: Any?) {
@objc private func highlightText(_ sender: Any?) {
guard let selectedText = self.getSelectedText() else { return }
self.postHighlight(selectedText)
}
@objc public func muteText(_ sender: Any?) {
@objc private 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
@@ -172,7 +200,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)
let view = TextView(frame: .zero, textContainer: nil, postHighlight: postHighlight, muteWord: muteWord, enableHighlighting: enableHighlighting)
view.isEditable = false
view.dataDetectorTypes = .all
view.isSelectable = true
@@ -183,11 +211,6 @@ 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
}

View File

@@ -632,7 +632,7 @@ struct ContentView: View {
func handleNotification(notification: LossyLocalNotification) {
Log.info("ContentView is handling a notification", for: .push_notifications)
guard let damus_state else {
guard damus_state != nil 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?) -> ()) {
var nostrKinds: [NostrKind]? = NostrKind(rawValue: naddr.kind).map { [$0] }
let 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(let string):
case .nrelay:
// 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(

View File

@@ -1,32 +0,0 @@
//
// 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
}
}
}

View File

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

View File

@@ -209,7 +209,7 @@ enum Route: Hashable {
case .Search(let search):
hasher.combine("search")
hasher.combine(search.search)
case .NDBSearch(let results):
case .NDBSearch:
hasher.combine("results")
case .EULA:
hasher.combine("eula")

View File

@@ -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, .zap, .zap_request, .nwc_request, .nwc_response, .http_auth, .status:
case .contacts, .metadata, .delete, .boost, .chat, .mute_list, .list_deprecated, .draft, .longform, .nwc_request, .nwc_response, .http_auth, .status:
return .unknown_or_unsupported_kind
}
case .naddr(let naddr):

View File

@@ -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)
await self.open_url(url: url)
self.open_url(url: url)
}
catch {
await self.open_url(url: damus_state.purple.environment.purple_landing_page_url().appendingPathComponent("checkout"))
self.open_url(url: damus_state.purple.environment.purple_landing_page_url().appendingPathComponent("checkout"))
}
}
}

View File

@@ -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.
var other_matches = drafts.highlights
let 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

View File

@@ -287,7 +287,7 @@ struct EditPictureControl: View {
var accessibility_value: String? {
if style.first_time_setup {
if let current_image_url = model.current_image_url {
if model.current_image_url != nil {
switch self.model.context {
case .normal:
return NSLocalizedString("Image is setup", comment: "Accessibility value on image control")

View File

@@ -297,7 +297,7 @@ fileprivate struct ProfileActionSheetZapButton: View {
.foregroundColor(Color.primary)
.profile_button_style(scheme: colorScheme)
case .zap_success:
Image("checkmark")
Image("checkmark-damus")
.foregroundColor(Color.green)
.profile_button_style(scheme: colorScheme)
case .zap_failure:

View File

@@ -57,11 +57,14 @@ struct QRScanNSECView: View {
}
}
#Preview {
@State var showQR = true
@State var privKeyFound = false
@State var shouldSaveKey = true
return QRScanNSECView(showQR: $showQR,
privKeyFound: $privKeyFound,
codeScannerCompletion: { _ in })
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 })
}
}