diff --git a/Frameworks/Account/Account.xcodeproj/project.pbxproj b/Frameworks/Account/Account.xcodeproj/project.pbxproj index 338b98813..62d8daf90 100644 --- a/Frameworks/Account/Account.xcodeproj/project.pbxproj +++ b/Frameworks/Account/Account.xcodeproj/project.pbxproj @@ -111,6 +111,9 @@ 9E44C90F23C6FF3600CCC286 /* FeedlyIngestStreamArticleIdsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E44C90E23C6FF3600CCC286 /* FeedlyIngestStreamArticleIdsOperation.swift */; }; 9E5ABE9A236BE6BD00B5DE9F /* feedly-1-initial in Resources */ = {isa = PBXBuildFile; fileRef = 9E5ABE99236BE6BC00B5DE9F /* feedly-1-initial */; }; 9E5DE60E23C3F4B70064DA30 /* FeedlyFetchIdsForMissingArticlesOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5DE60D23C3F4B70064DA30 /* FeedlyFetchIdsForMissingArticlesOperation.swift */; }; + 9E5EC15923E01D8A00A4E503 /* FeedlyCollectionParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5EC15823E01D8A00A4E503 /* FeedlyCollectionParser.swift */; }; + 9E5EC15B23E01DEF00A4E503 /* FeedlyRTLTextSanitizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5EC15A23E01DEF00A4E503 /* FeedlyRTLTextSanitizer.swift */; }; + 9E5EC15D23E0D58500A4E503 /* FeedlyFeedParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5EC15C23E0D58500A4E503 /* FeedlyFeedParser.swift */; }; 9E672394236F7CA0000BE141 /* FeedlyRefreshAccessTokenOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E672393236F7CA0000BE141 /* FeedlyRefreshAccessTokenOperation.swift */; }; 9E672396236F7E68000BE141 /* OAuthAcessTokenRefreshing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E672395236F7E68000BE141 /* OAuthAcessTokenRefreshing.swift */; }; 9E7299D723505E9600DAEFB7 /* FeedlyAddFeedToCollectionOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7299D623505E9600DAEFB7 /* FeedlyAddFeedToCollectionOperation.swift */; }; @@ -164,6 +167,10 @@ 9EF1B10723590D61000A486A /* FeedlyGetStreamIdsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF1B10623590D61000A486A /* FeedlyGetStreamIdsOperation.swift */; }; 9EF1B10923590E93000A486A /* FeedlyStreamIds.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF1B10823590E93000A486A /* FeedlyStreamIds.swift */; }; 9EF2602C23C91FFE006D160C /* FeedlyGetUpdatedArticleIdsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF2602B23C91FFE006D160C /* FeedlyGetUpdatedArticleIdsOperation.swift */; }; + 9EF58EB023E1606000992A2B /* FeedlyTextSanitizationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF58EAF23E1606000992A2B /* FeedlyTextSanitizationTests.swift */; }; + 9EF58EB223E1647400992A2B /* FeedlyCollectionParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF58EB123E1647400992A2B /* FeedlyCollectionParserTests.swift */; }; + 9EF58EB423E1655300992A2B /* FeedlyFeedParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF58EB323E1655300992A2B /* FeedlyFeedParserTests.swift */; }; + 9EF58EB623E1669F00992A2B /* FeedlyEntryParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF58EB523E1669F00992A2B /* FeedlyEntryParserTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -327,6 +334,9 @@ 9E489E92236101FC004372EE /* FeedlyUpdateAccountFeedsWithItemsOperationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyUpdateAccountFeedsWithItemsOperationTests.swift; sourceTree = ""; }; 9E5ABE99236BE6BC00B5DE9F /* feedly-1-initial */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "feedly-1-initial"; sourceTree = ""; }; 9E5DE60D23C3F4B70064DA30 /* FeedlyFetchIdsForMissingArticlesOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyFetchIdsForMissingArticlesOperation.swift; sourceTree = ""; }; + 9E5EC15823E01D8A00A4E503 /* FeedlyCollectionParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyCollectionParser.swift; sourceTree = ""; }; + 9E5EC15A23E01DEF00A4E503 /* FeedlyRTLTextSanitizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyRTLTextSanitizer.swift; sourceTree = ""; }; + 9E5EC15C23E0D58500A4E503 /* FeedlyFeedParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyFeedParser.swift; sourceTree = ""; }; 9E672393236F7CA0000BE141 /* FeedlyRefreshAccessTokenOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyRefreshAccessTokenOperation.swift; sourceTree = ""; }; 9E672395236F7E68000BE141 /* OAuthAcessTokenRefreshing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuthAcessTokenRefreshing.swift; sourceTree = ""; }; 9E7299D623505E9600DAEFB7 /* FeedlyAddFeedToCollectionOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyAddFeedToCollectionOperation.swift; sourceTree = ""; }; @@ -386,6 +396,10 @@ 9EF1B10623590D61000A486A /* FeedlyGetStreamIdsOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyGetStreamIdsOperation.swift; sourceTree = ""; }; 9EF1B10823590E93000A486A /* FeedlyStreamIds.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyStreamIds.swift; sourceTree = ""; }; 9EF2602B23C91FFE006D160C /* FeedlyGetUpdatedArticleIdsOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyGetUpdatedArticleIdsOperation.swift; sourceTree = ""; }; + 9EF58EAF23E1606000992A2B /* FeedlyTextSanitizationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyTextSanitizationTests.swift; sourceTree = ""; }; + 9EF58EB123E1647400992A2B /* FeedlyCollectionParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyCollectionParserTests.swift; sourceTree = ""; }; + 9EF58EB323E1655300992A2B /* FeedlyFeedParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyFeedParserTests.swift; sourceTree = ""; }; + 9EF58EB523E1669F00992A2B /* FeedlyEntryParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyEntryParserTests.swift; sourceTree = ""; }; D511EEB5202422BB00712EC3 /* Account_project_debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Account_project_debug.xcconfig; sourceTree = ""; }; D511EEB6202422BB00712EC3 /* Account_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Account_target.xcconfig; sourceTree = ""; }; D511EEB7202422BB00712EC3 /* Account_project_release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Account_project_release.xcconfig; sourceTree = ""; }; @@ -652,6 +666,10 @@ 9E0260CA236FF99A00D122D3 /* FeedlyRefreshAccessTokenOperationTests.swift */, 9E784EBF237E8BE100099B1B /* FeedlyLogoutOperationTests.swift */, 9EA643D823945CE00018A28C /* FeedlyAddNewFeedOperationTests.swift */, + 9EF58EAF23E1606000992A2B /* FeedlyTextSanitizationTests.swift */, + 9EF58EB123E1647400992A2B /* FeedlyCollectionParserTests.swift */, + 9EF58EB323E1655300992A2B /* FeedlyFeedParserTests.swift */, + 9EF58EB523E1669F00992A2B /* FeedlyEntryParserTests.swift */, 9E79F7732395C9EF0031DB98 /* feedly-add-new-feed */, 9E5ABE99236BE6BC00B5DE9F /* feedly-1-initial */, 9EC804E4236C1A7F0057CFCB /* feedly-2-changestatuses */, @@ -720,7 +738,9 @@ children = ( 9E1773D823458D590056A5A8 /* FeedlyResourceId.swift */, 9EAEC60B2332FE830085D7C9 /* FeedlyCollection.swift */, + 9E5EC15823E01D8A00A4E503 /* FeedlyCollectionParser.swift */, 9EAEC60D2332FEC20085D7C9 /* FeedlyFeed.swift */, + 9E5EC15C23E0D58500A4E503 /* FeedlyFeedParser.swift */, 9EAEC623233315F60085D7C9 /* FeedlyEntry.swift */, 9E1773D4234570E30056A5A8 /* FeedlyEntryParser.swift */, 9EAEC625233318400085D7C9 /* FeedlyStream.swift */, @@ -731,6 +751,7 @@ 9E1773D6234575AB0056A5A8 /* FeedlyTag.swift */, 9EA643D4239306AC0018A28C /* FeedlyFeedsSearchResponse.swift */, 9EBD49C123C67784005AD5CD /* FeedlyEntryIdentifierProviding.swift */, + 9E5EC15A23E01DEF00A4E503 /* FeedlyRTLTextSanitizer.swift */, ); path = Models; sourceTree = ""; @@ -977,10 +998,12 @@ 51E5959B228C781500FCC42B /* FeedbinStarredEntry.swift in Sources */, 846E77451F6EF9B900A165E2 /* Container.swift in Sources */, 9EA643D3239305680018A28C /* FeedlySearchOperation.swift in Sources */, + 9E5EC15D23E0D58500A4E503 /* FeedlyFeedParser.swift in Sources */, 9E1D15532334304B00F4944C /* FeedlyGetStreamContentsOperation.swift in Sources */, 9E12B0202334696A00ADE5A0 /* FeedlyCreateFeedsForCollectionFoldersOperation.swift in Sources */, 552032FD229D5D5A009559E0 /* ReaderAPITagging.swift in Sources */, 9EAEC62A23331EE70085D7C9 /* FeedlyOrigin.swift in Sources */, + 9E5EC15B23E01DEF00A4E503 /* FeedlyRTLTextSanitizer.swift in Sources */, 511B9804237CD4270028BCAA /* FeedIdentifier.swift in Sources */, 84F73CF1202788D90000BCEF /* ArticleFetcher.swift in Sources */, 841974251F6DDCE4006346C4 /* AccountDelegate.swift in Sources */, @@ -995,6 +1018,7 @@ 9EEAE06E235D002D00E3FEE4 /* FeedlyGetCollectionsService.swift in Sources */, 5165D72922835F7A00D9D53D /* FeedSpecifier.swift in Sources */, 9E85C8ED2367020700D0F1F7 /* FeedlyGetEntriesService.swift in Sources */, + 9E5EC15923E01D8A00A4E503 /* FeedlyCollectionParser.swift in Sources */, 9E84DC492359A73600D6E809 /* FeedlyCheckpointOperation.swift in Sources */, 9E85C8EB236700E600D0F1F7 /* FeedlyGetEntriesOperation.swift in Sources */, 9E1D154D233370D800F4944C /* FeedlySyncAllOperation.swift in Sources */, @@ -1081,6 +1105,7 @@ 9EC228572362C7F900766EF8 /* FeedlyCheckpointOperationTests.swift in Sources */, 9E03C122235E62E100FB6D9E /* FeedlyTestSupport.swift in Sources */, 9EAADA1023C93144003A801F /* TestGetEntriesService.swift in Sources */, + 9EF58EB423E1655300992A2B /* FeedlyFeedParserTests.swift in Sources */, 9E1FF8622368219B00834C24 /* TestGetPagedStreamIdsService.swift in Sources */, 9E03C11E235D976500FB6D9E /* FeedlyGetCollectionsOperationTests.swift in Sources */, 9E85C8E62366FED600D0F1F7 /* TestGetStreamContentsService.swift in Sources */, @@ -1088,14 +1113,17 @@ 9E03C11C235D921400FB6D9E /* FeedlyOperationTests.swift in Sources */, 9E1FF8602368216B00834C24 /* TestGetStreamIdsService.swift in Sources */, 9E85C8E82366FF4200D0F1F7 /* TestGetPagedStreamContentsService.swift in Sources */, + 9EF58EB023E1606000992A2B /* FeedlyTextSanitizationTests.swift in Sources */, 5165D7122282080C00D9D53D /* AccountFeedbinFolderContentsSyncTest.swift in Sources */, 51D5875E227F643C00900287 /* AccountFeedbinFolderSyncTest.swift in Sources */, + 9EF58EB623E1669F00992A2B /* FeedlyEntryParserTests.swift in Sources */, 9EC804E3236C18AB0057CFCB /* FeedlySyncAllMockResponseProvider.swift in Sources */, 9E1FF8682368EE4900834C24 /* TestGetCollectionsService.swift in Sources */, 5107A09B227DE49500C7C3C5 /* TestAccountManager.swift in Sources */, 513323082281070D00C30F19 /* AccountFeedbinSyncTest.swift in Sources */, 5107A09D227DE77700C7C3C5 /* TestTransport.swift in Sources */, 9E1773DB234593CF0056A5A8 /* FeedlyResourceIdTests.swift in Sources */, + 9EF58EB223E1647400992A2B /* FeedlyCollectionParserTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Frameworks/Account/AccountTests/Feedly/FeedlyCollectionParserTests.swift b/Frameworks/Account/AccountTests/Feedly/FeedlyCollectionParserTests.swift new file mode 100644 index 000000000..3c732a821 --- /dev/null +++ b/Frameworks/Account/AccountTests/Feedly/FeedlyCollectionParserTests.swift @@ -0,0 +1,28 @@ +// +// FeedlyCollectionParserTests.swift +// AccountTests +// +// Created by Kiel Gillard on 29/1/20. +// Copyright © 2020 Ranchero Software, LLC. All rights reserved. +// + +import XCTest +@testable import Account + +class FeedlyCollectionParserTests: XCTestCase { + + func testParsing() { + let collection = FeedlyCollection(feeds: [], label: "Test Collection", id: "test/collection/1") + let parser = FeedlyCollectionParser(collection: collection) + XCTAssertEqual(parser.folderName, collection.label) + XCTAssertEqual(parser.externalID, collection.id) + } + + func testSanitization() { + let name = "Test Collection" + let collection = FeedlyCollection(feeds: [], label: "
\(name)
", id: "test/collection/1") + let parser = FeedlyCollectionParser(collection: collection) + XCTAssertEqual(parser.folderName, name) + XCTAssertEqual(parser.externalID, collection.id) + } +} diff --git a/Frameworks/Account/AccountTests/Feedly/FeedlyEntryParserTests.swift b/Frameworks/Account/AccountTests/Feedly/FeedlyEntryParserTests.swift new file mode 100644 index 000000000..e62a18e6b --- /dev/null +++ b/Frameworks/Account/AccountTests/Feedly/FeedlyEntryParserTests.swift @@ -0,0 +1,121 @@ +// +// FeedlyEntryParserTests.swift +// AccountTests +// +// Created by Kiel Gillard on 29/1/20. +// Copyright © 2020 Ranchero Software, LLC. All rights reserved. +// + +import XCTest +@testable import Account + +class FeedlyEntryParserTests: XCTestCase { + + func testParsing() { + let content = FeedlyEntry.Content(content: "Test Content", direction: .leftToRight) + let summary = FeedlyEntry.Content(content: "Test Summary", direction: .leftToRight) + let origin = FeedlyOrigin(title: "Test Feed", streamId: "tests://feeds/1", htmlUrl: nil) + let entry = FeedlyEntry(id: "tests/feeds/1/entries/1", + title: "Test Entry 1", + content: content, + summary: summary, + author: nil, + crawled: .distantPast, + recrawled: Date(timeIntervalSinceReferenceDate: 0), + origin: origin, + canonical: nil, + alternate: nil, + unread: false, + tags: nil, + categories: nil, + enclosure: nil) + + let parser = FeedlyEntryParser(entry: entry) + + XCTAssertEqual(parser.id, entry.id) + XCTAssertEqual(parser.feedUrl, origin.streamId) + XCTAssertEqual(parser.externalUrl, nil) + XCTAssertEqual(parser.title, entry.title) + XCTAssertEqual(parser.contentHMTL, content.content) + XCTAssertEqual(parser.summary, summary.content) + XCTAssertEqual(parser.datePublished, .distantPast) + XCTAssertEqual(parser.dateModified, Date(timeIntervalSinceReferenceDate: 0)) + } + + func testSanitization() { + let content = FeedlyEntry.Content(content: "
Test Content
", direction: .rightToLeft) + let summaryContent = "Test Summary" + let summary = FeedlyEntry.Content(content: "
\(summaryContent)
", direction: .rightToLeft) + let origin = FeedlyOrigin(title: "Test Feed", streamId: "tests://feeds/1", htmlUrl: nil) + let title = "Test Entry 1" + let entry = FeedlyEntry(id: "tests/feeds/1/entries/1", + title: "
\(title)
", + content: content, + summary: summary, + author: nil, + crawled: .distantPast, + recrawled: nil, + origin: origin, + canonical: nil, + alternate: nil, + unread: false, + tags: nil, + categories: nil, + enclosure: nil) + + let parser = FeedlyEntryParser(entry: entry) + + // These should be sanitized + XCTAssertEqual(parser.title, title) + XCTAssertEqual(parser.summary, summaryContent) + + // These should not be sanitized because it is supposed to be HTML content. + XCTAssertEqual(parser.contentHMTL, content.content) + } + + func testLocatesCanonicalExternalUrl() { + let canonicalLink = FeedlyLink(href: "tests://feeds/1/entries/1", type: "text/html") + let alternateLink = FeedlyLink(href: "tests://feeds/1/entries/alternate/1", type: "text/html") + let entry = FeedlyEntry(id: "tests/feeds/1/entries/1", + title: "Test Entry 1", + content: nil, + summary: nil, + author: nil, + crawled: .distantPast, + recrawled: Date(timeIntervalSinceReferenceDate: 0), + origin: nil, + canonical: [canonicalLink], + alternate: [alternateLink], + unread: false, + tags: nil, + categories: nil, + enclosure: nil) + + let parser = FeedlyEntryParser(entry: entry) + + XCTAssertEqual(parser.externalUrl, canonicalLink.href) + } + + func testLocatesAlternateExternalUrl() { + let canonicalLink = FeedlyLink(href: "tests://feeds/1/entries/1", type: "text/json") + let alternateLink = FeedlyLink(href: "tests://feeds/1/entries/alternate/1", type: nil) + let entry = FeedlyEntry(id: "tests/feeds/1/entries/1", + title: "Test Entry 1", + content: nil, + summary: nil, + author: nil, + crawled: .distantPast, + recrawled: Date(timeIntervalSinceReferenceDate: 0), + origin: nil, + canonical: [canonicalLink], + alternate: [alternateLink], + unread: false, + tags: nil, + categories: nil, + enclosure: nil) + + let parser = FeedlyEntryParser(entry: entry) + + XCTAssertEqual(parser.externalUrl, alternateLink.href) + } +} diff --git a/Frameworks/Account/AccountTests/Feedly/FeedlyFeedParserTests.swift b/Frameworks/Account/AccountTests/Feedly/FeedlyFeedParserTests.swift new file mode 100644 index 000000000..8ddd4aaec --- /dev/null +++ b/Frameworks/Account/AccountTests/Feedly/FeedlyFeedParserTests.swift @@ -0,0 +1,41 @@ +// +// FeedlyFeedParserTests.swift +// AccountTests +// +// Created by Kiel Gillard on 29/1/20. +// Copyright © 2020 Ranchero Software, LLC. All rights reserved. +// + +import XCTest +@testable import Account + +class FeedlyFeedParserTests: XCTestCase { + + func testParsing() { + let name = "Test Feed" + let website = "tests://nnw/feed/1" + let url = "tests://nnw/feed.xml" + let id = "feed/\(url)" + let updated = Date.distantPast + let feed = FeedlyFeed(id: id, title: name, updated: updated, website: website) + let parser = FeedlyFeedParser(feed: feed) + XCTAssertEqual(parser.title, name) + XCTAssertEqual(parser.homePageURL, website) + XCTAssertEqual(parser.url, url) + XCTAssertEqual(parser.webFeedID, id) + } + + func testSanitization() { + let name = "Test Feed" + let website = "tests://nnw/feed/1" + let url = "tests://nnw/feed.xml" + let id = "feed/\(url)" + let updated = Date.distantPast + let feed = FeedlyFeed(id: id, title: "
\(name)
", updated: updated, website: website) + let parser = FeedlyFeedParser(feed: feed) + XCTAssertEqual(parser.title, name) + XCTAssertEqual(parser.homePageURL, website) + XCTAssertEqual(parser.url, url) + XCTAssertEqual(parser.webFeedID, id) + } +} diff --git a/Frameworks/Account/AccountTests/Feedly/FeedlyTextSanitizationTests.swift b/Frameworks/Account/AccountTests/Feedly/FeedlyTextSanitizationTests.swift new file mode 100644 index 000000000..ae9eefba0 --- /dev/null +++ b/Frameworks/Account/AccountTests/Feedly/FeedlyTextSanitizationTests.swift @@ -0,0 +1,38 @@ +// +// FeedlyTextSanitizationTests.swift +// AccountTests +// +// Created by Kiel Gillard on 29/1/20. +// Copyright © 2020 Ranchero Software, LLC. All rights reserved. +// + +import XCTest +@testable import Account + +class FeedlyTextSanitizationTests: XCTestCase { + + func testRTLSanitization() { + + let targetsAndExpectations: [(target: String?, expectation: String?)] = [ + (nil, nil), + ("", ""), + (" ", " "), + ("text", "text"), + ("
", "
"), + ("
", "
"), + ("
text", "
text"), + ("text
", "text
"), + ("
", ""), + ("
", "
"), + ("
", "
"), + ("
text
", "text"), + ] + + let sanitizer = FeedlyRTLTextSanitizer() + + for (target, expectation) in targetsAndExpectations { + let calculated = sanitizer.sanitize(target) + XCTAssertEqual(expectation, calculated) + } + } +} diff --git a/Frameworks/Account/Feedly/Models/FeedlyCollectionParser.swift b/Frameworks/Account/Feedly/Models/FeedlyCollectionParser.swift new file mode 100644 index 000000000..229498592 --- /dev/null +++ b/Frameworks/Account/Feedly/Models/FeedlyCollectionParser.swift @@ -0,0 +1,23 @@ +// +// FeedlyCollectionParser.swift +// Account +// +// Created by Kiel Gillard on 28/1/20. +// Copyright © 2020 Ranchero Software, LLC. All rights reserved. +// + +import Foundation + +struct FeedlyCollectionParser { + let collection: FeedlyCollection + + private let rightToLeftTextSantizer = FeedlyRTLTextSanitizer() + + var folderName: String { + return rightToLeftTextSantizer.sanitize(collection.label) ?? "" + } + + var externalID: String { + return collection.id + } +} diff --git a/Frameworks/Account/Feedly/Models/FeedlyEntryParser.swift b/Frameworks/Account/Feedly/Models/FeedlyEntryParser.swift index 06b287806..2e71b3ab8 100644 --- a/Frameworks/Account/Feedly/Models/FeedlyEntryParser.swift +++ b/Frameworks/Account/Feedly/Models/FeedlyEntryParser.swift @@ -13,6 +13,8 @@ import RSParser struct FeedlyEntryParser { let entry: FeedlyEntry + private let rightToLeftTextSantizer = FeedlyRTLTextSanitizer() + var id: String { return entry.id } @@ -36,7 +38,7 @@ struct FeedlyEntryParser { } var title: String? { - return entry.title + return rightToLeftTextSantizer.sanitize(entry.title) } var contentHMTL: String? { @@ -49,7 +51,7 @@ struct FeedlyEntryParser { } var summary: String? { - return entry.summary?.content + return rightToLeftTextSantizer.sanitize(entry.summary?.content) } var datePublished: Date { diff --git a/Frameworks/Account/Feedly/Models/FeedlyFeedParser.swift b/Frameworks/Account/Feedly/Models/FeedlyFeedParser.swift new file mode 100644 index 000000000..e2f288dce --- /dev/null +++ b/Frameworks/Account/Feedly/Models/FeedlyFeedParser.swift @@ -0,0 +1,32 @@ +// +// FeedlyFeedParser.swift +// Account +// +// Created by Kiel Gillard on 29/1/20. +// Copyright © 2020 Ranchero Software, LLC. All rights reserved. +// + +import Foundation + +struct FeedlyFeedParser { + let feed: FeedlyFeed + + private let rightToLeftTextSantizer = FeedlyRTLTextSanitizer() + + var title: String? { + return rightToLeftTextSantizer.sanitize(feed.title) ?? "" + } + + var webFeedID: String { + return feed.id + } + + var url: String { + let resource = FeedlyFeedResourceId(id: feed.id) + return resource.url + } + + var homePageURL: String? { + return feed.website + } +} diff --git a/Frameworks/Account/Feedly/Models/FeedlyRTLTextSanitizer.swift b/Frameworks/Account/Feedly/Models/FeedlyRTLTextSanitizer.swift new file mode 100644 index 000000000..11eb2dcb5 --- /dev/null +++ b/Frameworks/Account/Feedly/Models/FeedlyRTLTextSanitizer.swift @@ -0,0 +1,28 @@ +// +// FeedlyRTLTextSanitizer.swift +// Account +// +// Created by Kiel Gillard on 28/1/20. +// Copyright © 2020 Ranchero Software, LLC. All rights reserved. +// + +import Foundation + +struct FeedlyRTLTextSanitizer { + private let rightToLeftPrefix = "
" + private let rightToLeftSuffix = "
" + + func sanitize(_ sourceText: String?) -> String? { + guard let source = sourceText, !source.isEmpty else { + return sourceText + } + + guard source.hasPrefix(rightToLeftPrefix) && source.hasSuffix(rightToLeftSuffix) else { + return source + } + + let start = source.index(source.startIndex, offsetBy: rightToLeftPrefix.indices.count) + let end = source.index(source.endIndex, offsetBy: -rightToLeftSuffix.indices.count) + return String(source[start.. (FeedlyCollection, Folder)? in - guard let folder = account.ensureFolder(with: collection.label) else { + feedsAndFolders = collections.compactMap { collection -> ([FeedlyFeed], Folder)? in + let parser = FeedlyCollectionParser(collection: collection) + guard let folder = account.ensureFolder(with: parser.folderName) else { assertionFailure("Why wasn't a folder created?") return nil } - folder.externalID = collection.id - return (collection, folder) - } - - collectionsAndFolders = pairs - os_log(.debug, log: log, "Ensured %i folders for %i collections.", pairs.count, collections.count) - - feedsAndFolders = pairs.map { (collection, folder) -> (([FeedlyFeed], Folder)) in + folder.externalID = parser.externalID return (collection.feeds, folder) } - // Remove folders without a corresponding collection - let collectionFolders = Set(pairs.map { $0.1 }) - let foldersWithoutCollections = localFolders.subtracting(collectionFolders) - for unmatched in foldersWithoutCollections { - account.removeFolder(unmatched) - } + os_log(.debug, log: log, "Ensured %i folders for %i collections.", feedsAndFolders.count, collections.count) - os_log(.debug, log: log, "Removed %i folders: %@", foldersWithoutCollections.count, foldersWithoutCollections.map { $0.externalID ?? $0.nameForDisplay }) + // Remove folders without a corresponding collection + let collectionFolders = Set(feedsAndFolders.map { $0.1 }) + let foldersWithoutCollections = localFolders.subtracting(collectionFolders) + + if !foldersWithoutCollections.isEmpty { + for unmatched in foldersWithoutCollections { + account.removeFolder(unmatched) + } + + os_log(.debug, log: log, "Removed %i folders: %@", foldersWithoutCollections.count, foldersWithoutCollections.map { $0.externalID ?? $0.nameForDisplay }) + } } }