FIrst pass at OPML import. Broken.
This commit is contained in:
parent
a6e0cae377
commit
42203c0170
|
@ -49,10 +49,11 @@ final class GoogleReaderCompatibleAPICaller: NSObject {
|
||||||
case editTag = "/reader/api/0/edit-tag"
|
case editTag = "/reader/api/0/edit-tag"
|
||||||
}
|
}
|
||||||
|
|
||||||
// private let GoogleReaderCompatibleBaseURL = URL(string: "https://api.GoogleReaderCompatible.com/v2/")!
|
|
||||||
private var transport: Transport!
|
private var transport: Transport!
|
||||||
|
|
||||||
var credentials: Credentials?
|
var credentials: Credentials?
|
||||||
|
private var accessToken: String?
|
||||||
|
|
||||||
weak var accountMetadata: AccountMetadata?
|
weak var accountMetadata: AccountMetadata?
|
||||||
|
|
||||||
var server: String? {
|
var server: String? {
|
||||||
|
@ -127,6 +128,13 @@ final class GoogleReaderCompatibleAPICaller: NSObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
func requestAuthorizationToken(endpoint: URL, completion: @escaping (Result<String, Error>) -> Void) {
|
func requestAuthorizationToken(endpoint: URL, completion: @escaping (Result<String, Error>) -> Void) {
|
||||||
|
// If we have a token already, use it
|
||||||
|
if let accessToken = accessToken {
|
||||||
|
completion(.success(accessToken))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise request one.
|
||||||
guard let credentials = credentials else {
|
guard let credentials = credentials else {
|
||||||
completion(.failure(CredentialsError.incompleteCredentials))
|
completion(.failure(CredentialsError.incompleteCredentials))
|
||||||
return
|
return
|
||||||
|
@ -143,67 +151,19 @@ final class GoogleReaderCompatibleAPICaller: NSObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the return data to UTF8 and then parse out the Auth token
|
// Convert the return data to UTF8 and then parse out the Auth token
|
||||||
guard let rawData = String(data: resultData, encoding: .utf8) else {
|
guard let accessToken = String(data: resultData, encoding: .utf8) else {
|
||||||
completion(.failure(TransportError.noData))
|
completion(.failure(TransportError.noData))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.accessToken = accessToken
|
||||||
completion(.success(rawData))
|
completion(.success(accessToken))
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
completion(.failure(error))
|
completion(.failure(error))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func importOPML(opmlData: Data, completion: @escaping (Result<GoogleReaderCompatibleImportResult, Error>) -> Void) {
|
|
||||||
|
|
||||||
// let callURL = GoogleReaderCompatibleBaseURL.appendingPathComponent("imports.json")
|
|
||||||
// var request = URLRequest(url: callURL, credentials: credentials)
|
|
||||||
// request.addValue("text/xml; charset=utf-8", forHTTPHeaderField: HTTPRequestHeader.contentType)
|
|
||||||
//
|
|
||||||
// transport.send(request: request, method: HTTPMethod.post, payload: opmlData) { result in
|
|
||||||
//
|
|
||||||
// switch result {
|
|
||||||
// case .success(let (_, data)):
|
|
||||||
//
|
|
||||||
// guard let resultData = data else {
|
|
||||||
// completion(.failure(TransportError.noData))
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// do {
|
|
||||||
// let result = try JSONDecoder().decode(GoogleReaderCompatibleImportResult.self, from: resultData)
|
|
||||||
// completion(.success(result))
|
|
||||||
// } catch {
|
|
||||||
// completion(.failure(error))
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// case .failure(let error):
|
|
||||||
// completion(.failure(error))
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func retrieveOPMLImportResult(importID: Int, completion: @escaping (Result<GoogleReaderCompatibleImportResult?, Error>) -> Void) {
|
|
||||||
|
|
||||||
// let callURL = GoogleReaderCompatibleBaseURL.appendingPathComponent("imports/\(importID).json")
|
|
||||||
// let request = URLRequest(url: callURL, credentials: credentials)
|
|
||||||
//
|
|
||||||
// transport.send(request: request, resultType: GoogleReaderCompatibleImportResult.self) { result in
|
|
||||||
//
|
|
||||||
// switch result {
|
|
||||||
// case .success(let (_, importResult)):
|
|
||||||
// completion(.success(importResult))
|
|
||||||
// case .failure(let error):
|
|
||||||
// completion(.failure(error))
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func retrieveTags(completion: @escaping (Result<[GoogleReaderCompatibleTag]?, Error>) -> Void) {
|
func retrieveTags(completion: @escaping (Result<[GoogleReaderCompatibleTag]?, Error>) -> Void) {
|
||||||
guard let baseURL = APIBaseURL else {
|
guard let baseURL = APIBaseURL else {
|
||||||
|
|
|
@ -210,31 +210,113 @@ final class GoogleReaderCompatibleAccountDelegate: AccountDelegate {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
os_log(.debug, log: log, "Begin importing OPML...")
|
let parserData = ParserData(url: opmlFile.absoluteString, data: opmlData)
|
||||||
opmlImportInProgress = true
|
var opmlDocument: RSOPMLDocument?
|
||||||
|
|
||||||
caller.importOPML(opmlData: opmlData) { result in
|
do {
|
||||||
switch result {
|
opmlDocument = try RSOPMLParser.parseOPML(with: parserData)
|
||||||
case .success(let importResult):
|
} catch {
|
||||||
if importResult.complete {
|
completion(.failure(error))
|
||||||
os_log(.debug, log: self.log, "Import OPML done.")
|
return
|
||||||
self.opmlImportInProgress = false
|
}
|
||||||
DispatchQueue.main.async {
|
|
||||||
completion(.success(()))
|
guard let loadDocument = opmlDocument else {
|
||||||
}
|
completion(.success(()))
|
||||||
} else {
|
return
|
||||||
self.checkImportResult(opmlImportResultID: importResult.importResultID, completion: completion)
|
}
|
||||||
|
|
||||||
|
// We use the same mechanism to load local accounts as we do to load the subscription
|
||||||
|
// OPML all accounts.
|
||||||
|
BatchUpdate.shared.perform {
|
||||||
|
loadOPML(account: account, opmlDocument: loadDocument)
|
||||||
|
}
|
||||||
|
completion(.success(()))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadOPML(account: Account, opmlDocument: RSOPMLDocument) {
|
||||||
|
|
||||||
|
guard let children = opmlDocument.children else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
loadOPMLItems(account: account, items: children, parentFolder: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadOPMLItems(account: Account, items: [RSOPMLItem], parentFolder: Folder?) {
|
||||||
|
|
||||||
|
var feedsToAdd = Set<String>()
|
||||||
|
|
||||||
|
items.forEach { (item) in
|
||||||
|
|
||||||
|
if let feedSpecifier = item.feedSpecifier {
|
||||||
|
feedsToAdd.insert(feedSpecifier.feedURL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let folderName = item.titleFromAttributes else {
|
||||||
|
// Folder doesn’t have a name, so it won’t be created, and its items will go one level up.
|
||||||
|
if let itemChildren = item.children {
|
||||||
|
loadOPMLItems(account: account, items: itemChildren, parentFolder: parentFolder)
|
||||||
}
|
}
|
||||||
case .failure(let error):
|
return
|
||||||
os_log(.debug, log: self.log, "Import OPML failed.")
|
}
|
||||||
self.opmlImportInProgress = false
|
|
||||||
DispatchQueue.main.async {
|
if let itemChildren = item.children, let folder = account.ensureFolder(with: folderName) {
|
||||||
let wrappedError = AccountError.wrappedError(error: error, account: account)
|
loadOPMLItems(account: account, items: itemChildren, parentFolder: folder)
|
||||||
completion(.failure(wrappedError))
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let group = DispatchGroup()
|
||||||
|
|
||||||
|
if let parentFolder = parentFolder {
|
||||||
|
for url in feedsToAdd {
|
||||||
|
group.enter()
|
||||||
|
caller.createSubscription(url: url) { result in
|
||||||
|
group.leave()
|
||||||
|
switch result {
|
||||||
|
case .success(let subResult):
|
||||||
|
switch subResult {
|
||||||
|
case .created(let subscription):
|
||||||
|
let feed = account.createFeed(with: subscription.name, url: subscription.url, feedID: String(subscription.feedID), homePageURL: subscription.homePageURL)
|
||||||
|
feed.subscriptionID = String(subscription.feedID)
|
||||||
|
account.addFeed(feed, to: parentFolder) { _ in }
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case .failure(_):
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for url in feedsToAdd {
|
||||||
|
group.enter()
|
||||||
|
caller.createSubscription(url: url) { result in
|
||||||
|
group.leave()
|
||||||
|
switch result {
|
||||||
|
case .success(let subResult):
|
||||||
|
switch subResult {
|
||||||
|
case .created(let subscription):
|
||||||
|
let feed = account.createFeed(with: subscription.name, url: subscription.url, feedID: String(subscription.feedID), homePageURL: subscription.homePageURL)
|
||||||
|
feed.subscriptionID = String(subscription.feedID)
|
||||||
|
account.addFeed(feed)
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case .failure(_):
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
group.notify(queue: DispatchQueue.main) {
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.refreshAll(for: account) { (_) in }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addFolder(for account: Account, name: String, completion: @escaping (Result<Folder, Error>) -> Void) {
|
func addFolder(for account: Account, name: String, completion: @escaping (Result<Folder, Error>) -> Void) {
|
||||||
|
@ -498,41 +580,6 @@ private extension GoogleReaderCompatibleAccountDelegate {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkImportResult(opmlImportResultID: Int, completion: @escaping (Result<Void, Error>) -> Void) {
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
|
|
||||||
Timer.scheduledTimer(withTimeInterval: 15, repeats: true) { timer in
|
|
||||||
|
|
||||||
os_log(.debug, log: self.log, "Checking status of OPML import...")
|
|
||||||
|
|
||||||
self.caller.retrieveOPMLImportResult(importID: opmlImportResultID) { result in
|
|
||||||
switch result {
|
|
||||||
case .success(let importResult):
|
|
||||||
if let result = importResult, result.complete {
|
|
||||||
os_log(.debug, log: self.log, "Checking status of OPML import successfully completed.")
|
|
||||||
timer.invalidate()
|
|
||||||
self.opmlImportInProgress = false
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
completion(.success(()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case .failure(let error):
|
|
||||||
os_log(.debug, log: self.log, "Import OPML check failed.")
|
|
||||||
timer.invalidate()
|
|
||||||
self.opmlImportInProgress = false
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
completion(.failure(error))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func syncFolders(_ account: Account, _ tags: [GoogleReaderCompatibleTag]?) {
|
func syncFolders(_ account: Account, _ tags: [GoogleReaderCompatibleTag]?) {
|
||||||
|
|
||||||
guard let tags = tags else { return }
|
guard let tags = tags else { return }
|
||||||
|
|
Loading…
Reference in New Issue