added iOS source code
[wl-app.git] / iOS / WolneLektury / Screens / Filter / FilterViewController.swift
1 //
2 //  FilterViewController.swift
3 //  WolneLektury
4 //
5 //  Created by Pawel Dabrowski on 30/05/2018.
6 //  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
7 //
8
9 import UIKit
10 import Toast_Swift
11
12 enum FilterSection: Int{
13     case onlyLectures = 0
14     case hasAudiobook
15     case epochs
16     case kinds
17     case genres
18     
19     var title: String{
20         return "filter_\(self)".localized.uppercased()
21     }
22     
23     var failedText: String{
24         return "load_\(self)_failed".localized
25     }
26 }
27
28 class LeftAlignedCollectionViewFlowLayout: UICollectionViewFlowLayout {
29     
30     override init() {
31         super.init()
32         minimumLineSpacing = 0
33         
34     }
35     
36     required init?(coder aDecoder: NSCoder) {
37         fatalError("init(coder:) has not been implemented")
38     }
39     
40     override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
41         let attributes = super.layoutAttributesForElements(in: rect)
42         
43         var leftMargin = sectionInset.left
44         var maxY: CGFloat = -1.0
45         attributes?.forEach { layoutAttribute in
46             
47             if(layoutAttribute.representedElementCategory == .cell){
48                 if layoutAttribute.frame.origin.y >= maxY {
49                     leftMargin = sectionInset.left
50                 }
51                 
52                 layoutAttribute.frame.origin.x = leftMargin
53                 
54                 leftMargin += layoutAttribute.frame.width + minimumInteritemSpacing
55                 maxY = max(layoutAttribute.frame.maxY , maxY)
56             }
57         }
58         return attributes
59     }
60 }
61
62 protocol FilterViewControllerDelegate: class {
63     func filterViewControllerDidSelectItems(kindsArray: [CategoryModel], epochsArray: [CategoryModel], genresArray: [CategoryModel], onlyLectures: Bool, hasAudiobook: Bool, filterChanged: Bool)
64 }
65
66 class FilterViewController: WLViewController {
67     
68     class func instance(delegate: FilterViewControllerDelegate, selectedKindsArray: [CategoryModel], selectedEpochsArray: [CategoryModel], selectedGenresArray: [CategoryModel], onlyLectures: Bool, hasAudiobook: Bool) -> FilterViewController{
69         let controller = FilterViewController.instance()
70         controller.initialSelectedKindsArray = selectedKindsArray
71         controller.initialSelectedEpochsArray = selectedEpochsArray
72         controller.initialSelectedGenresArray = selectedGenresArray
73         controller.onlyLectures = onlyLectures
74         controller.hasAudiobook = hasAudiobook
75         controller.delegate = delegate
76         return controller
77     }
78     
79     weak var delegate: FilterViewControllerDelegate!
80     @IBOutlet weak var collectionView: UICollectionView!
81
82     var initialSelectedKindsArray: [CategoryModel]!
83     var initialSelectedEpochsArray: [CategoryModel]!
84     var initialSelectedGenresArray: [CategoryModel]!
85
86     var kindsArray: [CategoryModel]?
87     var epochsArray: [CategoryModel]?
88     var genresArray: [CategoryModel]?
89     
90     var kindsSection: FilterSectionHeaderCollectionReusableView?
91     var epochsSection: FilterSectionHeaderCollectionReusableView?
92     var genresSection: FilterSectionHeaderCollectionReusableView?
93     
94     var isKindsDownloading = false
95     var isEpochsDownloading = false
96     var isGenresDownloading = false
97     
98     var filterChanged = false
99     var onlyLectures = false
100     var hasAudiobook = false
101
102     override func viewDidLoad() {
103         super.viewDidLoad()
104
105         title = "filters".localized
106         
107         setupCollectionView()
108         
109         getData(filterSection: .epochs)
110         getData(filterSection: .kinds)
111         getData(filterSection: .genres)
112     }
113     
114     @IBAction func closeAction(_ sender: Any) {
115         navigationController?.dismiss(animated: true, completion: nil)
116     }
117     
118     @IBAction func confirmAction(_ sender: Any) {
119         delegate.filterViewControllerDidSelectItems(kindsArray: kindsArray?.filter({$0.checked == true}) ?? [CategoryModel](), epochsArray: epochsArray?.filter({$0.checked == true}) ?? [CategoryModel](), genresArray: genresArray?.filter({$0.checked == true}) ?? [CategoryModel](), onlyLectures: onlyLectures, hasAudiobook: hasAudiobook, filterChanged: filterChanged)
120     }
121     
122     func getSection(filterSection: FilterSection) -> FilterSectionHeaderCollectionReusableView? {
123         switch filterSection {
124         case .epochs:
125             return epochsSection
126         case .genres:
127             return genresSection
128         case .kinds:
129             return kindsSection
130         default:
131             return nil
132         }
133     }
134     
135     func setDataSource(filterSection:FilterSection, dSource: [CategoryModel]?) {
136         switch filterSection {
137         case .epochs:
138             if let arr = dSource{
139                 for obj in initialSelectedEpochsArray{
140                     arr.first(where: {$0.slug == obj.slug})?.checked = true
141                 }
142             }
143             epochsArray = dSource
144         case .genres:
145             if let arr = dSource{
146                 for obj in initialSelectedGenresArray{
147                     arr.first(where: {$0.slug == obj.slug})?.checked = true
148                 }
149             }
150             genresArray = dSource
151         case .kinds:
152             if let arr = dSource{
153                 for obj in initialSelectedKindsArray{
154                     arr.first(where: {$0.slug == obj.slug})?.checked = true
155                 }
156             }
157             kindsArray = dSource
158         default:
159             break
160         }
161     }
162     
163     func setIsDownloading(isDownloading: Bool, section: FilterSection){
164         switch section {
165         case .epochs:
166             isEpochsDownloading = isDownloading
167         case .genres:
168             isGenresDownloading = isDownloading
169         case .kinds:
170             isKindsDownloading = isDownloading
171         default:
172             break
173         }
174     }
175
176     func getIsDownloading(section: FilterSection) -> Bool{
177         switch section {
178         case .epochs:
179             return isEpochsDownloading
180         case .genres:
181             return isGenresDownloading
182         case .kinds:
183             return isKindsDownloading
184         default:
185             return false
186         }
187     }
188
189     func getData(filterSection: FilterSection){
190         
191         getSection(filterSection: filterSection)?.refreshButton.setIndicatorButtonState(state: .loading)
192         setIsDownloading(isDownloading: true, section: filterSection)
193         
194         syncManager.getCategories(filterSection: filterSection, bookOnly: true) { [weak self] (result) in
195             self?.setIsDownloading(isDownloading: false, section: filterSection)
196             switch result {
197             case .success(let model):
198                 self?.getSection(filterSection: filterSection)?.refreshButton.setIndicatorButtonState(state: .hidden)
199                 self?.setDataSource(filterSection: filterSection, dSource: model as? [CategoryModel])
200                 self?.collectionView.reloadSections([filterSection.rawValue])
201             case .failure/*(let *error)*/:
202                 self?.getSection(filterSection: filterSection)?.refreshButton.setIndicatorButtonState(state: .button)
203                 self?.view.makeToast(filterSection.failedText, duration: 3.0, position: .bottom)
204             }
205         }
206     }
207     
208     func setupCollectionView(){
209         
210         collectionView.backgroundColor = UIColor.clear
211         collectionView.delegate = self
212         collectionView.dataSource = self
213         collectionView.register(UINib.init(nibName: "FilterCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "FilterCollectionViewCell")
214         collectionView.register(UINib.init(nibName: "FilterSectionHeaderCollectionReusableView", bundle: nil), forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "FilterSectionHeaderCollectionReusableView")
215         collectionView.register(UINib.init(nibName: "FilterOnlyLecturesReusableView", bundle: nil), forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "FilterOnlyLecturesReusableView")
216         
217         if #available(iOS 11.0, *) {
218             collectionView.contentInsetAdjustmentBehavior = .always
219         }
220         
221         collectionView.setCollectionViewLayout(LeftAlignedCollectionViewFlowLayout(), animated: true)
222     }
223     
224     func getArrayForSection(filterSection: FilterSection) -> [CategoryModel]?{
225         switch filterSection{
226         case .onlyLectures, .hasAudiobook:
227             return nil
228         case .epochs:
229             return epochsArray
230         case .genres:
231             return genresArray
232         case .kinds:
233             return kindsArray
234         }
235     }
236     
237     func numberOfRowsInSection(filterSection: FilterSection) -> Int{
238         return getArrayForSection(filterSection: filterSection)?.count ?? 0
239     }
240     
241     func clearSectionHeaderReference(section: FilterSectionHeaderCollectionReusableView) {
242         if genresSection == section{
243             genresSection = nil
244         }
245         if epochsSection == section{
246             epochsSection = nil
247         }
248         if kindsSection == section{
249             kindsSection = nil
250         }
251     }
252 }
253
254 extension FilterViewController: UICollectionViewDelegateFlowLayout{
255    
256     func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
257         
258         if let items = getArrayForSection(filterSection: FilterSection(rawValue: indexPath.section)!){
259             let item = items[indexPath.row]
260             return CGSize(width: item.name.uppercased().width(withConstrainedHeight: 20, font: UIFont.systemFont(ofSize: 14, weight: .medium)) + 40, height: 30)
261         }
262         return CGSize(width: 50, height: 30)
263     }
264     
265     func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
266         
267         if indexPath.section == 0{
268             let sectionHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "FilterOnlyLecturesReusableView", for: indexPath) as? FilterOnlyLecturesReusableView
269             sectionHeader?.delegate = self
270             sectionHeader?.onSwitch.isOn = onlyLectures
271             sectionHeader?.setup(isAudiobook: false)
272             return sectionHeader!
273         }
274         if indexPath.section == 1{
275             let sectionHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "FilterOnlyLecturesReusableView", for: indexPath) as? FilterOnlyLecturesReusableView
276             sectionHeader?.delegate = self
277             sectionHeader?.onSwitch.isOn = hasAudiobook
278             sectionHeader?.setup(isAudiobook: true)
279             return sectionHeader!
280         }
281
282         else{
283             let sectionHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "FilterSectionHeaderCollectionReusableView", for: indexPath) as! FilterSectionHeaderCollectionReusableView
284             clearSectionHeaderReference(section: sectionHeader)
285             
286             let filterSection = FilterSection(rawValue: indexPath.section)!
287             sectionHeader.setup(filterSection: filterSection, isDownloading: getIsDownloading(section: filterSection))
288             sectionHeader.delegate = self
289
290             if indexPath.section == FilterSection.epochs.rawValue{
291                 epochsSection = sectionHeader
292             }
293             else if indexPath.section == FilterSection.genres.rawValue{
294                 genresSection = sectionHeader
295             }
296             else if indexPath.section == FilterSection.kinds.rawValue{
297                 kindsSection = sectionHeader
298             }
299             return sectionHeader
300         }
301     }
302     
303     func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
304         
305         if section == 0 || section == 1{
306             return CGSize(width: UIScreen.main.bounds.width, height: 44)
307         }
308         else{
309             if getArrayForSection(filterSection: FilterSection(rawValue: section)!) != nil{
310                 return CGSize(width: UIScreen.main.bounds.width, height: 44)
311             }
312             else{
313                 return CGSize(width: UIScreen.main.bounds.width, height: 180)
314             }
315         }
316     }
317 }
318
319 extension FilterViewController: UICollectionViewDataSource{ //UICollectionViewDataSource
320     
321     func numberOfSections(in collectionView: UICollectionView) -> Int {
322         return 5
323     }
324     
325     func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int{
326         return numberOfRowsInSection(filterSection: FilterSection(rawValue: section)!)
327     }
328     
329     func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell{
330         
331         let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FilterCollectionViewCell", for: indexPath) as! FilterCollectionViewCell
332         
333         if let items = getArrayForSection(filterSection: FilterSection(rawValue: indexPath.section)!){
334             cell.setup(categoryModel: items[indexPath.row])
335         }
336         return cell
337     }
338 }
339
340 extension FilterViewController: UICollectionViewDelegate{
341     func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
342         
343         if let items = getArrayForSection(filterSection: FilterSection(rawValue: indexPath.section)!), items.count > indexPath.row{
344             filterChanged = true
345             let item = items[indexPath.row]
346             item.checked = !item.checked
347             if let cell = collectionView.cellForItem(at: indexPath) as? FilterCollectionViewCell{
348                 cell.setChecked(value: item.checked)
349             }
350         }
351     }
352 }
353
354 extension FilterViewController: FilterOnlyLecturesReusableViewDelegate{
355     func filterOnlyLecturesReusableViewSwitchValueChanged(value: Bool, isAudiobook: Bool){
356         if isAudiobook {
357             hasAudiobook = value
358         }
359         else {
360             onlyLectures = value
361         }
362         filterChanged = true
363     }
364 }
365
366 extension FilterViewController: FilterSectionHeaderCollectionReusableViewDelegate{
367     func filterSectionRefreshButtonTapped(section: FilterSection){
368         getData(filterSection: section)
369     }
370 }