Skip to content

Commit

Permalink
Add promo stories support (#879)
Browse files Browse the repository at this point in the history
* Refactor rename

* Add StorageRecordsNetworkService

* Create personal offers storage record if needed

* Fetch promo stories
  • Loading branch information
ivan-magda authored Jan 26, 2021
1 parent dcaa8e5 commit bf152f7
Show file tree
Hide file tree
Showing 13 changed files with 333 additions and 149 deletions.
20 changes: 16 additions & 4 deletions Stepic.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
082BE3AE1E676373006BC60F /* AuthRoutingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 082BE3AD1E676373006BC60F /* AuthRoutingManager.swift */; };
082BE3B21E67686B006BC60F /* RoutingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 082BE3B11E67686B006BC60F /* RoutingManager.swift */; };
082E35AE20B55691006E28F9 /* StorageRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 082E35AD20B55691006E28F9 /* StorageRecord.swift */; };
082E35B020B5B14A006E28F9 /* StorageData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 082E35AF20B5B14A006E28F9 /* StorageData.swift */; };
082E35B020B5B14A006E28F9 /* StorageRecordData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 082E35AF20B5B14A006E28F9 /* StorageRecordData.swift */; };
082E35B220B5F1E4006E28F9 /* StorageRecordsAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 082E35B120B5F1E4006E28F9 /* StorageRecordsAPI.swift */; };
082E5E0E1F46379100F41426 /* ReplyCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 082E5E0D1F46379100F41426 /* ReplyCache.swift */; };
083267A61CDCE64F002F7B5A /* PersistentTaskRecoveryManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083267A51CDCE64F002F7B5A /* PersistentTaskRecoveryManager.swift */; };
Expand Down Expand Up @@ -698,6 +698,9 @@
2C8F3AE323CCBEAB004D113A /* StreamVideoQuality.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C8F3AE223CCBEAB004D113A /* StreamVideoQuality.swift */; };
2C8FA6CF25B600D6006BF4B6 /* StepikWidgetTokenFileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C8FA6CE25B600D6006BF4B6 /* StepikWidgetTokenFileManager.swift */; };
2C8FA6E225B6097D006BF4B6 /* StepikWidgetTokenFileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C8FA6CE25B600D6006BF4B6 /* StepikWidgetTokenFileManager.swift */; };
2C911B8625C02A9E0076DC31 /* StorageRecordKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C911B8525C02A9E0076DC31 /* StorageRecordKind.swift */; };
2C911B9025C02E430076DC31 /* StorageRecordsNetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C911B8F25C02E430076DC31 /* StorageRecordsNetworkService.swift */; };
2C911B9A25C037EB0076DC31 /* PersonalOffersService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C911B9925C037EB0076DC31 /* PersonalOffersService.swift */; };
2C92669720B5C7CF00525AFC /* PlaceholderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C92669520B5C7CF00525AFC /* PlaceholderTableViewCell.swift */; };
2C92669820B5C7CF00525AFC /* PlaceholderTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2C92669620B5C7CF00525AFC /* PlaceholderTableViewCell.xib */; };
2C936D45243D3ADB00A4A4A9 /* ApplicationThemeService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C936D44243D3ADB00A4A4A9 /* ApplicationThemeService.swift */; };
Expand Down Expand Up @@ -1615,7 +1618,7 @@
082BE3AD1E676373006BC60F /* AuthRoutingManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthRoutingManager.swift; sourceTree = "<group>"; };
082BE3B11E67686B006BC60F /* RoutingManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoutingManager.swift; sourceTree = "<group>"; };
082E35AD20B55691006E28F9 /* StorageRecord.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageRecord.swift; sourceTree = "<group>"; };
082E35AF20B5B14A006E28F9 /* StorageData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageData.swift; sourceTree = "<group>"; };
082E35AF20B5B14A006E28F9 /* StorageRecordData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageRecordData.swift; sourceTree = "<group>"; };
082E35B120B5F1E4006E28F9 /* StorageRecordsAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageRecordsAPI.swift; sourceTree = "<group>"; };
082E5E0D1F46379100F41426 /* ReplyCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReplyCache.swift; sourceTree = "<group>"; };
082EDED51D88871F006B51DC /* Model_v9.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model_v9.xcdatamodel; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2298,6 +2301,9 @@
2C8F3AE023CCBD26004D113A /* DownloadVideoQuality.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadVideoQuality.swift; sourceTree = "<group>"; };
2C8F3AE223CCBEAB004D113A /* StreamVideoQuality.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StreamVideoQuality.swift; sourceTree = "<group>"; };
2C8FA6CE25B600D6006BF4B6 /* StepikWidgetTokenFileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepikWidgetTokenFileManager.swift; sourceTree = "<group>"; };
2C911B8525C02A9E0076DC31 /* StorageRecordKind.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageRecordKind.swift; sourceTree = "<group>"; };
2C911B8F25C02E430076DC31 /* StorageRecordsNetworkService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageRecordsNetworkService.swift; sourceTree = "<group>"; };
2C911B9925C037EB0076DC31 /* PersonalOffersService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersonalOffersService.swift; sourceTree = "<group>"; };
2C92669520B5C7CF00525AFC /* PlaceholderTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceholderTableViewCell.swift; sourceTree = "<group>"; };
2C92669620B5C7CF00525AFC /* PlaceholderTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PlaceholderTableViewCell.xib; sourceTree = "<group>"; };
2C936D44243D3ADB00A4A4A9 /* ApplicationThemeService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationThemeService.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -6406,8 +6412,9 @@
2CFF9042242A2B0E00FD7311 /* StorageRecords */ = {
isa = PBXGroup;
children = (
082E35AF20B5B14A006E28F9 /* StorageData.swift */,
082E35AD20B55691006E28F9 /* StorageRecord.swift */,
082E35AF20B5B14A006E28F9 /* StorageRecordData.swift */,
2C911B8525C02A9E0076DC31 /* StorageRecordKind.swift */,
);
path = StorageRecords;
sourceTree = "<group>";
Expand Down Expand Up @@ -6855,6 +6862,7 @@
2C5418CB24A2903700B2DCE2 /* StepikMetricsNetworkService.swift */,
62E987944A98FB989B36D72C /* StepsNetworkService.swift */,
2CF10C8A238426B300F8CC95 /* StepSourcesNetworkService.swift */,
2C911B8F25C02E430076DC31 /* StorageRecordsNetworkService.swift */,
2C41F0EB25BAE6F200DA634A /* StoryTemplatesNetworkService.swift */,
2C6BBBBE22B26DB100889A45 /* SubmissionsNetworkService.swift */,
62E98E41865820B1B8F7357D /* UnitsNetworkService.swift */,
Expand Down Expand Up @@ -7612,6 +7620,7 @@
2C7AC4D525B462BC0024D4D2 /* ImagePreheater.swift */,
62E98E01F05F1205F284595F /* NetworkReachabilityService.swift */,
2CDAD309229EC81A00AA9EF5 /* PersistenceQueuesService.swift */,
2C911B9925C037EB0076DC31 /* PersonalOffersService.swift */,
2CDBCCCE23EB777E005D2370 /* SubmissionURLProvider.swift */,
62E98FBA6AB1C2BD6EA95634 /* UnitNavigationService.swift */,
62E98EBA0AF48AD90775FF7E /* UserAccountService.swift */,
Expand Down Expand Up @@ -8976,7 +8985,7 @@
08CA59EE1BBFC962008DC44D /* UIButtonExtensions.swift in Sources */,
2C52310924C5806F00DC1474 /* NewProfileCertificatesOutputProtocol.swift in Sources */,
2C04BA4E24072ED900D74D4B /* StringSubmissionFeedback.swift in Sources */,
082E35B020B5B14A006E28F9 /* StorageData.swift in Sources */,
082E35B020B5B14A006E28F9 /* StorageRecordData.swift in Sources */,
2C97E00C215E4805005684A1 /* SearchQueriesView.swift in Sources */,
08A0218E1D675B4700915679 /* SectionNavigationDelegate.swift in Sources */,
2CD6E256234E042800F49303 /* EmailAddressesAPI.swift in Sources */,
Expand Down Expand Up @@ -9927,6 +9936,7 @@
62E980270B31473DE5E16CDD /* SubmissionsAssembly.swift in Sources */,
62E9843A477D9364C662D3F4 /* SubmissionsDataFlow.swift in Sources */,
62E983B0039B41A802B982BE /* SubmissionsInteractor.swift in Sources */,
2C911B8625C02A9E0076DC31 /* StorageRecordKind.swift in Sources */,
62E984B01F594EA811436B6D /* SubmissionsPresenter.swift in Sources */,
62E98FE1CE19810D6930DAE6 /* SubmissionsProvider.swift in Sources */,
62E984D9CA1E33C6B75A12D9 /* SubmissionsView.swift in Sources */,
Expand Down Expand Up @@ -9965,6 +9975,7 @@
F26EBB85670ECB562C65D84C /* DownloadARQuickLookInteractor.swift in Sources */,
AAB4F45B011C05D59E6EEEEB /* DownloadARQuickLookPresenter.swift in Sources */,
245107514E8F81CF9A8C4C44 /* DownloadARQuickLookView.swift in Sources */,
2C911B9025C02E430076DC31 /* StorageRecordsNetworkService.swift in Sources */,
2C0B771D25480A7500BA474D /* CourseListFilterViewModel.swift in Sources */,
008437078C67C7149BE6DE53 /* DownloadARQuickLookViewController.swift in Sources */,
C2537B043956035AEFCABAB5 /* DownloadARQuickLookOutputProtocol.swift in Sources */,
Expand Down Expand Up @@ -10057,6 +10068,7 @@
DF2D0B6B2964A1E24CB373B2 /* TableQuizView.swift in Sources */,
4B32F5A0FE4CF3EE3F0FB14B /* TableQuizViewController.swift in Sources */,
F97BB185088B9D4BDC7CF147 /* CourseListFilterAssembly.swift in Sources */,
2C911B9A25C037EB0076DC31 /* PersonalOffersService.swift in Sources */,
8B172316D4E6435D8AB2AEE8 /* CourseListFilterDataFlow.swift in Sources */,
48B0EC48DA9989013F58FB2A /* CourseListFilterInteractor.swift in Sources */,
7EAA19D4026219B6276DE691 /* CourseListFilterPresenter.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ final class PersonalDeadlinesService: PersonalDeadlinesServiceProtocol {
Promise { seal in
counter.countDeadlines(mode: mode, for: course).then {
sectionDeadlines -> Promise<StorageRecord> in
let data = DeadlineStorageData(courseID: course.id, deadlines: sectionDeadlines)
let record = StorageRecord(data: data, kind: StorageKind.deadline(courseID: course.id))
let data = DeadlineStorageRecordData(courseID: course.id, deadlines: sectionDeadlines)
let record = StorageRecord(data: data, kind: StorageRecordKind.deadline(courseID: course.id))
return self.storageRecordsAPI.create(record: record)
}.done { createdRecord in
self.localStorageManager.set(storageRecord: createdRecord, for: course)
Expand Down Expand Up @@ -118,7 +118,7 @@ final class PersonalDeadlinesService: PersonalDeadlinesServiceProtocol {
seal.reject(DeadlineChangeError.noLocalRecord)
return
}
guard let dataToChange = record.data as? DeadlineStorageData else {
guard let dataToChange = record.data as? DeadlineStorageRecordData else {
seal.reject(DeadlineChangeError.noLocalRecord)
return
}
Expand Down
10 changes: 9 additions & 1 deletion Stepic/Legacy/Controllers/Stories/Stories/StoriesAssembly.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,15 @@ final class StoriesAssembly: Assembly {

func makeModule() -> UIViewController {
let viewController = StoriesViewController()
let presenter = StoriesPresenter(view: viewController, storyTemplatesAPI: StoryTemplatesAPI())
let presenter = StoriesPresenter(
view: viewController,
storyTemplatesNetworkService: StoryTemplatesNetworkService(storyTemplatesAPI: StoryTemplatesAPI()),
contentLanguageService: ContentLanguageService(),
userAccountService: UserAccountService(),
personalOffersService: PersonalOffersService(
storageRecordsNetworkService: StorageRecordsNetworkService(storageRecordsAPI: StorageRecordsAPI())
)
)
presenter.moduleOutput = self.moduleOutput
viewController.presenter = presenter

Expand Down
100 changes: 64 additions & 36 deletions Stepic/Legacy/Controllers/Stories/Stories/StoriesPresenter.swift
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
//
// StoriesPresenter.swift
// stepik-stories
//
// Created by Ostrenkiy on 08.08.2018.
// Copyright © 2018 Ostrenkiy. All rights reserved.
//

import Foundation
import PromiseKit

enum StoriesViewState {
case normal
Expand Down Expand Up @@ -34,11 +27,24 @@ final class StoriesPresenter: StoriesPresenterProtocol {
weak var moduleOutput: StoriesOutputProtocol?

var stories: [Story] = []
var storyTemplatesAPI: StoryTemplatesAPI

init(view: StoriesViewProtocol, storyTemplatesAPI: StoryTemplatesAPI) {
private let storyTemplatesNetworkService: StoryTemplatesNetworkServiceProtocol
private let contentLanguageService: ContentLanguageServiceProtocol
private let userAccountService: UserAccountServiceProtocol
private let personalOffersService: PersonalOffersServiceProtocol

init(
view: StoriesViewProtocol,
storyTemplatesNetworkService: StoryTemplatesNetworkServiceProtocol,
contentLanguageService: ContentLanguageServiceProtocol,
userAccountService: UserAccountServiceProtocol,
personalOffersService: PersonalOffersServiceProtocol
) {
self.view = view
self.storyTemplatesAPI = storyTemplatesAPI
self.storyTemplatesNetworkService = storyTemplatesNetworkService
self.contentLanguageService = contentLanguageService
self.userAccountService = userAccountService
self.personalOffersService = personalOffersService

NotificationCenter.default.addObserver(
self,
Expand Down Expand Up @@ -67,39 +73,61 @@ final class StoriesPresenter: StoriesPresenterProtocol {
self.view?.set(state: .loading)

var isPublished: Bool?
if AuthInfo.shared.user?.profileEntity?.isStaff != true {
if self.userAccountService.currentUser?.profileEntity?.isStaff != true {
isPublished = true
}

self.storyTemplatesAPI.retrieve(
isPublished: isPublished,
language: ContentLanguageService().globalContentLanguage,
maxVersion: StepikApplicationsInfo.Versions.stories ?? 0
).done { [weak self] stories in
guard let strongSelf = self else {
return
self.storyTemplatesNetworkService.fetch(
language: self.contentLanguageService.globalContentLanguage,
maxVersion: StepikApplicationsInfo.Versions.stories ?? 0,
isPublished: isPublished
).then { templateStories -> Guarantee<([Story], [Story.IdType])> in
self.fetchPromoStoriesIDs().map { (templateStories, $0) }
}.then { templateStories, promoStoriesIDs -> Guarantee<[Story]> in
let templateStoriesIDs = Set(templateStories.map(\.id))
let promoStoriesIDsToFetch = Array(Set(promoStoriesIDs).subtracting(templateStoriesIDs))

if promoStoriesIDsToFetch.isEmpty {
return .value(templateStories)
} else {
return Guarantee(
self.storyTemplatesNetworkService.fetch(ids: promoStoriesIDsToFetch),
fallback: nil
).map { templateStories + ($0 ?? []) }
}
}.done { stories in
self.stories = stories
.filter { $0.isSupported }
.sorted { $0.position >= $1.position }
.sorted { !($0.isViewed.value) || ($1.isViewed.value) }

strongSelf.stories = stories.filter {
$0.isSupported
}.sorted(by: {
$0.position >= $1.position
}).sorted(by: {
!($0.isViewed.value) || ($1.isViewed.value)
})

strongSelf.view?.set(state: strongSelf.stories.isEmpty ? .empty : .normal)
strongSelf.view?.set(stories: strongSelf.stories)
self.view?.set(state: self.stories.isEmpty ? .empty : .normal)
self.view?.set(stories: self.stories)

if strongSelf.stories.isEmpty {
strongSelf.moduleOutput?.hideStories()
}
}.catch { [weak self] _ in
guard let strongSelf = self else {
return
if self.stories.isEmpty {
self.moduleOutput?.hideStories()
}
}.catch { _ in
self.view?.set(state: self.stories.isEmpty ? .empty : .normal)
}
}

strongSelf.view?.set(state: strongSelf.stories.isEmpty ? .empty : .normal)
private func fetchPromoStoriesIDs() -> Guarantee<[Story.IdType]> {
guard self.userAccountService.isAuthorized,
let userID = self.userAccountService.currentUserID else {
return .value([])
}

return Guarantee { seal in
self.personalOffersService.fetchPersonalOffer(userID: userID).done { storageRecord in
guard let data = storageRecord?.data as? PersonalOfferStorageRecordData else {
return seal([])
}

seal(data.promoStories)
}.catch { _ in
seal([])
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion Stepic/Legacy/Model/Entities/Course/Course.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ final class Course: NSManagedObject, IDFetchable {
typealias IdType = Int

var sectionDeadlines: [SectionDeadline]? {
(PersonalDeadlineLocalStorageManager().getRecord(for: self)?.data as? DeadlineStorageData)?.deadlines
(PersonalDeadlineLocalStorageManager().getRecord(for: self)?.data as? DeadlineStorageRecordData)?.deadlines
}

var metaInfo: String {
Expand Down
10 changes: 6 additions & 4 deletions Stepic/Legacy/Model/Network/Endpoints/StorageRecordsAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ import PromiseKit
import SwiftyJSON

final class StorageRecordsAPI: APIEndpoint {
private static let createUpdateParamName = "storage-record"

override var name: String { "storage-records" }

func retrieve(
userID: User.IdType,
kindPrefixType prefixType: StorageKind.PrefixType
kindPrefixType prefixType: StorageRecordKind.PrefixType
) -> Promise<([StorageRecord], Meta)> {
let params: Parameters = [
StorageRecord.JSONKey.user.rawValue: userID,
Expand All @@ -31,7 +33,7 @@ final class StorageRecordsAPI: APIEndpoint {
)
}

func retrieve(userID: User.IdType, kind: StorageKind?) -> Promise<([StorageRecord], Meta)> {
func retrieve(userID: User.IdType, kind: StorageRecordKind?) -> Promise<([StorageRecord], Meta)> {
let params: Parameters = [
StorageRecord.JSONKey.kind.rawValue: kind?.name ?? "",
StorageRecord.JSONKey.user.rawValue: userID
Expand All @@ -52,7 +54,7 @@ final class StorageRecordsAPI: APIEndpoint {
func create(record: StorageRecord) -> Promise<StorageRecord> {
self.create.request(
requestEndpoint: self.name,
paramName: "storage-record",
paramName: Self.createUpdateParamName,
creatingObject: record,
withManager: self.manager
)
Expand All @@ -61,7 +63,7 @@ final class StorageRecordsAPI: APIEndpoint {
func update(record: StorageRecord) -> Promise<StorageRecord> {
self.update.request(
requestEndpoint: self.name,
paramName: "storage-record",
paramName: Self.createUpdateParamName,
updatingObject: record,
withManager: self.manager
)
Expand Down
Loading

0 comments on commit bf152f7

Please sign in to comment.