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

[MBL-1024] Wire Up Block User Mutation #1893

Merged
merged 11 commits into from
Dec 4, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,9 @@ final class CommentRepliesViewController: UITableViewController, MessageBannerVi
.observeValues { [weak self] _ in
guard let self, let messageBanner = self.messageBannerViewController else { return }

self.commentComposer.isHidden = true
self.delegate?.commentRepliesViewControllerDidBlockUser(self)

messageBanner.showBanner(with: .success, message: Strings.Block_user_success())
scottkicks marked this conversation as resolved.
Show resolved Hide resolved
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ internal final class CommentsViewController: UITableViewController, MessageBanne
.observeValues { [weak self] _ in
guard let self, let messageBanner = self.messageBannerViewController else { return }

self.commentComposer.isHidden = true
messageBanner.showBanner(with: .success, message: Strings.Block_user_success())
}

Expand Down Expand Up @@ -267,7 +268,7 @@ extension CommentsViewController {

extension CommentsViewController: CommentCellDelegate {
func commentCellDidTapHeader(_ cell: CommentCell, _ author: Comment.Author) {
guard AppEnvironment.current.currentUser != nil, featureBlockUsersEnabled() else { return }
// guard AppEnvironment.current.currentUser != nil, featureBlockUsersEnabled() else { return }
scottkicks marked this conversation as resolved.
Show resolved Hide resolved
guard author.isBlocked == false else { return }

let actionSheet = UIAlertController
Expand Down
21 changes: 17 additions & 4 deletions KsApi/MockService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

fileprivate let addPaymentSheetPaymentSourceResult: Result<CreatePaymentSourceEnvelope, ErrorEnvelope>?

fileprivate let blockUserResult: Result<EmptyResponseEnvelope, ErrorEnvelope>?

fileprivate let cancelBackingResult: Result<EmptyResponseEnvelope, ErrorEnvelope>?

fileprivate let changeCurrencyResult: Result<EmptyResponseEnvelope, ErrorEnvelope>?
Expand Down Expand Up @@ -219,6 +221,7 @@
addNewCreditCardResult: Result<CreatePaymentSourceEnvelope, ErrorEnvelope>? = nil,
addPaymentSheetPaymentSourceResult: Result<CreatePaymentSourceEnvelope, ErrorEnvelope>? = nil,
apolloClient: ApolloClientType? = nil,
blockUserResult: Result<EmptyResponseEnvelope, ErrorEnvelope>? = nil,
cancelBackingResult: Result<EmptyResponseEnvelope, ErrorEnvelope>? = nil,
changeEmailResult: Result<EmptyResponseEnvelope, ErrorEnvelope>? = nil,
changePasswordResult: Result<EmptyResponseEnvelope, ErrorEnvelope>? = nil,
Expand Down Expand Up @@ -332,6 +335,8 @@

self.apolloClient = apolloClient ?? MockGraphQLClient.shared.client

self.blockUserResult = blockUserResult

self.cancelBackingResult = cancelBackingResult

self.changeEmailResult = changeEmailResult
Expand Down Expand Up @@ -543,6 +548,18 @@
return client.performWithResult(mutation: mutation, result: self.addPaymentSheetPaymentSourceResult)
}

public func blockUser(input: BlockUserInput)
-> SignalProducer<EmptyResponseEnvelope, ErrorEnvelope> {
guard let client = self.apolloClient else {
return .empty
}

let mutation = GraphAPI
.BlockUserMutation(input: GraphAPI.BlockUserInput(blockUserId: input.blockUserId))

return client.performWithResult(mutation: mutation, result: self.blockUserResult)
}

public func cancelBacking(input: CancelBackingInput)
-> SignalProducer<EmptyResponseEnvelope, ErrorEnvelope> {
guard let client = self.apolloClient else {
Expand Down Expand Up @@ -1681,10 +1698,6 @@
return SignalProducer(value: self.fetchUpdateResponse)
}

func blockUser(input _: BlockUserInput) -> SignalProducer<EmptyResponseEnvelope, ErrorEnvelope> {
return SignalProducer(value: EmptyResponseEnvelope())
}

internal func previewUrl(forDraft draft: UpdateDraft) -> URL? {
return URL(
string: "https://\(Secrets.Api.Endpoint.production)/projects/\(draft.update.projectId)/updates/"
Expand Down
66 changes: 65 additions & 1 deletion Library/ViewModels/CommentRepliesViewModelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ internal final class CommentRepliesViewModelTests: TestCase {
private let configureCommentComposerBecomeFirstResponder = TestObserver<Bool, Never>()
private let configureCommentComposerViewURL = TestObserver<URL?, Never>()
private let configureCommentComposerViewCanPostComment = TestObserver<Bool, Never>()
private let didBlockUser = TestObserver<(), Never>()
private let didBlockUserError = TestObserver<(), Never>()
private let loadCommentIntoDataSourceComment = TestObserver<Comment, Never>()
private let loadFailableReplyIntoDataSource = TestObserver<Comment, Never>()
private let loadFailableCommentIDIntoDataSource = TestObserver<String, Never>()
Expand All @@ -33,7 +35,8 @@ internal final class CommentRepliesViewModelTests: TestCase {
self.vm.outputs.configureCommentComposerViewWithData.map(\.canPostComment)
.observe(self.configureCommentComposerViewCanPostComment.observer)
self.vm.outputs.loadCommentIntoDataSource.observe(self.loadCommentIntoDataSourceComment.observer)

self.vm.outputs.didBlockUser.observe(self.didBlockUser.observer)
self.vm.outputs.didBlockUserError.observe(self.didBlockUserError.observer)
self.vm.outputs.loadFailableReplyIntoDataSource.map(first)
.observe(self.loadFailableReplyIntoDataSource.observer)

Expand Down Expand Up @@ -108,6 +111,67 @@ internal final class CommentRepliesViewModelTests: TestCase {
}
}

func testDidBlockUser_EmitsOnSuccess() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ‘

let envelope = EmptyResponseEnvelope()

withEnvironment(apiService: MockService(blockUserResult: .success(envelope)), currentUser: .template) {
self.vm.inputs
.configureWith(
comment: .template,
project: .template,
update: nil,
inputAreaBecomeFirstResponder: false,
replyId: nil
)

self.vm.inputs.viewDidLoad()
self.vm.inputs.viewDidAppear()

self.didBlockUser.assertValueCount(0)
self.didBlockUserError.assertValueCount(0)

self.vm.inputs.blockUser(id: "\(User.template.id)")

self.scheduler.advance()

self.didBlockUser.assertValueCount(1)
self.didBlockUserError.assertValueCount(0)
}
}

func testDidBlockUserError_EmitsOnFailure() {
let error = ErrorEnvelope(
errorMessages: ["block user request error"],
ksrCode: .GraphQLError,
httpCode: 401,
exception: nil
)

withEnvironment(apiService: MockService(blockUserResult: .failure(error)), currentUser: .template) {
self.vm.inputs
.configureWith(
comment: .template,
project: .template,
update: nil,
inputAreaBecomeFirstResponder: false,
replyId: nil
)

self.vm.inputs.viewDidLoad()
self.vm.inputs.viewDidAppear()

self.didBlockUser.assertValueCount(0)
self.didBlockUserError.assertValueCount(0)

self.vm.inputs.blockUser(id: "\(User.template.id)")

self.scheduler.advance()

self.didBlockUser.assertValueCount(0)
self.didBlockUserError.assertValueCount(1)
}
}

func testOutput_ConfigureCommentComposerViewWithData_IsLoggedIn_IsBacking_False_HasBlockedCommentComposer() {
let user = User.template |> \.id .~ 12_345

Expand Down
49 changes: 49 additions & 0 deletions Library/ViewModels/CommentsViewModelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ internal final class CommentsViewModelTests: TestCase {
private let configureCommentComposerViewURL = TestObserver<URL?, Never>()
private let configureCommentComposerViewCanPostComment = TestObserver<Bool, Never>()
private let configureFooterViewWithState = TestObserver<CommentTableViewFooterViewState, Never>()
private let didBlockUser = TestObserver<(), Never>()
private let didBlockUserError = TestObserver<(), Never>()
private let goToCommentRepliesComment = TestObserver<Comment, Never>()
private let goToCommentRepliesProject = TestObserver<Project, Never>()
private let goToCommentRepliesUpdate = TestObserver<Update?, Never>()
Expand All @@ -35,6 +37,8 @@ internal final class CommentsViewModelTests: TestCase {
.observe(self.configureCommentComposerViewURL.observer)
self.vm.outputs.configureCommentComposerViewWithData.map(\.canPostComment)
.observe(self.configureCommentComposerViewCanPostComment.observer)
self.vm.outputs.didBlockUser.observe(self.didBlockUser.observer)
self.vm.outputs.didBlockUserError.observe(self.didBlockUserError.observer)
self.vm.outputs.configureFooterViewWithState.observe(self.configureFooterViewWithState.observer)
self.vm.outputs.goToRepliesWithCommentProjectUpdateAndBecomeFirstResponder.map { $0.0 }
.observe(self.goToCommentRepliesComment.observer)
Expand Down Expand Up @@ -142,6 +146,51 @@ internal final class CommentsViewModelTests: TestCase {
}
}

func testDidBlockUser_EmitsOnSuccess() {
let envelope = EmptyResponseEnvelope()

withEnvironment(apiService: MockService(blockUserResult: .success(envelope)), currentUser: .template) {
self.vm.inputs.configureWith(project: .template, update: nil)

self.vm.inputs.viewDidLoad()

self.didBlockUser.assertValueCount(0)
self.didBlockUserError.assertValueCount(0)

self.vm.inputs.blockUser(id: "\(User.template.id)")

self.scheduler.advance()

self.didBlockUser.assertValueCount(1)
self.didBlockUserError.assertValueCount(0)
}
}

func testDidBlockUserError_EmitsOnFailure() {
let error = ErrorEnvelope(
errorMessages: ["block user request error"],
ksrCode: .GraphQLError,
httpCode: 401,
exception: nil
)

withEnvironment(apiService: MockService(blockUserResult: .failure(error)), currentUser: .template) {
self.vm.inputs.configureWith(project: .template, update: nil)

self.vm.inputs.viewDidLoad()

self.didBlockUser.assertValueCount(0)
self.didBlockUserError.assertValueCount(0)

self.vm.inputs.blockUser(id: "\(User.template.id)")

self.scheduler.advance()

self.didBlockUser.assertValueCount(0)
self.didBlockUserError.assertValueCount(1)
}
}

func testCommentComposerHidden_WhenUserIsNotLoggedIn() {
withEnvironment(currentUser: nil) {
self.vm.inputs.configureWith(project: .template, update: nil)
Expand Down
47 changes: 46 additions & 1 deletion Library/ViewModels/MessagesViewModelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ internal final class MessagesViewModelTests: TestCase {
self.vm.outputs.replyButtonIsEnabled.observe(self.replyButtonIsEnabled.observer)
self.vm.outputs.successfullyMarkedAsRead.observe(self.successfullyMarkedAsRead.observer)
self.vm.outputs.didBlockUser.observe(self.didBlockUser.observer)
self.vm.outputs.didBlockUserError.observe(self.didBlockUser.observer)
self.vm.outputs.didBlockUserError.observe(self.didBlockUserError.observer)

AppEnvironment.login(AccessTokenEnvelope(accessToken: "deadbeef", user: User.template))
}
Expand Down Expand Up @@ -279,6 +279,51 @@ internal final class MessagesViewModelTests: TestCase {
}
}

func testDidBlockUser_EmitsOnSuccess() {
let envelope = EmptyResponseEnvelope()

withEnvironment(apiService: MockService(blockUserResult: .success(envelope)), currentUser: .template) {
self.vm.inputs.configureWith(data: .right((project: .template, backing: .template)))

self.vm.inputs.viewDidLoad()

self.didBlockUser.assertValueCount(0)
self.didBlockUserError.assertValueCount(0)

self.vm.inputs.blockUser(id: "\(User.template.id)")

self.scheduler.advance()

self.didBlockUser.assertValueCount(1)
self.didBlockUserError.assertValueCount(0)
}
}

func testDidBlockUserError_EmitsOnFailure() {
let error = ErrorEnvelope(
errorMessages: ["block user request error"],
ksrCode: .GraphQLError,
httpCode: 401,
exception: nil
)

withEnvironment(apiService: MockService(blockUserResult: .failure(error)), currentUser: .template) {
self.vm.inputs.configureWith(data: .right((project: .template, backing: .template)))

self.vm.inputs.viewDidLoad()

self.didBlockUser.assertValueCount(0)
self.didBlockUserError.assertValueCount(0)

self.vm.inputs.blockUser(id: "\(User.template.id)")

self.scheduler.advance()

self.didBlockUser.assertValueCount(0)
self.didBlockUserError.assertValueCount(1)
}
}

func testParticipantPreviouslyBlockedFlow_False() {
let creator = User.template
|> \.id .~ 20
Expand Down
49 changes: 49 additions & 0 deletions Library/ViewModels/ProjectPageViewModelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ final class ProjectPageViewModelTests: TestCase {
private let configurePledgeCTAViewIsLoading = TestObserver<Bool, Never>()
private let configurePledgeCTAViewRefTag = TestObserver<RefTag?, Never>()
private let configureProjectNavigationSelectorView = TestObserver<(Project, RefTag?), Never>()
private let didBlockUser = TestObserver<(), Never>()
private let didBlockUserError = TestObserver<(), Never>()
private let dismissManagePledgeAndShowMessageBannerWithMessage = TestObserver<String, Never>()
private let goToComments = TestObserver<Project, Never>()
private let goToManagePledgeProjectParam = TestObserver<Param, Never>()
Expand Down Expand Up @@ -99,6 +101,8 @@ final class ProjectPageViewModelTests: TestCase {

self.vm.outputs.configurePledgeCTAView.map(second).observe(self.configurePledgeCTAViewIsLoading.observer)
self.vm.outputs.configurePledgeCTAView.map(third).observe(self.configurePledgeCTAViewContext.observer)
self.vm.outputs.didBlockUser.observe(self.didBlockUser.observer)
self.vm.outputs.didBlockUserError.observe(self.didBlockUserError.observer)
self.vm.outputs.dismissManagePledgeAndShowMessageBannerWithMessage
.observe(self.dismissManagePledgeAndShowMessageBannerWithMessage.observer)
self.vm.outputs.goToComments.observe(self.goToComments.observer)
Expand Down Expand Up @@ -315,6 +319,51 @@ final class ProjectPageViewModelTests: TestCase {
}
}

func testDidBlockUser_EmitsOnSuccess() {
let envelope = EmptyResponseEnvelope()

withEnvironment(apiService: MockService(blockUserResult: .success(envelope)), currentUser: .template) {
self.vm.inputs.configureWith(projectOrParam: .left(self.projectWithEmptyProperties), refTag: .category)

self.vm.inputs.viewDidLoad()

self.didBlockUser.assertValueCount(0)
self.didBlockUserError.assertValueCount(0)

self.vm.inputs.blockUser(id: "\(User.template.id)")

self.scheduler.advance()

self.didBlockUser.assertValueCount(1)
self.didBlockUserError.assertValueCount(0)
}
}

func testDidBlockUserError_EmitsOnFailure() {
let error = ErrorEnvelope(
errorMessages: ["block user request error"],
ksrCode: .GraphQLError,
httpCode: 401,
exception: nil
)

withEnvironment(apiService: MockService(blockUserResult: .failure(error)), currentUser: .template) {
self.vm.inputs.configureWith(projectOrParam: .left(self.projectWithEmptyProperties), refTag: .category)

self.vm.inputs.viewDidLoad()

self.didBlockUser.assertValueCount(0)
self.didBlockUserError.assertValueCount(0)

self.vm.inputs.blockUser(id: "\(User.template.id)")

self.scheduler.advance()

self.didBlockUser.assertValueCount(0)
self.didBlockUserError.assertValueCount(1)
}
}

func testConfigureProjectPageViewControllerDataSourceProject_NonUS_ProjectCurrency_US_ProjectCountry() {
let USCurrencyProject = self.projectWithEmptyProperties
|> Project.lens.country .~ .us
Expand Down