Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Subscribe and unsubscribe to talk page topics #4324

Merged
merged 9 commits into from Sep 22, 2022
4 changes: 3 additions & 1 deletion Wikipedia/Code/TalkPageCellViewModel.swift
Expand Up @@ -7,6 +7,7 @@ final class TalkPageCellViewModel {

let topicTitle: String
let timestamp: Date?
let topicName: String

let id: String
var leadComment: TalkPageCellCommentViewModel
Expand All @@ -21,10 +22,11 @@ final class TalkPageCellViewModel {

let isUserLoggedIn: Bool

init(id: String, topicTitle: String, timestamp: Date?, leadComment: TalkPageCellCommentViewModel, replies: [TalkPageCellCommentViewModel], activeUsersCount: String, isUserLoggedIn: Bool) {
init(id: String, topicTitle: String, timestamp: Date?, topicName: String, leadComment: TalkPageCellCommentViewModel, replies: [TalkPageCellCommentViewModel], activeUsersCount: String, isUserLoggedIn: Bool) {
self.id = id
self.topicTitle = topicTitle
self.timestamp = timestamp
self.topicName = topicName
self.leadComment = leadComment
self.replies = replies
self.activeUsersCount = activeUsersCount
Expand Down
51 changes: 38 additions & 13 deletions Wikipedia/Code/TalkPageDataController.swift
Expand Up @@ -20,7 +20,7 @@ class TalkPageDataController {

// MARK: Public

typealias TalkPageResult = Result<(articleSummary: WMFArticle?, items: [TalkPageItem]), Error>
typealias TalkPageResult = Result<(articleSummary: WMFArticle?, items: [TalkPageItem], subscribedTopicNames: [String]), Error>

func fetchTalkPage(completion: @escaping (TalkPageResult) -> Void) {

Expand All @@ -31,6 +31,7 @@ class TalkPageDataController {
var finalErrors: [Error] = []
var finalItems: [TalkPageItem] = []
var finalArticleSummary: WMFArticle?
var finalSubscribedTopics: [String] = []

fetchTalkPageItems(dispatchGroup: group) { items, errors in
finalItems = items
Expand All @@ -42,15 +43,21 @@ class TalkPageDataController {
finalErrors.append(contentsOf: errors)
}


group.notify(queue: DispatchQueue.main, execute: {

if let firstError = finalErrors.first {
completion(.failure(firstError))
return
}

completion(.success((finalArticleSummary, finalItems)))
self.fetchTopicSubscriptions(for: finalItems, dispatchGroup: group) { items, errors in
finalSubscribedTopics = items
finalErrors.append(contentsOf: errors)
completion(.success((finalArticleSummary, finalItems, finalSubscribedTopics)))
}

})

}

func postReply(commentId: String, comment: String, completion: @escaping(Result<Void, Error>) -> Void) {
Expand All @@ -72,22 +79,40 @@ class TalkPageDataController {
}

func subscribeToTopic(topicName: String, shouldSubscribe: Bool, completion: @escaping (Result<Bool, Error>) -> Void) {
talkPageFetcher.subscribeToTopic(talkPageTitle: pageTitle, siteURL: siteURL, topic: topicName, shouldSubscribe: shouldSubscribe, completion: completion)
}

func fetchSubscriptions(for topics: [String], completion: @escaping (Result<[String], Error>) -> Void) {
talkPageFetcher.getSubscribedTopics(siteURL: siteURL, topics: topics) { result in
switch result {
case let .success(result):
completion(.success(result))
case let .failure(error):
completion(.failure(error))

talkPageFetcher.subscribeToTopic(talkPageTitle: pageTitle, siteURL: siteURL, topic: topicName, shouldSubscribe: shouldSubscribe) { result in
DispatchQueue.main.async {
completion(result)
}
}
}

// MARK: Private

private func fetchTopicSubscriptions(for items: [TalkPageItem], dispatchGroup group: DispatchGroup, completion: @escaping ([String], [Error]) -> Void) {

var topicNames = [String]()
for item in items {
if let itemName = item.name {
topicNames.append(itemName)
}
}


talkPageFetcher.getSubscribedTopics(siteURL: siteURL, topics: topicNames) { result in

DispatchQueue.main.async {
switch result {
case let .success(result):
completion(result, [])
case let .failure(error):
completion([], [error])
}
}

}
}

private func fetchTalkPageItems(dispatchGroup group: DispatchGroup, completion: @escaping ([TalkPageItem], [Error]) -> Void) {

group.enter()
Expand Down
5 changes: 5 additions & 0 deletions Wikipedia/Code/TalkPageFetcher.swift
Expand Up @@ -145,6 +145,11 @@ class TalkPageFetcher: Fetcher {
}
}

/// Returns a list of active talk page topics subscription
/// - Parameters:
/// - siteURL: URL for the talk page, takes a URL object
/// - topics: Expects a array of Strings containing the `name` value from `TalkPageItem`
/// - completion: Returns either and array with the the `name` property of subscribed topics or an Error
func getSubscribedTopics(siteURL: URL, topics: [String], completion: @escaping (Result<[String], Error>) -> Void) {

let joinedString = topics.joined(separator: "|")
Expand Down
20 changes: 18 additions & 2 deletions Wikipedia/Code/TalkPageViewController.swift
Expand Up @@ -368,9 +368,25 @@ extension TalkPageViewController: TalkPageCellDelegate {
}

let configuredCellViewModel = viewModel.topics[indexOfConfiguredCell]
configuredCellViewModel.isSubscribed.toggle()

let shouldSubscribe = !configuredCellViewModel.isSubscribed
cellViewModel.isSubscribed.toggle()
cell.configure(viewModel: configuredCellViewModel, linkDelegate: self)
self.handleSubscriptionAlert(isSubscribedToTopic: configuredCellViewModel.isSubscribed)

viewModel.subscribe(to: configuredCellViewModel.topicName, shouldSubscribe: shouldSubscribe) { result in
switch result {
case let .success(didSubscribe):
self.handleSubscriptionAlert(isSubscribedToTopic: didSubscribe)
case let .failure(error):
cellViewModel.isSubscribed.toggle()
if cell.viewModel?.topicName == cellViewModel.topicName {
cell.configure(viewModel: cellViewModel, linkDelegate: self)
}
DDLogError("Error subscribing to topic: \(error)")
// TODO: Error handling
}
}

}
}

Expand Down
32 changes: 23 additions & 9 deletions Wikipedia/Code/TalkPageViewModel.swift
Expand Up @@ -76,6 +76,7 @@ final class TalkPageViewModel {
let oldViewModels: [TalkPageCellViewModel] = self.topics
self.topics.removeAll()
self.populateCellData(topics: result.items, oldViewModels: oldViewModels)
self.updateSubscriptionForTopic(topicNames: result.subscribedTopicNames)
completion(.success(()))
case .failure(let error):
DDLogError("Failure fetching talk page: \(error)")
Expand All @@ -84,27 +85,35 @@ final class TalkPageViewModel {
}
}
}

func postTopic(topicTitle: String, topicBody: String, completion: @escaping(Result<Void, Error>) -> Void) {
dataController.postTopic(topicTitle: topicTitle, topicBody: topicBody, completion: completion)
}

func postReply(commentId: String, comment: String, completion: @escaping(Result<Void, Error>) -> Void) {
dataController.postReply(commentId: commentId, comment: comment, completion: completion)
}


func updateSubscriptionToTopic(topic: String, shouldSubscribe: Bool, completion: @escaping (Result<Bool, Error>) -> Void) {
dataController.subscribeToTopic(topicName: topic, shouldSubscribe: shouldSubscribe) { [self] result in
func subscribe(to topic: String, shouldSubscribe: Bool, completion: @escaping (Result<Bool, Error>) -> Void) {
dataController.subscribeToTopic(topicName: topic, shouldSubscribe: shouldSubscribe) { result in
switch result {
case let .success(result) :
let topicUpdated = topics.filter { $0.topicTitle == topic}
topicUpdated[0].isSubscribed = result
completion(.success(result))
case let .failure(error):
completion(.failure(error))
}
}
}

func postReply(commentId: String, comment: String, completion: @escaping(Result<Void, Error>) -> Void) {
dataController.postReply(commentId: commentId, comment: comment, completion: completion)
func updateSubscriptionForTopic(topicNames: [String]) {
for topic in topics {
for id in topicNames {
if topic.topicName == id {
topic.isSubscribed = true
}
}
}
}

// MARK: - Private
Expand Down Expand Up @@ -166,8 +175,13 @@ final class TalkPageViewModel {

let activeUsersCount = activeUsersCount(topic: topic)

let topicViewModel = TalkPageCellViewModel(id: topic.id, topicTitle: topicTitle, timestamp: firstReply.timestamp, leadComment: leadCommentViewModel, replies: remainingCommentViewModels, activeUsersCount: activeUsersCount, isUserLoggedIn: isUserLoggedIn)

guard let topicName = topic.name else {
DDLogError("Unable to parse topic name")
continue
}

let topicViewModel = TalkPageCellViewModel(id: topic.id, topicTitle: topicTitle, timestamp: firstReply.timestamp, topicName: topicName, leadComment: leadCommentViewModel, replies: remainingCommentViewModels, activeUsersCount: activeUsersCount, isUserLoggedIn: isUserLoggedIn)

// Note this is a nested loop, so it will not perform well with many topics.
// Talk pages generally have a limited number of topics, so optimize later if we determine it's needed
assignExpandedFlag(to: topicViewModel, from: oldViewModels)
Expand Down