aboutsummaryrefslogtreecommitdiff
path: root/Hotline/Utility
diff options
context:
space:
mode:
authorRuben Beltran del Rio <git@r.bdr.sh>2025-11-27 23:38:04 +0100
committerRuben Beltran del Rio <git@r.bdr.sh>2025-11-27 23:38:04 +0100
commit213710bf5bd6413c747bf126db50816ef5de5a6e (patch)
tree912f8cf87955a08077c92fea8ad934f50b7ab975 /Hotline/Utility
parentf466b21dc02f78c984ba6748e703f6780a7a0db4 (diff)
parent6a95b53616a4abfa306ddce43151cf4fefbd20ed (diff)
Merge remote-tracking branch 'upstream/main'
Diffstat (limited to 'Hotline/Utility')
-rw-r--r--Hotline/Utility/BookmarkDocument.swift32
-rw-r--r--Hotline/Utility/DAKeychain.swift82
-rw-r--r--Hotline/Utility/FoundationExtensions.swift87
-rw-r--r--Hotline/Utility/NSWindowBridge.swift25
-rw-r--r--Hotline/Utility/ObservableScrollView.swift42
-rw-r--r--Hotline/Utility/RegularExpressions.swift235
-rw-r--r--Hotline/Utility/SoundEffects.swift52
-rw-r--r--Hotline/Utility/SwiftUIExtensions.swift9
-rw-r--r--Hotline/Utility/TextDocument.swift31
9 files changed, 0 insertions, 595 deletions
diff --git a/Hotline/Utility/BookmarkDocument.swift b/Hotline/Utility/BookmarkDocument.swift
deleted file mode 100644
index 43f251c..0000000
--- a/Hotline/Utility/BookmarkDocument.swift
+++ /dev/null
@@ -1,32 +0,0 @@
-import Foundation
-import SwiftUI
-import UniformTypeIdentifiers
-
-struct BookmarkDocument: FileDocument {
- static var readableContentTypes: [UTType] { [.data, UTType(filenameExtension: "hlbm")!] }
- static var writableContentTypes: [UTType] { [.data, UTType(filenameExtension: "hlbm")!] }
-
- var bookmark: Bookmark
-
- init(bookmark: Bookmark) {
- self.bookmark = bookmark
- }
-
- init(configuration: ReadConfiguration) throws {
- guard configuration.file.isRegularFile,
- let data = configuration.file.regularFileContents,
- let fileName = configuration.file.preferredFilename,
- let bookmark = Bookmark(fileData: data, name: (fileName as NSString).deletingPathExtension)
- else {
- throw CocoaError(.fileReadCorruptFile)
- }
- self.bookmark = bookmark
- }
-
- func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
- let wrapper = FileWrapper(regularFileWithContents: self.bookmark.bookmarkFileData()!)
- wrapper.fileAttributes[FileAttributeKey.hfsCreatorCode.rawValue] = "HTLC".fourCharCode()
- wrapper.fileAttributes[FileAttributeKey.hfsTypeCode.rawValue] = "HTbm".fourCharCode()
- return wrapper
- }
-}
diff --git a/Hotline/Utility/DAKeychain.swift b/Hotline/Utility/DAKeychain.swift
deleted file mode 100644
index aa71508..0000000
--- a/Hotline/Utility/DAKeychain.swift
+++ /dev/null
@@ -1,82 +0,0 @@
-//
-// DAKeychain.swift
-// DAKeychain
-//
-// Created by Dejan on 28/02/2017.
-// Copyright © 2017 Dejan. All rights reserved.
-//
-
-import Foundation
-
-open class DAKeychain {
-
- open var loggingEnabled = false
-
- private init() {}
- public static let shared = DAKeychain()
-
- open subscript(key: String) -> String? {
- get {
- return load(withKey: key)
- }
- set {
- DispatchQueue.global().sync(flags: .barrier) {
- self.save(newValue, forKey: key)
- }
- }
- }
-
- private func save(_ string: String?, forKey key: String) {
- let query = keychainQuery(withKey: key)
- let objectData: Data? = string?.data(using: .utf8, allowLossyConversion: false)
-
- if SecItemCopyMatching(query, nil) == noErr {
- if let dictData = objectData {
- let status = SecItemUpdate(query, NSDictionary(dictionary: [kSecValueData: dictData]))
- logPrint("Update status: ", status)
- } else {
- let status = SecItemDelete(query)
- logPrint("Delete status: ", status)
- }
- } else {
- if let dictData = objectData {
- query.setValue(dictData, forKey: kSecValueData as String)
- let status = SecItemAdd(query, nil)
- logPrint("Update status: ", status)
- }
- }
- }
-
- private func load(withKey key: String) -> String? {
- let query = keychainQuery(withKey: key)
- query.setValue(kCFBooleanTrue, forKey: kSecReturnData as String)
- query.setValue(kCFBooleanTrue, forKey: kSecReturnAttributes as String)
-
- var result: CFTypeRef?
- let status = SecItemCopyMatching(query, &result)
-
- guard
- let resultsDict = result as? NSDictionary,
- let resultsData = resultsDict.value(forKey: kSecValueData as String) as? Data,
- status == noErr
- else {
- logPrint("Load status: ", status)
- return nil
- }
- return String(data: resultsData, encoding: .utf8)
- }
-
- private func keychainQuery(withKey key: String) -> NSMutableDictionary {
- let result = NSMutableDictionary()
- result.setValue(kSecClassGenericPassword, forKey: kSecClass as String)
- result.setValue(key, forKey: kSecAttrService as String)
- result.setValue(kSecAttrAccessibleWhenUnlocked, forKey: kSecAttrAccessible as String)
- return result
- }
-
- private func logPrint(_ items: Any...) {
- if loggingEnabled {
- print(items)
- }
- }
-}
diff --git a/Hotline/Utility/FoundationExtensions.swift b/Hotline/Utility/FoundationExtensions.swift
deleted file mode 100644
index b1cd1b9..0000000
--- a/Hotline/Utility/FoundationExtensions.swift
+++ /dev/null
@@ -1,87 +0,0 @@
-import Foundation
-import SwiftUI
-
-enum Endianness {
- case big
- case little
-}
-
-extension String {
-
- func convertToAttributedStringWithLinks() -> AttributedString {
- let attributedString: NSMutableAttributedString = NSMutableAttributedString(string: self)
- let matches = self.ranges(of: RegularExpressions.relaxedLink)
- for match in matches {
- 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
- }
- switch url.scheme?.lowercased() {
- case "http", "https":
- return true
- default:
- return false
- }
- }
-
- func isImageURL() -> Bool {
- guard let url = URL(string: self) else {
- return false
- }
-
- switch url.pathExtension.lowercased() {
- case "jpg", "jpeg", "png", "gif":
- return true
- default:
- return false
- }
- }
-
- func convertingLinksToMarkdown() -> String {
- var cp = String(self)
- cp.replace(RegularExpressions.relaxedLink) { match -> String in
- let linkText = self[match.range]
- var injectedScheme = "https://"
- if (try? RegularExpressions.supportedLinkScheme.prefixMatch(in: linkText)) != nil {
- injectedScheme = ""
- }
-
- return "[\(linkText)](\(injectedScheme)\(linkText))"
- }
- return cp
- }
-}
-
-extension Binding where Value: OptionSet, Value == Value.Element {
- func bindedValue(_ options: Value) -> Bool {
- return wrappedValue.contains(options)
- }
-
- func bind(_ options: Value) -> Binding<Bool> {
- return .init { () -> Bool in
- self.wrappedValue.contains(options)
- } set: { newValue in
- if newValue {
- self.wrappedValue.insert(options)
- } else {
- self.wrappedValue.remove(options)
- }
- }
- }
-}
diff --git a/Hotline/Utility/NSWindowBridge.swift b/Hotline/Utility/NSWindowBridge.swift
deleted file mode 100644
index ad56105..0000000
--- a/Hotline/Utility/NSWindowBridge.swift
+++ /dev/null
@@ -1,25 +0,0 @@
-import SwiftUI
-
-private class NSWindowAccessorView: NSView {
- let executeBlock: (_ window: NSWindow?) -> Void
-
- init(_ inConfigFunction: @escaping (_ window: NSWindow?) -> Void) {
- 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?) -> Void
-
- 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/ObservableScrollView.swift b/Hotline/Utility/ObservableScrollView.swift
deleted file mode 100644
index 05688f1..0000000
--- a/Hotline/Utility/ObservableScrollView.swift
+++ /dev/null
@@ -1,42 +0,0 @@
-import SwiftUI
-
-struct ScrollViewOffsetPreferenceKey: PreferenceKey {
- typealias Value = CGFloat
- static var defaultValue = CGFloat.zero
- static func reduce(value: inout Value, nextValue: () -> Value) {
- value += nextValue()
- }
-}
-
-struct ObservableScrollView<Content>: View where Content: View {
- @Namespace var scrollSpace
- @Binding var scrollOffset: CGFloat
- let content: () -> Content
-
- init(
- scrollOffset: Binding<CGFloat>,
- @ViewBuilder content: @escaping () -> Content
- ) {
- _scrollOffset = scrollOffset
- self.content = content
- }
-
- var body: some View {
- ScrollView {
- content()
- .background(
- GeometryReader { geo in
- let offset = -geo.frame(in: .named(scrollSpace)).minY
- Color.clear
- .preference(
- key: ScrollViewOffsetPreferenceKey.self,
- value: offset)
- })
-
- }
- .coordinateSpace(name: scrollSpace)
- .onPreferenceChange(ScrollViewOffsetPreferenceKey.self) { value in
- scrollOffset = value
- }
- }
-}
diff --git a/Hotline/Utility/RegularExpressions.swift b/Hotline/Utility/RegularExpressions.swift
deleted file mode 100644
index db705ae..0000000
--- a/Hotline/Utility/RegularExpressions.swift
+++ /dev/null
@@ -1,235 +0,0 @@
-import RegexBuilder
-
-struct RegularExpressions {
- static let messageBoardDivider = Regex {
- Capture {
- OneOrMore {
- CharacterClass(.newlineSequence)
- }
- ZeroOrMore {
- CharacterClass(.whitespace, .newlineSequence)
- }
- Repeat(2...) {
- CharacterClass(.anyOf("_-"))
- }
- ZeroOrMore {
- CharacterClass(.whitespace)
- }
- OneOrMore {
- CharacterClass(.newlineSequence)
- }
- }
- }
-
- static let supportedLinkScheme = Regex {
- Anchor.startOfLine
- ChoiceOf {
- "hotline"
- "http"
- "https"
- }
- "://"
- }.ignoresCase().anchorsMatchLineEndings()
-
- static let relaxedLink = Regex {
- ChoiceOf {
- Anchor.startOfLine
- Anchor.wordBoundary
- }
- Capture {
- // scheme (optional)
- Optionally {
- ChoiceOf {
- "hotline://"
- "http://"
- "https://"
- }
- }
- // 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"
- }
- // Port
- Optionally {
- ":"
- OneOrMore {
- CharacterClass(.digit)
- }
- }
- // path
- ZeroOrMore {
- CharacterClass(
- .anyOf("#_-/.?=&%\\()[]"),
- ("a"..."z"),
- ("0"..."9")
- )
- }
- }
- ChoiceOf {
- Anchor.endOfLine
- Anchor.wordBoundary
- }
- }
- .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()
-}
diff --git a/Hotline/Utility/SoundEffects.swift b/Hotline/Utility/SoundEffects.swift
deleted file mode 100644
index 74314e4..0000000
--- a/Hotline/Utility/SoundEffects.swift
+++ /dev/null
@@ -1,52 +0,0 @@
-import AVFAudio
-import Foundation
-
-enum SoundEffects: String {
- case loggedIn = "logged-in"
- case chatMessage = "chat-message"
- case transferComplete = "transfer-complete"
- case userLogin = "user-login"
- case userLogout = "user-logout"
- case newNews = "new-news"
- case serverMessage = "server-message"
- case error = "error"
-}
-
-@Observable
-class SoundEffectPlayer: NSObject, AVAudioPlayerDelegate {
- static let shared = SoundEffectPlayer()
-
- private var activeSounds: [AVAudioPlayer] = []
-
- func playSoundEffect(_ name: SoundEffects) {
- // Load a local sound file
- guard
- let soundFileURL = Bundle.main.url(
- forResource: name.rawValue,
- withExtension: "aiff"
- )
- else {
- return
- }
-
- DispatchQueue.main.async { [weak self] in
- guard let self = self else {
- return
- }
-
- if let soundEffect = try? AVAudioPlayer(contentsOf: soundFileURL) {
- soundEffect.delegate = self
- soundEffect.volume = 0.75
- soundEffect.play()
-
- self.activeSounds.append(soundEffect)
- }
- }
- }
-
- func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
- if let i = self.activeSounds.firstIndex(of: player) {
- self.activeSounds.remove(at: i)
- }
- }
-}
diff --git a/Hotline/Utility/SwiftUIExtensions.swift b/Hotline/Utility/SwiftUIExtensions.swift
deleted file mode 100644
index 27697f8..0000000
--- a/Hotline/Utility/SwiftUIExtensions.swift
+++ /dev/null
@@ -1,9 +0,0 @@
-import SwiftUI
-
-extension Color {
- init(hex: Int, opacity: Double = 1.0) {
- self.init(
- red: Double((hex >> 16) & 0xFF) / 255.0, green: Double((hex >> 8) & 0xFF) / 255.0,
- blue: Double(hex & 0xFF) / 255.0, opacity: opacity)
- }
-}
diff --git a/Hotline/Utility/TextDocument.swift b/Hotline/Utility/TextDocument.swift
deleted file mode 100644
index a3591fd..0000000
--- a/Hotline/Utility/TextDocument.swift
+++ /dev/null
@@ -1,31 +0,0 @@
-import Foundation
-import SwiftUI
-import UniformTypeIdentifiers
-
-struct TextFile: FileDocument {
- // tell the system we support only plain text
- static var readableContentTypes = [UTType.plainText, UTType.utf8PlainText]
-
- // by default our document is empty
- var text = ""
-
- // a simple initializer that creates new, empty documents
- init(initialText: String = "") {
- text = initialText
- }
-
- // this initializer loads data that has been saved previously
- init(configuration: ReadConfiguration) throws {
-
- if let data = configuration.file.regularFileContents {
- if let str = String(data: data, encoding: .utf8) {
- self.text = str
- }
- }
- }
-
- // this will be called when the system wants to write our data to disk
- func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
- return FileWrapper(regularFileWithContents: self.text.data(using: .utf8)!)
- }
-}