kiwix-apple/SwiftUI/Model/LibraryOperations.swift
ChrisLi c6be0da9da
iOS 15 + macOS 12 (#459)
* setup

* RootView setup

* open url

* ReadingView

* ReadingView

* webview delegate

* webview outlint

* sidebar

* buttons

* bar buttons

* reader view model

* reader view model

* ReadingView iOS

* NavigationTitleSubtitle

* map

* introspect

* content group

* setting navigation

* WebViewCoordinator

* webview state

* outline

* root view iOS macos

* ios buttons

* OutlineMenu

* OutlineMenu

* outlint tree

* webview gesture

* bookmark button

* merge notification

* BookmarkMultiButton

* ArticleCell

* ArticleCell

* data model

* iOS bookmarks

* bookmark loading

* search

* RandomArticle

* MainArticleMenuButton

* MoreActionMenu

* swiftui4

* BarSetupModifier

* RootView_SwiftUI4

* NavigationItem

* purge & renaming

* bookmarks

* welcome no content

* welcome no content

* file importer

* macos library hookup

* library iOS setup

* Library

* Library setup

* buttons

* library setup

* CategoryList

* predicates

* grid

* settings view

* RootView_SwiftUI4

* RootView

* root view

* RootView_iOS

* ios reading view model

* rootview

* onchange

* ios bars

* reading view iOS 16 button

* bookmark sort

* mvoe

* project setting

* focus

* NavigationItemButtons

* patches

* PageZoomButtons

* FocusedSceneValue modifier

* pagezoom observer

* search view

* SearchView

* dismiss search

* min detail view size

* search result

* SearchView

* resrch

* dismiss search on url change

* search view

* ios search setup

* search

* macos search

* ios search

* remove old search

* use searchbar

* searching

* ios memory leak

* WebViewConfiguration

* macOS webview

* refactor

* SearchBar setup

* search bar setup

* SearchBar setup

* searchbar

* ReadingView

* webview ref

* outline

* sheet

* toolbar title outline

* compact reading view

* settings

* recent search

* reading view

* reading view

* SearchView

* several small changes

* shortcuts

* commands buttons

* macos command

* navigation action

* patch

* revert

* webview retention

* navigation focused scene value

* ipad keyboard shortcut

* macos app min fheight

* ios sheet action

* macOS 13 build

* purge

* move

* move

* indent

* settings

* file import

* rename

* GridCommon

* library views

* purge

* remove LibraryTopic

* LibraryView_iOS

* move

* coredata context

* add url binding

* ZimFileSelection

* pass url down

* ZimFileDetail

* load main page

* revert

* macos 12 compile

* reading view empty title

* view model

* onboarding view

* root view iOS 16

* ios search

* root view

* OutlineTree

* search overlay

* library view

* library refresh views

* view model

* view model

* LibraryViewModel

* zim file detail

* zim file detail styling

* zim file detail

* library detail & refresh

* ZimFileContextMenu

* directory monitor

* LibraryOperations

* LibraryOperations

* reopen

* migration

* zim file migration

* bookmark migration

* comments

* open zim file via bookmark

* welcome view

* hide bookmark section when empty

* bookmark context menu setup

* bookmark add / delete view model

* skip notification

* refactor view modifier

* remove old code
2022-09-21 22:11:09 -04:00

136 lines
5.2 KiB
Swift

//
// LibraryOperations.swift
// Kiwix
//
// Created by Chris Li on 9/12/22.
// Copyright © 2022 Chris Li. All rights reserved.
//
import CoreData
import os
struct LibraryOperations {
private init() {}
// MARK: - Open
/// Open a zim file with url
/// - Parameter url: url of the zim file
static func open(url: URL) {
guard let metadata = ZimFileService.getMetaData(url: url),
let fileURLBookmark = ZimFileService.getBookmarkData(url: url) else { return }
// open the file
do {
try ZimFileService.shared.open(bookmark: fileURLBookmark)
} catch {
return
}
// upsert zim file in the database
Database.shared.container.performBackgroundTask { context in
context.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
let predicate = NSPredicate(format: "fileID == %@", metadata.fileID as CVarArg)
let fetchRequest = ZimFile.fetchRequest(predicate: predicate)
guard let zimFile = try? context.fetch(fetchRequest).first ?? ZimFile(context: context) else { return }
LibraryOperations.configureZimFile(zimFile, metadata: metadata)
zimFile.fileURLBookmark = fileURLBookmark
zimFile.isMissing = false
if context.hasChanges { try? context.save() }
}
}
/// Reopen zim files from url bookmark data.
static func reopen() {
var successCount = 0
let context = Database.shared.container.viewContext
let request = ZimFile.fetchRequest(predicate: ZimFile.withFileURLBookmarkPredicate)
guard let zimFiles = try? context.fetch(request) else { return }
zimFiles.forEach { zimFile in
guard let data = zimFile.fileURLBookmark else { return }
do {
if let data = try ZimFileService.shared.open(bookmark: data) {
zimFile.fileURLBookmark = data
}
zimFile.isMissing = false
successCount += 1
} catch ZimFileOpenError.missing {
zimFile.isMissing = true
} catch {
zimFile.fileURLBookmark = nil
zimFile.isMissing = false
}
}
if context.hasChanges {
try? context.save()
}
os_log("Reopened %d out of %d zim files", log: Log.LibraryOperations, type: .info, successCount, zimFiles.count)
}
/// Scan a directory and open available zim files inside it
/// - Parameter url: directory to scan
static func scanDirectory(_ url: URL) {
guard let fileURLs = try? FileManager.default.contentsOfDirectory(
at: url,
includingPropertiesForKeys: nil,
options: [.skipsHiddenFiles, .skipsPackageDescendants, .skipsSubdirectoryDescendants]
).filter({ $0.pathExtension == "zim"}) else { return }
os_log("Discovered %d probable zim files.", log: Log.LibraryOperations, type: .info, fileURLs.count)
for fileURL in fileURLs {
LibraryOperations.open(url: fileURL)
}
}
// MARK: - Configure
/// Configure a zim file object based on its metadata.
static func configureZimFile(_ zimFile: ZimFile, metadata: ZimFileMetaData) {
zimFile.articleCount = metadata.articleCount.int64Value
zimFile.category = metadata.category
zimFile.created = metadata.creationDate
zimFile.fileDescription = metadata.fileDescription
zimFile.fileID = metadata.fileID
zimFile.flavor = metadata.flavor
zimFile.hasDetails = metadata.hasDetails
zimFile.hasPictures = metadata.hasPictures
zimFile.hasVideos = metadata.hasVideos
zimFile.languageCode = metadata.languageCode
zimFile.mediaCount = metadata.mediaCount.int64Value
zimFile.name = metadata.title
zimFile.persistentID = metadata.groupIdentifier
zimFile.requiresServiceWorkers = metadata.requiresServiceWorkers
zimFile.size = metadata.size.int64Value
// Only overwrite favicon data and url if there is a new value
if let url = metadata.downloadURL { zimFile.downloadURL = url }
if let url = metadata.faviconURL { zimFile.faviconURL = url }
}
//MARK: - Deletion
/// Unlink a zim file from library, and delete the file.
/// - Parameter zimFile: the zim file to delete
static func delete(zimFileID: UUID) {
LibraryOperations.unlink(zimFileID: zimFileID)
}
/// Unlink a zim file from library, but don't delete the file.
/// - Parameter zimFile: the zim file to unlink
static func unlink(zimFileID: UUID) {
ZimFileService.shared.close(fileID: zimFileID)
Database.shared.container.performBackgroundTask { context in
context.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
guard let zimFile = try? ZimFile.fetchRequest(fileID: zimFileID).execute().first else { return }
if zimFile.downloadURL == nil {
context.delete(zimFile)
} else {
zimFile.fileURLBookmark = nil
zimFile.isMissing = false
}
if context.hasChanges { try? context.save() }
}
}
}