2aaa8eab7b
SwiftUI app for iPhone that connects to a WebDAV server and lists, searches, reads and edits Denote-format notes (.org). Credentials stored in the iOS Keychain. Server configured via a setup screen on first launch.
88 lines
2.9 KiB
Swift
88 lines
2.9 KiB
Swift
import Foundation
|
|
|
|
struct DenoteNote: Identifiable, Hashable {
|
|
let id: String
|
|
let filename: String
|
|
let path: String
|
|
let title: String
|
|
let keywords: [String]
|
|
let fileExtension: String
|
|
let modifiedDate: Date?
|
|
var content: String?
|
|
|
|
var displayTitle: String {
|
|
title.isEmpty ? filename : title
|
|
}
|
|
|
|
var formattedDate: String {
|
|
guard let date = modifiedDate else { return "" }
|
|
let f = DateFormatter()
|
|
f.dateStyle = .medium
|
|
f.timeStyle = .none
|
|
return f.string(from: date)
|
|
}
|
|
|
|
// Parses a Denote filename from a WebDAV href.
|
|
// Expected format: /notes/YYYYMMDDTHHMMSS(==SIGNATURE)?(--TITLE)?(__KW1_KW2)?.EXT
|
|
static func parse(href: String, modifiedDate: Date?) -> DenoteNote? {
|
|
let filename = href.split(separator: "/").last.map(String.init) ?? href
|
|
|
|
guard filename.range(of: #"^\d{8}T\d{6}"#, options: .regularExpression) != nil else { return nil }
|
|
guard !filename.hasSuffix(".organice-bak"),
|
|
!filename.hasSuffix(".bak"),
|
|
filename != "DavLock" else { return nil }
|
|
|
|
let nameWithoutExt: String
|
|
let ext: String
|
|
if let dotIndex = filename.lastIndex(of: ".") {
|
|
nameWithoutExt = String(filename[..<dotIndex])
|
|
ext = String(filename[filename.index(after: dotIndex)...])
|
|
} else {
|
|
nameWithoutExt = filename
|
|
ext = ""
|
|
}
|
|
|
|
var remaining = nameWithoutExt
|
|
let identifier: String
|
|
if let range = remaining.range(of: "--") {
|
|
let rawId = String(remaining[..<range.lowerBound])
|
|
identifier = rawId.components(separatedBy: "==").first ?? rawId
|
|
remaining = String(remaining[range.upperBound...])
|
|
} else {
|
|
identifier = remaining.components(separatedBy: "==").first ?? remaining
|
|
remaining = ""
|
|
}
|
|
|
|
let title: String
|
|
let keywords: [String]
|
|
if let kwRange = remaining.range(of: "__") {
|
|
title = slugToTitle(String(remaining[..<kwRange.lowerBound]))
|
|
keywords = String(remaining[kwRange.upperBound...]).split(separator: "_").map(String.init)
|
|
} else {
|
|
title = slugToTitle(remaining)
|
|
keywords = []
|
|
}
|
|
|
|
return DenoteNote(
|
|
id: identifier,
|
|
filename: filename,
|
|
path: href,
|
|
title: title,
|
|
keywords: keywords,
|
|
fileExtension: ext,
|
|
modifiedDate: modifiedDate,
|
|
content: nil
|
|
)
|
|
}
|
|
|
|
private static func slugToTitle(_ slug: String) -> String {
|
|
slug.replacingOccurrences(of: "-", with: " ")
|
|
.split(separator: " ")
|
|
.map { $0.prefix(1).uppercased() + $0.dropFirst() }
|
|
.joined(separator: " ")
|
|
}
|
|
|
|
func hash(into hasher: inout Hasher) { hasher.combine(id) }
|
|
static func == (lhs: DenoteNote, rhs: DenoteNote) -> Bool { lhs.id == rhs.id }
|
|
}
|