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

Default recommended projects #680

Merged
merged 26 commits into from
May 28, 2019
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ccfdcaa
Payment methods (#597)
justinswart Feb 15, 2019
8e02483
Merge branch 'master' of https://github.com/kickstarter/ios-oss
Scollaco Feb 19, 2019
91269f3
Merge branch 'master' of https://github.com/kickstarter/ios-oss
Scollaco Feb 20, 2019
2bb3cef
Merge branch 'master' of https://github.com/kickstarter/ios-oss
Scollaco Feb 21, 2019
48d2742
Merge branch 'master' of https://github.com/kickstarter/ios-oss
Scollaco Feb 25, 2019
15aa6ac
Merge branch 'master' of https://github.com/kickstarter/ios-oss
Scollaco Feb 27, 2019
267b0da
Merge branch 'master' of https://github.com/kickstarter/ios-oss
Scollaco Mar 5, 2019
64897e4
Merge branch 'master' of https://github.com/kickstarter/ios-oss
Scollaco Mar 5, 2019
822a34b
Merge branch 'master' of https://github.com/kickstarter/ios-oss
Scollaco Mar 6, 2019
818afed
Merge branch 'master' of https://github.com/kickstarter/ios-oss
Scollaco Mar 21, 2019
c38221b
Merge branch 'master' of https://github.com/kickstarter/ios-oss
Scollaco Mar 28, 2019
e906b5d
Merge branch 'master' of https://github.com/kickstarter/ios-oss
Scollaco Apr 18, 2019
dfbab73
Merge branch 'master' of https://github.com/kickstarter/ios-oss
Scollaco Apr 25, 2019
da7e0de
Merge branch 'master' of https://github.com/kickstarter/ios-oss
Scollaco May 17, 2019
1c00ffd
Added Recommended Projects as default filter when user logs in
Scollaco May 17, 2019
3f63864
Changed logic to show recommendations also based on privacy setting
Scollaco May 17, 2019
470ef0b
Merge branch 'master' into default-recommended-projects
Scollaco May 17, 2019
6c5b252
Merge branch 'master' into default-recommended-projects
Scollaco May 21, 2019
f620cb1
Feedback from pavel
Scollaco May 21, 2019
83411c2
Added notification to notify DiscoveryViewController when Recommendat…
Scollaco May 22, 2019
cbfdcea
Alphabetization
Scollaco May 22, 2019
4f03dd5
PR feedback
Scollaco May 23, 2019
5743834
PR Feedback from Justin
Scollaco May 28, 2019
556e8ec
Merge remote-tracking branch 'oss/master' into default-recommended-pr…
Scollaco May 28, 2019
a3e09a9
Update Library/Notifications.swift
Scollaco May 28, 2019
cf94b7a
Merge branch 'master' into default-recommended-projects
Scollaco May 28, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ internal final class SettingsPrivacyRecommendationCell: UITableViewCell, ValueCe
internal override func bindViewModel() {
super.bindViewModel()

self.viewModel.outputs.postNotification
.observeForUI()
.observeValues { NotificationCenter.default.post($0) }

self.viewModel.outputs.updateCurrentUser
.observeForUI()
.observeValues { user in AppEnvironment.updateCurrentUser(user) }
Expand Down
13 changes: 13 additions & 0 deletions Kickstarter-iOS/Views/Controllers/DiscoveryViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ internal final class DiscoveryViewController: UIViewController {
fileprivate let viewModel: DiscoveryViewModelType = DiscoveryViewModel()
fileprivate var dataSource: DiscoveryPagesDataSource!

private var recommendationsChangedObserver: Any?

private weak var navigationHeaderViewController: DiscoveryNavigationHeaderViewController!
private weak var pageViewController: UIPageViewController!
private weak var sortPagerViewController: SortPagerViewController!
Expand Down Expand Up @@ -35,9 +37,20 @@ internal final class DiscoveryViewController: UIViewController {
.compactMap { $0 as? DiscoveryNavigationHeaderViewController }.first
self.navigationHeaderViewController.delegate = self

self.recommendationsChangedObserver = NotificationCenter.default
.addObserver(forName: Notification.Name.ksr_recommendationsSettingChanged,
object: nil,
queue: nil) { [weak self] _ in
self?.viewModel.inputs.didChangeRecommendationsSetting()
}

self.viewModel.inputs.viewDidLoad()
}

deinit {
self.recommendationsChangedObserver.doIfSome(NotificationCenter.default.removeObserver)
}

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)

Expand Down
3 changes: 3 additions & 0 deletions Library/Notifications.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ public enum CurrentUserNotifications {
public static let environmentChanged = "CurrentUserNotification.environmentChanged"
public static let localePreferencesChanged = "CurrentUserNotification.localePreferencesChanged"
public static let projectSaved = "CurrentUserNotifications.projectSaved"
public static let recommendationsSettingChanged = "CurrentUserNotifications.recommendationSettingChanged"
Scollaco marked this conversation as resolved.
Show resolved Hide resolved
public static let savedProjectEmptyStateTapped = "CurrentUserNotifications.savedProjectEmptyStateTapped"
public static let sessionEnded = "CurrentUserNotifications.sessionEnded"
public static let sessionStarted = "CurrentUserNotifications.sessionStarted"
Expand All @@ -23,6 +24,8 @@ extension Notification.Name {
CurrentUserNotifications.environmentChanged
)
public static let ksr_projectSaved = Notification.Name(rawValue: CurrentUserNotifications.projectSaved)
public static let ksr_recommendationsSettingChanged =
Notification.Name(rawValue: CurrentUserNotifications.recommendationsSettingChanged)
public static let ksr_savedProjectEmptyStateTapped =
Notification.Name(rawValue: CurrentUserNotifications.savedProjectEmptyStateTapped)
public static let ksr_sessionStarted = Notification.Name(rawValue: CurrentUserNotifications.sessionStarted)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ DiscoveryNavigationHeaderViewModelInputs, DiscoveryNavigationHeaderViewModelOutp

public init() {
let currentParams = Signal.merge(
self.paramsProperty.signal.skipNil(),
self.paramsProperty.signal.skipNil().skipRepeats(),
self.filtersSelectedRowProperty.signal.skipNil().map { $0.params }
)

Expand Down
36 changes: 29 additions & 7 deletions Library/ViewModels/DiscoveryViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import ReactiveExtensions
import Result

public protocol DiscoveryViewModelInputs {

/// Call when Recommendations setting changes on Settings > Account > Privacy > Recommendations.
func didChangeRecommendationsSetting()
Scollaco marked this conversation as resolved.
Show resolved Hide resolved

/// Call when params have been selected.
func filter(withParams params: DiscoveryParams)

Expand Down Expand Up @@ -61,26 +65,39 @@ public protocol DiscoveryViewModelType {
var outputs: DiscoveryViewModelOutputs { get }
}

private func initialParam() -> DiscoveryParams {
public final class DiscoveryViewModel: DiscoveryViewModelType, DiscoveryViewModelInputs,
DiscoveryViewModelOutputs {

private static func initialParams() -> DiscoveryParams {
guard AppEnvironment.current.currentUser?.optedOutOfRecommendations == .some(false) else {
return DiscoveryParams.defaults
|> DiscoveryParams.lens.includePOTD .~ true
}
return DiscoveryParams.defaults
|> DiscoveryParams.lens.includePOTD .~ true
}

public final class DiscoveryViewModel: DiscoveryViewModelType, DiscoveryViewModelInputs,
DiscoveryViewModelOutputs {
|> DiscoveryParams.lens.backed .~ false
|> DiscoveryParams.lens.recommended .~ true
}

public init() {
let sorts: [DiscoveryParams.Sort] = [.magic, .popular, .newest, .endingSoon]

self.configurePagerDataSource = self.viewDidLoadProperty.signal.mapConst(sorts)
self.configureSortPager = self.configurePagerDataSource

let initialParams = Signal.merge(
self.viewWillAppearProperty.signal.take(first: 1).ignoreValues(),
self.didChangeRecommendationsSettingProperty.signal
.takeWhen(self.viewWillAppearProperty.signal)
)
.map(DiscoveryViewModel.initialParams)
.skipRepeats()

let currentParams = Signal.merge(
self.viewWillAppearProperty.signal.take(first: 1).map { _ in initialParam() },
initialParams,
self.filterWithParamsProperty.signal.skipNil()
)
.skipRepeats()
.skipRepeats()

self.configureNavigationHeader = currentParams
self.loadFilterIntoDataSource = currentParams
Expand Down Expand Up @@ -132,6 +149,11 @@ DiscoveryViewModelOutputs {
.observeValues { AppEnvironment.current.koala.trackDiscoveryViewed(params: $0) }
}

fileprivate let didChangeRecommendationsSettingProperty = MutableProperty(())
public func didChangeRecommendationsSetting() {
self.didChangeRecommendationsSettingProperty.value = ()
}

fileprivate let filterWithParamsProperty = MutableProperty<DiscoveryParams?>(nil)
public func filter(withParams params: DiscoveryParams) {
self.filterWithParamsProperty.value = params
Expand Down
68 changes: 63 additions & 5 deletions Library/ViewModels/DiscoveryViewModelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@ internal final class DiscoveryViewModelTests: TestCase {
fileprivate let selectSortPage = TestObserver<DiscoveryParams.Sort, NoError>()
fileprivate let updateSortPagerStyle = TestObserver<Int?, NoError>()

let recsInitialParams = .defaults
|> DiscoveryParams.lens.recommended .~ true
|> DiscoveryParams.lens.backed .~ false
let initialParams = .defaults |> DiscoveryParams.lens.includePOTD .~ true
let initialParams = .defaults
|> DiscoveryParams.lens.includePOTD .~ true

let categoryParams = .defaults |> DiscoveryParams.lens.category .~ .art
let subcategoryParams = .defaults |> DiscoveryParams.lens.category .~ .documentary
Expand Down Expand Up @@ -85,6 +83,66 @@ internal final class DiscoveryViewModelTests: TestCase {
"New params load into data source after selecting.")
}

func testLoadRecommendedProjectsIntoDataSource_UserRecommendationsOptedOut() {

let user = User.template
|> \.optedOutOfRecommendations .~ true

withEnvironment(config: Config.template, currentUser: user) {
self.vm.inputs.viewDidLoad()
self.vm.inputs.viewWillAppear(animated: false)

self.configureNavigationHeader.assertValues([initialParams])
}
}

func testLoadRecommendedProjectsIntoDataSource_UserRecommendationsOptedIn() {

let recsInitialParams = .defaults
|> DiscoveryParams.lens.includePOTD .~ true
|> DiscoveryParams.lens.recommended .~ true
|> DiscoveryParams.lens.backed .~ false

let user = User.template
|> \.optedOutOfRecommendations .~ false

withEnvironment(config: Config.template, currentUser: user) {
self.vm.inputs.viewDidLoad()
self.vm.inputs.viewWillAppear(animated: false)

self.configureNavigationHeader.assertValues([recsInitialParams])
}
}

func testLoadRecommendedProjectsIntoDataSource_AfterChangingSetting() {

let recsInitialParams = .defaults
|> DiscoveryParams.lens.includePOTD .~ true
|> DiscoveryParams.lens.recommended .~ true
|> DiscoveryParams.lens.backed .~ false

let user = User.template
|> \.optedOutOfRecommendations .~ false

let optedOutUser = User.template
|> \.optedOutOfRecommendations .~ true

withEnvironment(config: Config.template, currentUser: user) {
self.vm.inputs.viewDidLoad()
self.vm.inputs.viewWillAppear(animated: false)

self.configureNavigationHeader.assertValues([recsInitialParams])

withEnvironment(currentUser: optedOutUser) {

self.vm.inputs.didChangeRecommendationsSetting()
self.vm.inputs.viewWillAppear(animated: false)

self.configureNavigationHeader.assertValues([recsInitialParams, initialParams])
}
}
}

func testConfigureNavigationHeader() {
self.configureNavigationHeader.assertValueCount(0)

Expand All @@ -99,7 +157,7 @@ internal final class DiscoveryViewModelTests: TestCase {
Signal.merge(
self.vm.outputs.configurePagerDataSource.mapConst("configureDataSource"),
self.vm.outputs.loadFilterIntoDataSource.mapConst("loadFilterIntoDataSource")
).observe(test.observer)
).observe(test.observer)

self.vm.inputs.viewDidLoad()
self.vm.inputs.viewWillAppear(animated: false)
Expand Down
10 changes: 10 additions & 0 deletions Library/ViewModels/SettingsRecommendationsCellViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public protocol SettingsRecommendationsCellViewModelInputs {
}

public protocol SettingsRecommendationsCellViewModelOutputs {
var postNotification: Signal<Notification, NoError> { get }
var recommendationsOn: Signal<Bool, NoError> { get }
var unableToSaveError: Signal<String, NoError> { get }
var updateCurrentUser: Signal<User, NoError> { get }
Expand Down Expand Up @@ -47,6 +48,14 @@ SettingsRecommendationsCellViewModelInputs, SettingsRecommendationsCellViewModel
.materialize()
}

self.postNotification = updateEvent.values()
.map { _ in
Notification(
name: .ksr_recommendationsSettingChanged,
userInfo: nil
)
}

self.unableToSaveError = updateEvent.errors()
.map { env in
env.errorMessages.first ?? Strings.profile_settings_error()
Expand Down Expand Up @@ -76,6 +85,7 @@ SettingsRecommendationsCellViewModelInputs, SettingsRecommendationsCellViewModel
self.recommendationsTappedProperty.value = on
}

public let postNotification: Signal<Notification, NoError>
public let recommendationsOn: Signal<Bool, NoError>
public let unableToSaveError: Signal<String, NoError>
public let updateCurrentUser: Signal<User, NoError>
Expand Down
17 changes: 17 additions & 0 deletions Library/ViewModels/SettingsRecommendationsCellViewModelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ import ReactiveExtensions_TestHelpers

internal final class SettingsRecommendationsCellViewModelTests: TestCase {
internal let vm = SettingsRecommendationsCellViewModel()
internal let postNotification = TestObserver<Notification, NoError>()
internal let recommendationsOn = TestObserver<Bool, NoError>()
internal let unableToSaveError = TestObserver<String, NoError>()
internal let updateCurrentUser = TestObserver<User, NoError>()

internal override func setUp() {
super.setUp()
self.vm.outputs.postNotification.observe(self.postNotification.observer)
self.vm.outputs.recommendationsOn.observe(self.recommendationsOn.observer)
self.vm.outputs.unableToSaveError.observe(self.unableToSaveError.observer)
self.vm.outputs.updateCurrentUser.observe(self.updateCurrentUser.observer)
Expand All @@ -38,6 +40,21 @@ internal final class SettingsRecommendationsCellViewModelTests: TestCase {
self.updateCurrentUser.assertValueCount(3)
}

func testPostNotification() {
let notification = Notification(name: Notification.Name.ksr_recommendationsSettingChanged)
let user = User.template

withEnvironment(apiService: MockService(fetchUserSelfResponse: user)) {
self.vm.inputs.configureWith(user: user)
self.postNotification.assertDidNotEmitValue()
self.vm.inputs.recommendationsTapped(on: true)

self.scheduler.advance()

self.postNotification.assertValue(notification)
}
}

func testUnableToSaveError() {
let error = ErrorEnvelope(
errorMessages: ["Unable to save."],
Expand Down