From 139df9ad51472c9c4eab9427aa172969fb8f9924 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Fri, 14 Aug 2020 19:31:54 -0500 Subject: [PATCH] Initial support for Twitter lists --- .../Twitter/TwitterFeedProvider.swift | 56 ++++++++++++++++--- .../FeedProvider/Twitter/TwitterList.swift | 17 ++++++ 2 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 Account/Sources/Account/FeedProvider/Twitter/TwitterList.swift diff --git a/Account/Sources/Account/FeedProvider/Twitter/TwitterFeedProvider.swift b/Account/Sources/Account/FeedProvider/Twitter/TwitterFeedProvider.swift index 7ab04827b..1e6a5a5d9 100644 --- a/Account/Sources/Account/FeedProvider/Twitter/TwitterFeedProvider.swift +++ b/Account/Sources/Account/FeedProvider/Twitter/TwitterFeedProvider.swift @@ -158,6 +158,21 @@ public final class TwitterFeedProvider: FeedProvider { default: if let hashtag = deriveHashtag(urlComponents) { completion(.success(FeedProviderFeedMetaData(name: "#\(hashtag)", homePageURL: Self.homeURL))) + } else if let listID = deriveListID(urlComponents) { + retrieveList(listID: listID) { result in + switch result { + case .success(let list): + if let userName = list.name { + var urlComponents = URLComponents(string: Self.homeURL) + urlComponents?.path = "/i/lists/\(listID)" + completion(.success(FeedProviderFeedMetaData(name: userName, homePageURL: urlComponents?.url?.absoluteString))) + } else { + completion(.failure(TwitterFeedProviderError.screenNameNotFound)) + } + case .failure(let error): + completion(.failure(error)) + } + } } else if let screenName = deriveScreenName(urlComponents) { retrieveUser(screenName: screenName) { result in switch result { @@ -211,15 +226,16 @@ public final class TwitterFeedProvider: FeedProvider { api = "search/tweets.json" parameters["q"] = "#\(hashtag)" isSearch = true - } else { + } else if let listID = deriveListID(urlComponents) { + api = "lists/statuses.json" + parameters["list_id"] = listID + } else if let screenName = deriveScreenName(urlComponents) { api = "statuses/user_timeline.json" parameters["exclude_replies"] = true - if let screenName = deriveScreenName(urlComponents) { - parameters["screen_name"] = screenName - } else { - completion(.failure(TwitterFeedProviderError.unknown)) - return - } + parameters["screen_name"] = screenName + } else { + completion(.failure(TwitterFeedProviderError.unknown)) + return } } @@ -319,6 +335,12 @@ private extension TwitterFeedProvider { } } + func deriveListID(_ urlComponents: URLComponents) -> String? { + let path = urlComponents.path + guard path.starts(with: "/i/lists/") else { return nil } + return String(path.suffix(from: path.index(path.startIndex, offsetBy: 9))) + } + func retrieveUser(screenName: String, completion: @escaping (Result) -> Void) { let url = "\(Self.apiBase)users/show.json" let parameters = ["screen_name": screenName] @@ -339,6 +361,26 @@ private extension TwitterFeedProvider { } } + func retrieveList(listID: String, completion: @escaping (Result) -> Void) { + let url = "\(Self.apiBase)lists/show.json" + let parameters = ["list_id": listID] + + client.get(url, parameters: parameters, headers: Self.userAgentHeaders) { result in + switch result { + case .success(let response): + let decoder = JSONDecoder() + do { + let collection = try decoder.decode(TwitterList.self, from: response.data) + completion(.success(collection)) + } catch { + completion(.failure(error)) + } + case .failure(let error): + completion(.failure(error)) + } + } + } + func retrieveTweets(api: String, parameters: [String: Any], isSearch: Bool, completion: @escaping (Result<[TwitterStatus], Error>) -> Void) { let url = "\(Self.apiBase)\(api)" var expandedParameters = parameters diff --git a/Account/Sources/Account/FeedProvider/Twitter/TwitterList.swift b/Account/Sources/Account/FeedProvider/Twitter/TwitterList.swift new file mode 100644 index 000000000..96427fe94 --- /dev/null +++ b/Account/Sources/Account/FeedProvider/Twitter/TwitterList.swift @@ -0,0 +1,17 @@ +// +// TwitterList.swift +// +// +// Created by Maurice Parker on 8/14/20. +// + +import Foundation + +struct TwitterList: Codable { + + let name: String? + + enum CodingKeys: String, CodingKey { + case name = "name" + } +}