From f01755fe473af0c9a479a00b226712188c5f79a4 Mon Sep 17 00:00:00 2001 From: Jason Morley Date: Sat, 1 Jul 2023 00:14:19 -1000 Subject: [PATCH] fix: Show tag suggestions in the share extension (#704) --- .../Common/ApplicationModel.swift | 25 +++++++++++++------ .../Model/InfoContentViewModel.swift | 10 -------- .../BookmarksCore/Store/Database.swift | 14 +++++++++++ .../BookmarksCore/Store/TagsModel.swift | 19 ++++++++------ .../BookmarksCore/Views/InfoContentView.swift | 7 +++++- .../Model/ShareExtensionModel.swift | 9 ++++++- .../Views/EditorView.swift | 4 ++- 7 files changed, 60 insertions(+), 28 deletions(-) diff --git a/core/Sources/BookmarksCore/Common/ApplicationModel.swift b/core/Sources/BookmarksCore/Common/ApplicationModel.swift index 62282126..625a01e8 100644 --- a/core/Sources/BookmarksCore/Common/ApplicationModel.swift +++ b/core/Sources/BookmarksCore/Common/ApplicationModel.swift @@ -41,20 +41,31 @@ public class ApplicationModel: ObservableObject { public var cache: NSCache = NSCache() public var database: Database - private var documentsUrl: URL private var downloadManager: DownloadManager private var updater: Updater private var cancellables: Set = [] public init() { - documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! - try! FileManager.default.createDirectory(at: documentsUrl, withIntermediateDirectories: true, attributes: nil) + + // Ensure the documents directory exists. + let fileManager = FileManager.default + let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first! + try! fileManager.createDirectory(at: documentsURL, withIntermediateDirectories: true, attributes: nil) + + // Clean up the legacy store if it exists. + let legacyStoreURL = documentsURL.appendingPathComponent("store.db") + if fileManager.fileExists(atPath: legacyStoreURL.path) { + print("Legacy store exists; cleaning up.") + try? fileManager.removeItem(at: legacyStoreURL) + } + + // Create the database. // TODO: Handle database initialisation errors #143 // https://github.com/inseven/bookmarks/issues/143 - let storeURL = documentsUrl.appendingPathComponent("store.db") - print("Opening database at '\(storeURL.absoluteString)'...") - database = try! Database(path: storeURL) - imageCache = FileImageCache(path: documentsUrl.appendingPathComponent("thumbnails")) + print("Opening database at '\(Database.sharedStoreURL.absoluteString)'...") + database = try! Database(path: Database.sharedStoreURL) + + imageCache = FileImageCache(path: documentsURL.appendingPathComponent("thumbnails")) downloadManager = DownloadManager(limit: settings.maximumConcurrentThumbnailDownloads) thumbnailManager = ThumbnailManager(imageCache: imageCache, downloadManager: downloadManager) updater = Updater(database: database, settings: settings) diff --git a/core/Sources/BookmarksCore/Model/InfoContentViewModel.swift b/core/Sources/BookmarksCore/Model/InfoContentViewModel.swift index dd8b201d..e122d050 100644 --- a/core/Sources/BookmarksCore/Model/InfoContentViewModel.swift +++ b/core/Sources/BookmarksCore/Model/InfoContentViewModel.swift @@ -105,16 +105,6 @@ public class InfoContentViewModel: ObservableObject, Runnable { } - @MainActor func suggestions(candidate: String, existing: [String], count: Int) -> [String] { - let existing = Set(existing) - return applicationModel.tagsModel.tags(prefix: candidate) - .sorted { $0.count > $1.count } - .prefix(count + existing.count) - .filter { !existing.contains($0.name) } - .prefix(count) - .map { $0.name } - } - @MainActor public func stop() { cancellables.removeAll() } diff --git a/core/Sources/BookmarksCore/Store/Database.swift b/core/Sources/BookmarksCore/Store/Database.swift index 706f49c4..c78daf75 100644 --- a/core/Sources/BookmarksCore/Store/Database.swift +++ b/core/Sources/BookmarksCore/Store/Database.swift @@ -93,6 +93,20 @@ public class Database { } + // Default store location. + // Unfortunately, when running unsigned under test, the group container creation fails so this code silently fails + // over to using the documents directory. This is pretty inelegant as we'd really want a hard failure so it's not + // possible to use the incorrect location in production but, absent a better solution, this will have to do. + public static let sharedStoreURL: URL = { + let fileManager = FileManager.default + let identifier = "group.uk.co.inseven.bookmarks" + let groupURL = fileManager.containerURL(forSecurityApplicationGroupIdentifier: identifier) + let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first! + let directoryURL = groupURL ?? documentsURL + try? fileManager.createDirectory(atPath: directoryURL.path, withIntermediateDirectories: true) + return directoryURL.appendingPathComponent("store.db") + }() + static var migrations: [Int32:(Connection) throws -> Void] = [ 1: { _ in }, 2: { _ in }, diff --git a/core/Sources/BookmarksCore/Store/TagsModel.swift b/core/Sources/BookmarksCore/Store/TagsModel.swift index e5894e20..d9cabe88 100644 --- a/core/Sources/BookmarksCore/Store/TagsModel.swift +++ b/core/Sources/BookmarksCore/Store/TagsModel.swift @@ -72,7 +72,17 @@ public class TagsModel: ObservableObject { self.tags = [] } - @MainActor public func tags(prefix: String) -> [Database.Tag] { + @MainActor public func suggestions(candidate: String, existing: [String], count: Int) -> [String] { + let existing = Set(existing) + return tags(prefix: candidate) + .sorted { $0.count > $1.count } + .prefix(count + existing.count) + .filter { !existing.contains($0.name) } + .prefix(count) + .map { $0.name } + } + + @MainActor private func tags(prefix: String) -> [Database.Tag] { return trie.findWordsWithPrefix(prefix: prefix) .compactMap { name in guard let count = counts[name] else { @@ -82,12 +92,5 @@ public class TagsModel: ObservableObject { } } - @MainActor public func suggestions(prefix: String, existing: [String]) -> [String] { - let currentTags = Set(existing) - let tags = Set(trie.findWordsWithPrefix(prefix: prefix)) - let suggestions = Array(tags.subtracting(currentTags)) - return suggestions.sorted() - } - } diff --git a/core/Sources/BookmarksCore/Views/InfoContentView.swift b/core/Sources/BookmarksCore/Views/InfoContentView.swift index 06b1465c..8eef03c5 100644 --- a/core/Sources/BookmarksCore/Views/InfoContentView.swift +++ b/core/Sources/BookmarksCore/Views/InfoContentView.swift @@ -22,9 +22,12 @@ import SwiftUI public struct InfoContentView: View { + let applicationModel: ApplicationModel + @StateObject var model: InfoContentViewModel public init(applicationModel: ApplicationModel, id: String) { + self.applicationModel = applicationModel _model = StateObject(wrappedValue: InfoContentViewModel(applicationModel: applicationModel, id: id)) } @@ -50,7 +53,9 @@ public struct InfoContentView: View { } Section { TokenView("Add tags...", tokens: $model.tags) { candidate, existing, count in - model.suggestions(candidate: candidate, existing: existing, count: count) + applicationModel.tagsModel.suggestions(candidate: candidate, + existing: existing, + count: count) } } Section { diff --git a/ios/Bookmarks Share Extension/Model/ShareExtensionModel.swift b/ios/Bookmarks Share Extension/Model/ShareExtensionModel.swift index 8ebcbf65..bb29e169 100644 --- a/ios/Bookmarks Share Extension/Model/ShareExtensionModel.swift +++ b/ios/Bookmarks Share Extension/Model/ShareExtensionModel.swift @@ -41,6 +41,12 @@ class ShareExtensionModel: ObservableObject, Runnable { @MainActor private var cancellables: Set = [] + private static let database: Database = { + try! Database(path: Database.sharedStoreURL) + }() + + let tagsModel: TagsModel + weak var dataSource: ShareExtensionDataSource? = nil var pinboard: Pinboard? { @@ -52,7 +58,8 @@ class ShareExtensionModel: ObservableObject, Runnable { } init() { - + self.tagsModel = TagsModel(database: Self.database) + tagsModel.start() } @MainActor func load() { diff --git a/ios/Bookmarks Share Extension/Views/EditorView.swift b/ios/Bookmarks Share Extension/Views/EditorView.swift index 004dfb47..2ec482db 100644 --- a/ios/Bookmarks Share Extension/Views/EditorView.swift +++ b/ios/Bookmarks Share Extension/Views/EditorView.swift @@ -24,6 +24,8 @@ import BookmarksCore struct EditorView: View { + @EnvironmentObject var extensionModel: ShareExtensionModel + @Binding var post: Pinboard.Post var body: some View { @@ -34,7 +36,7 @@ struct EditorView: View { } Section { TokenView("Add tags...", tokens: $post.tags) { candidate, existing, count in - return [] + return extensionModel.tagsModel.suggestions(candidate: candidate, existing: existing, count: count) } } if let url = post.href {