From 75b40db9adac93dde9df3ff76bb172c76e3d3a55 Mon Sep 17 00:00:00 2001 From: Dustin Mierau Date: Wed, 8 May 2024 20:53:10 -0700 Subject: Fix about box layout on open. Remove about box from window menu, add expand button to server agreement, add email address highlighting, style server messages, unread badge on admins are red now, play server message sound, some code cleanup. --- Hotline/Utility/FoundationExtensions.swift | 12 +++- Hotline/Utility/NSWindowBridge.swift | 25 ++++++++ Hotline/Utility/RegularExpressions.swift | 96 +++++++++++++++++++++++++++++- 3 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 Hotline/Utility/NSWindowBridge.swift (limited to 'Hotline/Utility') diff --git a/Hotline/Utility/FoundationExtensions.swift b/Hotline/Utility/FoundationExtensions.swift index 6270ac7..3a325be 100644 --- a/Hotline/Utility/FoundationExtensions.swift +++ b/Hotline/Utility/FoundationExtensions.swift @@ -11,12 +11,22 @@ extension String { let attributedString: NSMutableAttributedString = NSMutableAttributedString(string: self) let matches = self.ranges(of: RegularExpressions.relaxedLink) for match in matches { - attributedString.addAttribute(.link, value: self[match], range: NSRange(match, in: self)) + let matchString = String(self[match]) + if matchString.isEmailAddress() { + attributedString.addAttribute(.link, value: "mailto:\(matchString)", range: NSRange(match, in: self)) + } + else { + attributedString.addAttribute(.link, value: matchString, range: NSRange(match, in: self)) + } // attributedString.addAttribute(.underlineStyle, value: 1, range: NSRange(match, in: self)) } return AttributedString(attributedString) } + func isEmailAddress() -> Bool { + self.wholeMatch(of: RegularExpressions.emailAddress) != nil + } + func isWebURL() -> Bool { guard let url = URL(string: self) else { return false diff --git a/Hotline/Utility/NSWindowBridge.swift b/Hotline/Utility/NSWindowBridge.swift new file mode 100644 index 0000000..5cd7197 --- /dev/null +++ b/Hotline/Utility/NSWindowBridge.swift @@ -0,0 +1,25 @@ +import SwiftUI + +fileprivate class NSWindowAccessorView: NSView { + let executeBlock: (_ window: NSWindow? ) -> () + + init(_ inConfigFunction: @escaping (_ window: NSWindow? ) -> () ) { + executeBlock = inConfigFunction + super.init( frame: NSRect() ) + } + + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + public override func viewDidMoveToWindow() { + super.viewDidMoveToWindow() + executeBlock( self.window ) // We pass it through even if it is nil. + } +} + +public struct NSWindowAccessor: NSViewRepresentable { + var configCode: (_ window: NSWindow? ) -> () + + public init(_ configCode: @escaping (_: NSWindow?) -> Void) { self.configCode = configCode } + public func makeNSView(context: Context) -> NSView { return NSWindowAccessorView( configCode ) } + public func updateNSView(_ nsView: NSView, context: Context) {} +} diff --git a/Hotline/Utility/RegularExpressions.swift b/Hotline/Utility/RegularExpressions.swift index bb53f8c..c1f2a18 100644 --- a/Hotline/Utility/RegularExpressions.swift +++ b/Hotline/Utility/RegularExpressions.swift @@ -48,7 +48,7 @@ struct RegularExpressions { // domain name OneOrMore { CharacterClass( - .anyOf(".-"), + .anyOf(".-@"), ("a"..."z"), ("0"..."9") ) @@ -138,4 +138,98 @@ struct RegularExpressions { } .anchorsMatchLineEndings() .ignoresCase() + + static let emailAddress = Regex { + ChoiceOf { + Anchor.startOfLine + Anchor.wordBoundary + } + Capture { + // username + OneOrMore { + CharacterClass( + .anyOf(".-_"), + ("a"..."z"), + ("0"..."9") + ) + } + "@" + // domain name + OneOrMore { + CharacterClass( + .anyOf(".-"), + ("a"..."z"), + ("0"..."9") + ) + } + // top-level domain name + "." + ChoiceOf { + "com" + "net" + "org" + "edu" + "gov" + "mil" + "aero" + "asia" + "biz" + "cat" + "coop" + "info" + "int" + "jobs" + "mobi" + "museum" + "name" + "pizza" + "post" + "pro" + "red" + "tel" + "today" + "travel" + "garden" + "online" + "ai" + "be" + "by" + "ca" + "co" + "de" + "er" + "es" + "fr" + "gs" + "ie" + "im" + "in" + "io" + "is" + "it" + "jp" + "la" + "ly" + "ma" + "md" + "me" + "my" + "nl" + "ps" + "pt" + "ja" + "st" + "to" + "tv" + "uk" + "ws" + } + } + ChoiceOf { + Anchor.endOfLine + Anchor.wordBoundary + } + } + .anchorsMatchLineEndings() + .ignoresCase() } -- cgit