diff --git a/Frameworks/Account/Account.xcodeproj/project.pbxproj b/Frameworks/Account/Account.xcodeproj/project.pbxproj index 6d2f140db..a4347fbb9 100644 --- a/Frameworks/Account/Account.xcodeproj/project.pbxproj +++ b/Frameworks/Account/Account.xcodeproj/project.pbxproj @@ -67,6 +67,8 @@ 51B36309244B62A5000DEF2A /* TwitterURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B36308244B62A5000DEF2A /* TwitterURL.swift */; }; 51B3630B244B634A000DEF2A /* TwitterMention.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B3630A244B634A000DEF2A /* TwitterMention.swift */; }; 51B3630D244B6428000DEF2A /* TwitterSymbol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B3630C244B6428000DEF2A /* TwitterSymbol.swift */; }; + 51B3630F244B6CB9000DEF2A /* TwitterExtendedEntities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B3630E244B6CB9000DEF2A /* TwitterExtendedEntities.swift */; }; + 51B36311244B6CFB000DEF2A /* TwitterMedia.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B36310244B6CFA000DEF2A /* TwitterMedia.swift */; }; 51BB7B84233531BC008E8144 /* AccountBehaviors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51BB7B83233531BC008E8144 /* AccountBehaviors.swift */; }; 51BC8FCC237EC055004F8B56 /* Feed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51BC8FCB237EC055004F8B56 /* Feed.swift */; }; 51BFDECE238B508D00216323 /* ContainerIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51BFDECD238B508D00216323 /* ContainerIdentifier.swift */; }; @@ -313,6 +315,8 @@ 51B36308244B62A5000DEF2A /* TwitterURL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwitterURL.swift; sourceTree = ""; }; 51B3630A244B634A000DEF2A /* TwitterMention.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwitterMention.swift; sourceTree = ""; }; 51B3630C244B6428000DEF2A /* TwitterSymbol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwitterSymbol.swift; sourceTree = ""; }; + 51B3630E244B6CB9000DEF2A /* TwitterExtendedEntities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwitterExtendedEntities.swift; sourceTree = ""; }; + 51B36310244B6CFA000DEF2A /* TwitterMedia.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwitterMedia.swift; sourceTree = ""; }; 51BB7B83233531BC008E8144 /* AccountBehaviors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountBehaviors.swift; sourceTree = ""; }; 51BC8FCB237EC055004F8B56 /* Feed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Feed.swift; sourceTree = ""; }; 51BFDECD238B508D00216323 /* ContainerIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerIdentifier.swift; sourceTree = ""; }; @@ -576,14 +580,16 @@ 5132AAC22448BAD90077840A /* Twitter */ = { isa = PBXGroup; children = ( - 5132AAC32448BAD90077840A /* TwitterFeedProvider.swift */, - 5132DE802449159100806ADE /* TwitterUser.swift */, - 5132DE822449306F00806ADE /* TwitterStatus.swift */, 51B36304244B6135000DEF2A /* TwitterEntities.swift */, + 51B3630E244B6CB9000DEF2A /* TwitterExtendedEntities.swift */, + 5132AAC32448BAD90077840A /* TwitterFeedProvider.swift */, 51B36306244B6234000DEF2A /* TwitterHashtag.swift */, - 51B36308244B62A5000DEF2A /* TwitterURL.swift */, + 51B36310244B6CFA000DEF2A /* TwitterMedia.swift */, 51B3630A244B634A000DEF2A /* TwitterMention.swift */, + 5132DE822449306F00806ADE /* TwitterStatus.swift */, 51B3630C244B6428000DEF2A /* TwitterSymbol.swift */, + 51B36308244B62A5000DEF2A /* TwitterURL.swift */, + 5132DE802449159100806ADE /* TwitterUser.swift */, ); path = Twitter; sourceTree = ""; @@ -1191,6 +1197,7 @@ 9EEEF7212355277F009E9D80 /* FeedlyIngestStarredArticleIdsOperation.swift in Sources */, 3BC23AB92385ECB100371CBA /* FeedWranglerSubscriptionResult.swift in Sources */, 5144EA49227B497600D19003 /* FeedbinAPICaller.swift in Sources */, + 51B3630F244B6CB9000DEF2A /* TwitterExtendedEntities.swift in Sources */, 84B99C9F1FAE8D3200ECDEDB /* ContainerPath.swift in Sources */, 51BC8FCC237EC055004F8B56 /* Feed.swift in Sources */, 846E77501F6EF9C400A165E2 /* LocalAccountRefresher.swift in Sources */, @@ -1249,6 +1256,7 @@ 51B3630D244B6428000DEF2A /* TwitterSymbol.swift in Sources */, 769F2BA02EF5F329CDE45F5A /* NewsBlurAPICaller.swift in Sources */, 51C034DF242D65D20014DC71 /* CloudKitZoneResult.swift in Sources */, + 51B36311244B6CFB000DEF2A /* TwitterMedia.swift in Sources */, 179DB28CF49F73A945EBF5DB /* NewsBlurLoginResponse.swift in Sources */, 179DBF4DE2562D4C532F6008 /* NewsBlurFeed.swift in Sources */, 179DB02FFBC17AC9798F0EBC /* NewsBlurStory.swift in Sources */, diff --git a/Frameworks/Account/FeedProvider/Twitter/TwitterExtendedEntities.swift b/Frameworks/Account/FeedProvider/Twitter/TwitterExtendedEntities.swift new file mode 100644 index 000000000..1dedd3052 --- /dev/null +++ b/Frameworks/Account/FeedProvider/Twitter/TwitterExtendedEntities.swift @@ -0,0 +1,28 @@ +// +// TwitterExtendedEntities.swift +// Account +// +// Created by Maurice Parker on 4/18/20. +// Copyright © 2020 Ranchero Software, LLC. All rights reserved. +// + +import Foundation + +struct TwitterExtendedEntities: Codable { + + let medias: [TwitterMedia]? + + enum CodingKeys: String, CodingKey { + case medias = "media" + } + + func renderAsHTML() -> String { + var html = String() + if let medias = medias { + for media in medias { + html += media.renderAsHTML() + } + } + return html + } +} diff --git a/Frameworks/Account/FeedProvider/Twitter/TwitterMedia.swift b/Frameworks/Account/FeedProvider/Twitter/TwitterMedia.swift new file mode 100644 index 000000000..1f83311a7 --- /dev/null +++ b/Frameworks/Account/FeedProvider/Twitter/TwitterMedia.swift @@ -0,0 +1,62 @@ +// +// TwitterMedia.swift +// Account +// +// Created by Maurice Parker on 4/18/20. +// Copyright © 2020 Ranchero Software, LLC. All rights reserved. +// + +import Foundation + +struct TwitterMedia: Codable { + + let idStr: String? + let indices: [Int]? + let mediaURL: String? + let httpsMediaURL: String? + let url: String? + let displayURL: String? + let type: String? + + enum CodingKeys: String, CodingKey { + case idStr = "idStr" + case indices = "indices" + case mediaURL = "media_url" + case httpsMediaURL = "media_url_https" + case url = "url" + case displayURL = "display_url" + case type = "type" + } + + func renderAsHTML() -> String { + var html = String() + + switch type { + case "photo": + if let url = url { + html += "" + html += renderPhotoAsHTML() + html += "" + } + default: + return "" + } + + return html + } + +} + +private extension TwitterMedia { + + func renderPhotoAsHTML() -> String { + if let httpsMediaURL = httpsMediaURL { + return "" + } + if let mediaURL = mediaURL { + return "" + } + return "" + } + +} diff --git a/Frameworks/Account/FeedProvider/Twitter/TwitterStatus.swift b/Frameworks/Account/FeedProvider/Twitter/TwitterStatus.swift index 8ee5d275a..e5d0bb185 100644 --- a/Frameworks/Account/FeedProvider/Twitter/TwitterStatus.swift +++ b/Frameworks/Account/FeedProvider/Twitter/TwitterStatus.swift @@ -20,6 +20,7 @@ final class TwitterStatus: Codable { let retweetedStatus: TwitterStatus? let quotedStatus: TwitterStatus? let entities: TwitterEntities? + let extendedEntities: TwitterExtendedEntities? enum CodingKeys: String, CodingKey { case createdAt = "created_at" @@ -32,6 +33,7 @@ final class TwitterStatus: Codable { case retweetedStatus = "retweeted_status" case quotedStatus = "quoted_status" case entities = "entities" + case extendedEntities = "extended_entities" } var url: String? { @@ -54,40 +56,51 @@ final class TwitterStatus: Codable { return statusToRender.displayText } - func renderAsHTML() -> String? { + func renderAsHTML() -> String { if let retweetedStatus = retweetedStatus { return renderAsRetweetHTML(retweetedStatus) } if let quotedStatus = quotedStatus { return renderAsQuoteHTML(quotedStatus) } - return renderAsTweetHTML(self) + return renderAsOriginalHTML() } - func renderAsTweetHTML(_ status: TwitterStatus) -> String? { - return status.displayText + func renderAsTweetHTML(_ status: TwitterStatus) -> String { + var html = "

" + html += status.displayText ?? "" + html += "

" + return html + } + + func renderAsOriginalHTML() -> String { + var html = renderAsTweetHTML(self) + html += extendedEntities?.renderAsHTML() ?? "" + return html } func renderAsRetweetHTML(_ status: TwitterStatus) -> String { - var html = String() - html += "
" - if let userHTML = status.user?.renderHTML() { + var html = "
" + if let userHTML = status.user?.renderAsHTML() { html += userHTML } - html += renderAsTweetHTML(status) ?? "" + html += renderAsTweetHTML(status) html += "
" + html += status.extendedEntities?.renderAsHTML() ?? "" return html } func renderAsQuoteHTML(_ quotedStatus: TwitterStatus) -> String { var html = String() - html += renderAsTweetHTML(self) ?? "" + html += renderAsTweetHTML(self) html += "
" - if let userHTML = quotedStatus.user?.renderHTML() { + if let userHTML = quotedStatus.user?.renderAsHTML() { html += userHTML } - html += renderAsTweetHTML(quotedStatus) ?? "" + html += renderAsTweetHTML(quotedStatus) html += "
" + html += self.extendedEntities?.renderAsHTML() ?? "" + html += quotedStatus.extendedEntities?.renderAsHTML() ?? "" return html } diff --git a/Frameworks/Account/FeedProvider/Twitter/TwitterUser.swift b/Frameworks/Account/FeedProvider/Twitter/TwitterUser.swift index 1d4942416..58806431c 100644 --- a/Frameworks/Account/FeedProvider/Twitter/TwitterUser.swift +++ b/Frameworks/Account/FeedProvider/Twitter/TwitterUser.swift @@ -24,7 +24,7 @@ struct TwitterUser: Codable { return "https://twitter.com/\(screenName ?? "")" } - func renderHTML() -> String? { + func renderAsHTML() -> String? { var html = String() html += "