Sfoglia il codice sorgente

Added history page and color filter

Víctor Hernández 4 anni fa
parent
commit
19863e9e89

+ 37
- 5
Flowerdex/Flowerdex.xcodeproj/project.pbxproj Vedi File

@@ -13,6 +13,10 @@
13 13
 		A52FB5C42546C2EA000AD235 /* rawResponse.json in Resources */ = {isa = PBXBuildFile; fileRef = A52FB5C32546C2EA000AD235 /* rawResponse.json */; };
14 14
 		A5711B8B255CE03000E727BB /* Flower.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5711B8A255CE03000E727BB /* Flower.swift */; };
15 15
 		A5711B8E255CE53900E727BB /* DummyData.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5711B8D255CE53900E727BB /* DummyData.swift */; };
16
+		A5799DB02591390D003E975D /* HistoryService.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5799DAF2591390D003E975D /* HistoryService.swift */; };
17
+		A5799DB4259157AC003E975D /* FlowerVerbose.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5799DB3259157AC003E975D /* FlowerVerbose.swift */; };
18
+		A5799DB72591594D003E975D /* HistoryResponseModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5799DB62591594D003E975D /* HistoryResponseModel.swift */; };
19
+		A5799DBB25915C2B003E975D /* FlowerVerboseDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5799DBA25915C2B003E975D /* FlowerVerboseDetailView.swift */; };
16 20
 		A58640BA258F30AD00F626AB /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A58640B9258F30AD00F626AB /* LoginView.swift */; };
17 21
 		A58640BE258F30F200F626AB /* RegisterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A58640BD258F30F200F626AB /* RegisterView.swift */; };
18 22
 		A59A2741254556880052319F /* FlowerdexApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59A2740254556880052319F /* FlowerdexApp.swift */; };
@@ -24,6 +28,8 @@
24 28
 		A5EA2F28258FEC3D00699182 /* FlowerService.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5EA2F27258FEC3D00699182 /* FlowerService.swift */; };
25 29
 		A5EA2F2C258FF0BD00699182 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5EA2F2B258FF0BD00699182 /* Constants.swift */; };
26 30
 		A5EA2F32258FF58200699182 /* Responses.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5EA2F31258FF58200699182 /* Responses.swift */; };
31
+		A5EA2F3A2590A62D00699182 /* HistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5EA2F392590A62D00699182 /* HistoryView.swift */; };
32
+		A5EA2F3F2590A65A00699182 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5EA2F3E2590A65A00699182 /* MainView.swift */; };
27 33
 		A5EE388D2576362B0014D0BE /* FilterFields.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5EE388C2576362B0014D0BE /* FilterFields.swift */; };
28 34
 		A5EE389125763D3A0014D0BE /* AuthenticationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5EE389025763D3A0014D0BE /* AuthenticationModel.swift */; };
29 35
 		A5EE3897257658400014D0BE /* Filters.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5EE3896257658400014D0BE /* Filters.swift */; };
@@ -42,6 +48,10 @@
42 48
 		A52FB5C32546C2EA000AD235 /* rawResponse.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = rawResponse.json; sourceTree = "<group>"; };
43 49
 		A5711B8A255CE03000E727BB /* Flower.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Flower.swift; sourceTree = "<group>"; };
44 50
 		A5711B8D255CE53900E727BB /* DummyData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DummyData.swift; sourceTree = "<group>"; };
51
+		A5799DAF2591390D003E975D /* HistoryService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryService.swift; sourceTree = "<group>"; };
52
+		A5799DB3259157AC003E975D /* FlowerVerbose.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlowerVerbose.swift; sourceTree = "<group>"; };
53
+		A5799DB62591594D003E975D /* HistoryResponseModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryResponseModel.swift; sourceTree = "<group>"; };
54
+		A5799DBA25915C2B003E975D /* FlowerVerboseDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlowerVerboseDetailView.swift; sourceTree = "<group>"; };
45 55
 		A58640B9258F30AD00F626AB /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = "<group>"; };
46 56
 		A58640BD258F30F200F626AB /* RegisterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisterView.swift; sourceTree = "<group>"; };
47 57
 		A59A273D254556880052319F /* Flowerdex.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Flowerdex.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -55,6 +65,8 @@
55 65
 		A5EA2F27258FEC3D00699182 /* FlowerService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlowerService.swift; sourceTree = "<group>"; };
56 66
 		A5EA2F2B258FF0BD00699182 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
57 67
 		A5EA2F31258FF58200699182 /* Responses.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Responses.swift; sourceTree = "<group>"; };
68
+		A5EA2F392590A62D00699182 /* HistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryView.swift; sourceTree = "<group>"; };
69
+		A5EA2F3E2590A65A00699182 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; };
58 70
 		A5EE388C2576362B0014D0BE /* FilterFields.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterFields.swift; sourceTree = "<group>"; };
59 71
 		A5EE389025763D3A0014D0BE /* AuthenticationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationModel.swift; sourceTree = "<group>"; };
60 72
 		A5EE3896257658400014D0BE /* Filters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Filters.swift; sourceTree = "<group>"; };
@@ -81,9 +93,11 @@
81 93
 			isa = PBXGroup;
82 94
 			children = (
83 95
 				A51B4F762546674C002AFF05 /* FlowersResponseModel.swift */,
96
+				A5799DB62591594D003E975D /* HistoryResponseModel.swift */,
97
+				A5EE389025763D3A0014D0BE /* AuthenticationModel.swift */,
98
+				A5799DB3259157AC003E975D /* FlowerVerbose.swift */,
84 99
 				A5711B8A255CE03000E727BB /* Flower.swift */,
85 100
 				A5EE3896257658400014D0BE /* Filters.swift */,
86
-				A5EE389025763D3A0014D0BE /* AuthenticationModel.swift */,
87 101
 				A5F0D2FB258F3D2200AF735C /* User.swift */,
88 102
 			);
89 103
 			path = Model;
@@ -102,20 +116,22 @@
102 116
 		A5711B85255CCC1700E727BB /* View */ = {
103 117
 			isa = PBXGroup;
104 118
 			children = (
105
-				A58640AE258F213600F626AB /* Authentication */,
119
+				A58640AE258F213600F626AB /* Authentication Page */,
106 120
 				A5EE38B6257819AA0014D0BE /* Discover Page */,
121
+				A5EA2F382590A61000699182 /* History Page */,
122
+				A5EA2F3E2590A65A00699182 /* MainView.swift */,
107 123
 				A5EE38B2257818BE0014D0BE /* StatusIcons.swift */,
108 124
 			);
109 125
 			path = View;
110 126
 			sourceTree = "<group>";
111 127
 		};
112
-		A58640AE258F213600F626AB /* Authentication */ = {
128
+		A58640AE258F213600F626AB /* Authentication Page */ = {
113 129
 			isa = PBXGroup;
114 130
 			children = (
115 131
 				A58640B9258F30AD00F626AB /* LoginView.swift */,
116 132
 				A58640BD258F30F200F626AB /* RegisterView.swift */,
117 133
 			);
118
-			path = Authentication;
134
+			path = "Authentication Page";
119 135
 			sourceTree = "<group>";
120 136
 		};
121 137
 		A59A2734254556870052319F = {
@@ -162,9 +178,10 @@
162 178
 			isa = PBXGroup;
163 179
 			children = (
164 180
 				A50EEFCC255D175400545CFE /* ContentDataSource.swift */,
181
+				A5EE38A3257700230014D0BE /* ImageManager.swift */,
165 182
 				A5EA2F23258FEC2300699182 /* AuthenticationService.swift */,
166 183
 				A5EA2F27258FEC3D00699182 /* FlowerService.swift */,
167
-				A5EE38A3257700230014D0BE /* ImageManager.swift */,
184
+				A5799DAF2591390D003E975D /* HistoryService.swift */,
168 185
 			);
169 186
 			path = Services;
170 187
 			sourceTree = "<group>";
@@ -178,6 +195,15 @@
178 195
 			path = Supporting;
179 196
 			sourceTree = "<group>";
180 197
 		};
198
+		A5EA2F382590A61000699182 /* History Page */ = {
199
+			isa = PBXGroup;
200
+			children = (
201
+				A5EA2F392590A62D00699182 /* HistoryView.swift */,
202
+				A5799DBA25915C2B003E975D /* FlowerVerboseDetailView.swift */,
203
+			);
204
+			path = "History Page";
205
+			sourceTree = "<group>";
206
+		};
181 207
 		A5EE38B6257819AA0014D0BE /* Discover Page */ = {
182 208
 			isa = PBXGroup;
183 209
 			children = (
@@ -263,16 +289,21 @@
263 289
 			buildActionMask = 2147483647;
264 290
 			files = (
265 291
 				A50EEFCD255D175400545CFE /* ContentDataSource.swift in Sources */,
292
+				A5EA2F3F2590A65A00699182 /* MainView.swift in Sources */,
266 293
 				A5EA2F2C258FF0BD00699182 /* Constants.swift in Sources */,
267 294
 				A59A2743254556880052319F /* DiscoverView.swift in Sources */,
268 295
 				A5EE389125763D3A0014D0BE /* AuthenticationModel.swift in Sources */,
269 296
 				A5EE38A4257700230014D0BE /* ImageManager.swift in Sources */,
270 297
 				A5EE38B0257818520014D0BE /* FlowersList.swift in Sources */,
298
+				A5799DB02591390D003E975D /* HistoryService.swift in Sources */,
299
+				A5EA2F3A2590A62D00699182 /* HistoryView.swift in Sources */,
271 300
 				A51B4F772546674C002AFF05 /* FlowersResponseModel.swift in Sources */,
272 301
 				A5EE3897257658400014D0BE /* Filters.swift in Sources */,
273 302
 				A5711B8B255CE03000E727BB /* Flower.swift in Sources */,
274 303
 				A59A2741254556880052319F /* FlowerdexApp.swift in Sources */,
304
+				A5799DB72591594D003E975D /* HistoryResponseModel.swift in Sources */,
275 305
 				A5EA2F32258FF58200699182 /* Responses.swift in Sources */,
306
+				A5799DBB25915C2B003E975D /* FlowerVerboseDetailView.swift in Sources */,
276 307
 				A58640BE258F30F200F626AB /* RegisterView.swift in Sources */,
277 308
 				A5EE388D2576362B0014D0BE /* FilterFields.swift in Sources */,
278 309
 				A5F3271A25460C0B003BCE0F /* FlowerDetailView.swift in Sources */,
@@ -284,6 +315,7 @@
284 315
 				A5F0D2FC258F3D2200AF735C /* User.swift in Sources */,
285 316
 				A5EE38AC257772200014D0BE /* FiltersSheet.swift in Sources */,
286 317
 				A5EA2F28258FEC3D00699182 /* FlowerService.swift in Sources */,
318
+				A5799DB4259157AC003E975D /* FlowerVerbose.swift in Sources */,
287 319
 			);
288 320
 			runOnlyForDeploymentPostprocessing = 0;
289 321
 		};

BIN
Flowerdex/Flowerdex.xcodeproj/project.xcworkspace/xcuserdata/victor.hernandez.xcuserdatad/UserInterfaceState.xcuserstate Vedi File


+ 3
- 1
Flowerdex/Flowerdex/FlowerdexApp.swift Vedi File

@@ -12,11 +12,13 @@ struct FlowerdexApp: App {
12 12
     @StateObject var lm = LoginModel()
13 13
     @StateObject var rm = RegistrationModel()
14 14
     @StateObject var fm = FlowerService()
15
+    @StateObject var hm = HistoryService()
15 16
     var body: some Scene {
16 17
         WindowGroup {
17 18
             if User.exists() {
18
-                DiscoverView()
19
+                MainView()
19 20
                     .environmentObject(fm)
21
+                    .environmentObject(hm)
20 22
             } else {
21 23
                 LoginView()
22 24
                     .environmentObject(lm)

+ 3
- 1
Flowerdex/Flowerdex/Model/Filters.swift Vedi File

@@ -16,8 +16,9 @@ class Filters {
16 16
     var bloomMonths: Int
17 17
     var scientificName: String
18 18
     var commonName: String
19
+    var flowerColor: String
19 20
     
20
-    init(edible: Bool, vegetable: Bool, petalCount: Int, growthMonths: Int, bloomMonths: Int, scientificName: String, commonName: String) {
21
+    init(edible: Bool, vegetable: Bool, petalCount: Int, growthMonths: Int, bloomMonths: Int, scientificName: String, commonName: String, flowerColor: String) {
21 22
         self.edible = edible
22 23
         self.vegetable = vegetable
23 24
         self.petalCount = petalCount
@@ -25,6 +26,7 @@ class Filters {
25 26
         self.bloomMonths = bloomMonths
26 27
         self.scientificName = scientificName
27 28
         self.commonName = commonName
29
+        self.flowerColor = flowerColor
28 30
     }
29 31
     
30 32
 }

+ 21
- 15
Flowerdex/Flowerdex/Model/Flower.swift Vedi File

@@ -9,6 +9,7 @@ import Foundation
9 9
 
10 10
 struct Flower: Hashable, Codable, Identifiable {
11 11
     let id: Int
12
+    
12 13
     let common_name: String?
13 14
     var commonName: String {
14 15
         if let cm = common_name {
@@ -17,14 +18,25 @@ struct Flower: Hashable, Codable, Identifiable {
17 18
             return scientificName
18 19
         }
19 20
     }
21
+    
20 22
     let slug: String // *
23
+    
21 24
     let scientific_name: String
22 25
     var scientificName: String { scientific_name }
26
+    
27
+    let image_url: String?
28
+    var imageURL: String {
29
+        if let iu = image_url {
30
+            return iu
31
+        } else {
32
+            return "[Image URL]"
33
+        }
34
+    }
35
+    
23 36
     let year: Int?
24 37
     let bibliography: String?
25 38
     let author: String? // *
26
-    let status: String // *
27
-    let rank: String // *
39
+    
28 40
     let family_common_name: String?
29 41
     var familyCommonName: String {
30 42
         if let fcn = family_common_name {
@@ -33,30 +45,24 @@ struct Flower: Hashable, Codable, Identifiable {
33 45
             return "[Family Common Name]"
34 46
         }
35 47
     }
48
+    
36 49
     let genus_id: Int // *
37 50
     var genusID: Int { genus_id }
38
-    let image_url: String?
39
-    var imageURL: String {
40
-        if let iu = image_url {
41
-            return iu
42
-        } else {
43
-            return "[Image URL]"
44
-        }
45
-    }
51
+    
52
+    let status: String // *
53
+    let rank: String // *
46 54
     let synonyms: [String]
47 55
     let genus: String
48 56
     let family: String
49
-    let links: Dictionary<String, String> // FlowerLinks
57
+    let links: FlowerLinks
50 58
     
51 59
     // Properties added by us
52 60
     let isFavorite: Bool
53 61
     let hasBeenFound: Bool
54 62
 }
55 63
 
56
-/*
57
-struct FlowerLinks: Codable {
58
-    // let self: String
64
+struct FlowerLinks: Codable, Hashable {
65
+     let `self`: String
59 66
     let plant: String
60 67
     let genus: String
61 68
 }
62
-*/

+ 111
- 0
Flowerdex/Flowerdex/Model/FlowerVerbose.swift Vedi File

@@ -0,0 +1,111 @@
1
+//
2
+//  FlowerVerbose.swift
3
+//  Flowerdex
4
+//
5
+//  Created by Víctor A. Hernández on 12/21/20.
6
+//
7
+
8
+import Foundation
9
+
10
+struct FlowerVerbose: Hashable, Identifiable, Codable {
11
+    let id: Int
12
+    
13
+    let common_name: String?
14
+    var commonName: String {
15
+        if let cm = common_name {
16
+            return cm
17
+        } else {
18
+            return scientificName
19
+        }
20
+    }
21
+    
22
+    let slug: String // *
23
+    
24
+    let scientific_name: String
25
+    var scientificName: String { scientific_name }
26
+    
27
+    let image_url: String?
28
+    var imageURL: String {
29
+        if let iu = image_url {
30
+            return iu
31
+        } else {
32
+            return "[Image URL]"
33
+        }
34
+    }
35
+    
36
+    let year: Int?
37
+    let bibliography: String?
38
+    let author: String? // *
39
+    
40
+    let family_common_name: String?
41
+    var familyCommonName: String {
42
+        if let fcn = family_common_name {
43
+            return fcn
44
+        } else {
45
+            return "[Family Common Name]"
46
+        }
47
+    }
48
+    
49
+    let genus_id: Int // *
50
+    var genusID: Int { genus_id }
51
+    
52
+    let observations: String? // new
53
+    let vegetable: Bool? // new
54
+    let links: FlowerLinks
55
+    let genus: GenusVerbose? // changed
56
+    let family: FamilyVerbose? // changed
57
+    let species: [Flower]? // new
58
+    
59
+//    let subspecies: []
60
+//    let varieties: []
61
+//    let hybrids: []
62
+//    let forms : []
63
+//    let subvarieties: []
64
+//    let sources: []
65
+    
66
+    // Properties added by us
67
+    let isFavorite: Bool
68
+    let hasBeenFound: Bool
69
+}
70
+
71
+struct GenusVerbose: Codable, Hashable, Identifiable {
72
+    let id: Int
73
+    let name: String
74
+    let slug: String
75
+    let links: FlowerLinks
76
+}
77
+
78
+struct FamilyVerbose: Codable, Hashable, Identifiable {
79
+    let id: Int
80
+    let name: String
81
+    let common_name: String
82
+    let slug: String
83
+    let links: FlowerLinks
84
+}
85
+
86
+/*
87
+ "id": 238331,
88
+ "common_name": null,
89
+ "slug": "senecio-gamolepis",
90
+ "scientific_name": "Senecio gamolepis",
91
+ "main_species_id": 273225,
92
+ "image_url": null,
93
+ "year": 1955,
94
+ "bibliography": "Notas Mus. La Plata, Bot. 18(89): 222 (1955)",
95
+ "author": "Cabrera",
96
+ "family_common_name": "Aster family",
97
+ "genus_id": 669,
98
+ "observations": "Peru",
99
+ "vegetable": false,
100
+ "links": {},
101
+ "main_species": {},
102
+ "genus": {},
103
+ "family": {},
104
+ "species": [],
105
+ "subspecies": [ ],
106
+ "varieties": [ ],
107
+ "hybrids": [ ],
108
+ "forms": [ ],
109
+ "subvarieties": [ ],
110
+ "sources": []
111
+ */

+ 13
- 0
Flowerdex/Flowerdex/Model/HistoryResponseModel.swift Vedi File

@@ -0,0 +1,13 @@
1
+//
2
+//  HistoryResponseModel.swift
3
+//  Flowerdex
4
+//
5
+//  Created by Víctor A. Hernández on 12/21/20.
6
+//
7
+
8
+import Foundation
9
+
10
+struct HistoryResponseModel: Hashable, Codable {
11
+    let data: [FlowerVerbose]?
12
+    let error: String?
13
+}

+ 7
- 3
Flowerdex/Flowerdex/Services/FlowerService.swift Vedi File

@@ -20,13 +20,13 @@ class FlowerService: ObservableObject {
20 20
         list.append(URLQueryItem(name: "vegetable", value: "\(filters.vegetable)"))
21 21
         
22 22
         if filters.commonName != "" {
23
-            list.append(URLQueryItem(name: "q", value: filters.commonName))
23
+            list.append(URLQueryItem(name: "q", value: filters.commonName.lowercased()))
24 24
         }
25
-//        list.append(URLQueryItem(name: "common_name", value: filters.commonName)) // if common_name was empty, which didn't make sense
25
+//        list.append(URLQueryItem(name: "common_name", value: filters.commonName.lowercased())) // if common_name was empty, which didn't make sense
26 26
         
27 27
 
28 28
         if filters.scientificName != "" {
29
-            list.append(URLQueryItem(name: "scientific_name", value: filters.scientificName))
29
+            list.append(URLQueryItem(name: "scientific_name", value: filters.scientificName.lowercased()))
30 30
         }
31 31
         
32 32
         if filters.growthMonths > 0 {
@@ -37,6 +37,10 @@ class FlowerService: ObservableObject {
37 37
             list.append(URLQueryItem(name: "bloom_months", value: "\(filters.bloomMonths)"))
38 38
         }
39 39
         
40
+        if filters.flowerColor != "" {
41
+            list.append(URLQueryItem(name: "flower_color", value: filters.flowerColor.lowercased()))
42
+        }
43
+        
40 44
         return list
41 45
         
42 46
     }

+ 100
- 0
Flowerdex/Flowerdex/Services/HistoryService.swift Vedi File

@@ -0,0 +1,100 @@
1
+//
2
+//  HistoryService.swift
3
+//  Flowerdex
4
+//
5
+//  Created by Víctor A. Hernández on 12/21/20.
6
+//
7
+
8
+import Foundation
9
+
10
+class HistoryService: ObservableObject {
11
+    
12
+    @Published var response: Response = .unfetched
13
+    @Published var items = [FlowerVerbose]()
14
+    
15
+    
16
+    private func getBaseURLComponent(_ apiPath: String) -> URLComponents {
17
+        var components = URLComponents()
18
+        components.scheme = Constants.Services.apiScheme
19
+        components.host = Constants.Services.apiHost
20
+        components.path = apiPath
21
+        if apiPath == Constants.Services.apiGetHistoryPath {
22
+            components.queryItems = [
23
+                URLQueryItem(name: "user_id", value: "\(User.current.id!)"),
24
+            ]
25
+        }
26
+        return components
27
+    }
28
+    
29
+    
30
+    private func fetchFlowers(_ request: URLRequest) {
31
+        URLSession.shared.dataTask(with: request) { data, response, error in
32
+            guard let data = data else {
33
+                print("No data in response: \(error?.localizedDescription ?? "Unknown error")")
34
+                DispatchQueue.main.async {
35
+                    self.response = .failure
36
+                }
37
+                return
38
+            }
39
+            print(response ?? "No response")
40
+//            print("Recieved data:", String(decoding: data, as: UTF8.self))
41
+            
42
+            guard let apiResponse = try? JSONDecoder().decode(HistoryResponseModel.self, from: data) else {
43
+                print("Failed to decode History response")
44
+                DispatchQueue.main.async {
45
+                    self.response = .failure
46
+                }
47
+                return
48
+            }
49
+            
50
+            if let httpResponse = response as? HTTPURLResponse {
51
+                DispatchQueue.main.async {
52
+                    if httpResponse.statusCode == 200 && apiResponse.error == nil {
53
+                        print("No error")
54
+                        self.response = .success
55
+                        self.items = apiResponse.data!
56
+                    } else {
57
+                        print("Error: \(apiResponse.error ?? "Unknown error")")
58
+                        self.response = .failure
59
+//                        self.items = [FlowerVerbose]()
60
+                    }
61
+                }
62
+            }
63
+        }.resume()
64
+    }
65
+    
66
+    
67
+    func getHistory() {
68
+        
69
+        // Start loading state
70
+        self.response = .loading
71
+        
72
+        // Construct URL
73
+        let components = getBaseURLComponent(Constants.Services.apiGetHistoryPath)
74
+        
75
+        // Prepare and carry out request
76
+        var request = URLRequest(url: components.url!)
77
+        request.httpMethod = "GET"
78
+        self.fetchFlowers(request)
79
+        
80
+    }
81
+    
82
+    
83
+    func getWishlist() {
84
+        
85
+        // Start loading state
86
+        self.response = .loading
87
+        
88
+        // Construct URL
89
+        let components = getBaseURLComponent(Constants.Services.apiGetWishlistPath)
90
+        
91
+        // Prepare and carry out request
92
+        var request = URLRequest(url: components.url!)
93
+        request.httpMethod = "GET"
94
+        self.fetchFlowers(request)
95
+        
96
+    }
97
+    
98
+    
99
+    
100
+}

+ 0
- 45
Flowerdex/Flowerdex/Services/ImageManager.swift Vedi File

@@ -45,48 +45,3 @@ class ImageManager: ObservableObject {
45 45
     }
46 46
     
47 47
 }
48
-
49
-//
50
-//class HistoryManager: ObservableObject {
51
-//    
52
-//    @Published var response: Response = .unfetched
53
-//    @Published var hasBeenFound = false
54
-//    let baseURL = "https://ada.uprrp.edu/~victor.hernandez17/"
55
-//    let foundEndpoint = "flowerdex/isFoundFlower.php"
56
-//    let putFoundEndpoint = "flowerdex/putFoundFlower.php"
57
-//    let removeFoundEndpoint = "flowerdex/removeFoundFlower.php"
58
-//    
59
-//    func fetch() {
60
-//        
61
-//        // Start loading state
62
-//        self.response = .loading
63
-//        
64
-//        let urlString = baseURL + foundEndpoint
65
-//        
66
-//        if let url = URL(string: urlString) {
67
-//            URLSession(configuration: .default).dataTask(with: url) { data, response, error in
68
-//                
69
-//                if error != nil {
70
-//                    print("Error occurred: \(error?.localizedDescription ?? "Unknown error")")
71
-//                    return
72
-//                }
73
-//                
74
-//                guard let data = data else {
75
-//                    print("No data in response: \(error?.localizedDescription ?? "Unknown error")")
76
-//                    return
77
-//                }
78
-//                
79
-//                DispatchQueue.main.async {
80
-//                    if let img = UIImage(data: data) {
81
-//                        self.image = img
82
-//                        self.response = .success
83
-//                    } else {
84
-//                        self.response = .failure
85
-//                    }
86
-//                }
87
-//                
88
-//            }.resume()
89
-//        }
90
-//    }URLSession.shared.dataTask(with: request) { data, response, error in
91
-//    
92
-//}

+ 2
- 0
Flowerdex/Flowerdex/Supporting/Constants.swift Vedi File

@@ -24,6 +24,8 @@ struct Constants {
24 24
         static let apiRemoveFavoritePath = "/~victor.hernandez17/flowerdex/removeFavoriteFlower.php"
25 25
         static let apiPutFoundPath = "/~victor.hernandez17/flowerdex/putFoundFlower.php"
26 26
         static let apiRemoveFoundPath = "/~victor.hernandez17/flowerdex/removeFoundFlower.php"
27
+        static let apiGetHistoryPath = "/~victor.hernandez17/flowerdex/listHistory.php"
28
+        static let apiGetWishlistPath = "/~victor.hernandez17/flowerdex/getWishlist.php"
27 29
     }
28 30
     
29 31
     struct Colors {

Flowerdex/Flowerdex/View/Authentication/LoginView.swift → Flowerdex/Flowerdex/View/Authentication Page/LoginView.swift Vedi File


Flowerdex/Flowerdex/View/Authentication/RegisterView.swift → Flowerdex/Flowerdex/View/Authentication Page/RegisterView.swift Vedi File


+ 6
- 2
Flowerdex/Flowerdex/View/Discover Page/DiscoverView.swift Vedi File

@@ -21,6 +21,7 @@ struct DiscoverView: View {
21 21
     @State var bloomMonths: Int = 0
22 22
     @State var scientificName: String = ""
23 23
     @State var commonName: String = ""
24
+    @State var flowerColor: String = ""
24 25
     
25 26
     init() {
26 27
         // FROM: https://sarunw.com/posts/uinavigationbar-changes-in-ios13/
@@ -42,12 +43,15 @@ struct DiscoverView: View {
42 43
                     FilterButtonIcon()
43 44
                 }
44 45
                 .sheet(isPresented: $showingFilterPane, content: {
45
-                    FiltersSheet(showingFilterPane: $showingFilterPane, page: $page, edible: $edible, vegetable: $vegetable, petalCount: $petalCount, growthMonths: $growthMonths, bloomMonths: $bloomMonths, scientificName: $scientificName, commonName: $commonName)
46
+                    FiltersSheet(showingFilterPane: $showingFilterPane, page: $page, edible: $edible, vegetable: $vegetable, petalCount: $petalCount, growthMonths: $growthMonths, bloomMonths: $bloomMonths, scientificName: $scientificName, commonName: $commonName, flowerColor: $flowerColor)
46 47
                 })
47 48
             )
48 49
         }
49 50
         .onAppear {
50
-            self.fModel.getFlowers(p: page, f: nil)
51
+            // Refresh only if there's no items loaded yet
52
+            if self.fModel.items.count == 0 {
53
+                self.fModel.getFlowers(p: page, f: nil)
54
+            }
51 55
         }
52 56
     }
53 57
     

+ 16
- 0
Flowerdex/Flowerdex/View/Discover Page/FilterFields.swift Vedi File

@@ -16,6 +16,7 @@ struct FilterFields: View {
16 16
     @Binding var bloomMonths: Int
17 17
     @Binding var scientificName: String
18 18
     @Binding var commonName: String
19
+    @Binding var flowerColor: String
19 20
     
20 21
     var body: some View {
21 22
         VStack {
@@ -27,6 +28,7 @@ struct FilterFields: View {
27 28
             BloomMonthsField(bloomMonths: $bloomMonths)
28 29
             CommonNameField(commonName: $commonName)
29 30
             ScientificNameField(scientificName: $scientificName)
31
+            FlowerColorField(flowerColor: $flowerColor)
30 32
         }
31 33
         .padding()
32 34
     }
@@ -41,6 +43,20 @@ struct MainText: View {
41 43
     }
42 44
 }
43 45
 
46
+struct FlowerColorField: View {
47
+    @Binding var flowerColor: String
48
+    @Environment(\.colorScheme) var colorScheme
49
+    var body: some View {
50
+        TextField("FlowerColor", text: $flowerColor)
51
+            .disableAutocorrection(true)
52
+            .autocapitalization(.none)
53
+            .padding()
54
+            .background(colorScheme == .dark ? Constants.Colors.darkGrayColor : Constants.Colors.lightGrayColor)
55
+            .cornerRadius(5.0)
56
+            .padding(.bottom, 5)
57
+    }
58
+}
59
+
44 60
 struct CommonNameField: View {
45 61
     @Binding var commonName: String
46 62
     @Environment(\.colorScheme) var colorScheme

+ 3
- 2
Flowerdex/Flowerdex/View/Discover Page/FiltersSheet.swift Vedi File

@@ -21,14 +21,15 @@ struct FiltersSheet: View {
21 21
     @Binding var bloomMonths: Int
22 22
     @Binding var scientificName: String
23 23
     @Binding var commonName: String
24
+    @Binding var flowerColor: String
24 25
 
25 26
     var body: some View {
26
-        FilterFields(edible: $edible, vegetable: $vegetable, petalCount: $petalCount, growthMonths: $growthMonths, bloomMonths: $bloomMonths, scientificName: $scientificName, commonName: $commonName)
27
+        FilterFields(edible: $edible, vegetable: $vegetable, petalCount: $petalCount, growthMonths: $growthMonths, bloomMonths: $bloomMonths, scientificName: $scientificName, commonName: $commonName, flowerColor: $flowerColor)
27 28
         
28 29
         Spacer()
29 30
         
30 31
         Button(action: {
31
-            let filters = Filters(edible: edible, vegetable: vegetable, petalCount: petalCount, growthMonths: growthMonths, bloomMonths: bloomMonths, scientificName: scientificName, commonName: commonName)
32
+            let filters = Filters(edible: edible, vegetable: vegetable, petalCount: petalCount, growthMonths: growthMonths, bloomMonths: bloomMonths, scientificName: scientificName, commonName: commonName, flowerColor: flowerColor)
32 33
             
33 34
             self.fModel.getFlowers(p: page, f: filters)
34 35
             

+ 112
- 0
Flowerdex/Flowerdex/View/History Page/FlowerVerboseDetailView.swift Vedi File

@@ -0,0 +1,112 @@
1
+//
2
+//  FlowerVerboseDetailView.swift
3
+//  Flowerdex
4
+//
5
+//  Created by Víctor A. Hernández on 12/21/20.
6
+//
7
+
8
+import SwiftUI
9
+
10
+struct FlowerVerboseDetailView: View {
11
+    
12
+    var flower: FlowerVerbose
13
+    @State var hasBeenFoundLocal: Bool
14
+    @ObservedObject var imageManager = ImageManager()
15
+    @EnvironmentObject var fModel: FlowerService
16
+    var size: CGFloat = 300
17
+    
18
+    var body: some View {
19
+        VStack(alignment: .leading, spacing: 0) {
20
+            HStack {
21
+                Text(self.flower.commonName.capitalized)
22
+                    .font(.title)
23
+                    .fontWeight(/*@START_MENU_TOKEN@*/.semibold/*@END_MENU_TOKEN@*/)
24
+                    .foregroundColor(Constants.Colors.blueGray)
25
+                //                .frame(width: .infinity, alignment: .center)
26
+                Spacer()
27
+                Button(action: {
28
+                    
29
+                    // Depending on past value
30
+                    if self.hasBeenFoundLocal {
31
+                        fModel.removeFound(self.flower.id)
32
+                    } else {
33
+                        fModel.putFound(self.flower.id)
34
+                    }
35
+                    
36
+                    // Change value after request
37
+                    self.hasBeenFoundLocal.toggle()
38
+                    
39
+                }) {
40
+                    if self.hasBeenFoundLocal {
41
+                        Image(systemName: "eye")
42
+                            .font(.title)
43
+                            .foregroundColor(Constants.Colors.rausch)
44
+                    } else {
45
+                        Image(systemName: "eye.slash")
46
+                            .font(.title)
47
+                            .foregroundColor(Constants.Colors.blueGray)
48
+                    }
49
+                    
50
+                }
51
+            }
52
+            
53
+            .padding()
54
+            
55
+
56
+            
57
+//            URLImage(url: self.flower.imageURL)
58
+//            Image("amapola")
59
+            // TODO: make placeholder and actual image look equally good
60
+            Image(uiImage: self.imageManager.image!)
61
+                .resizable()
62
+                .frame(width: self.size, height: self.size)
63
+            FlowerVerboseInformationPane(flower: flower)
64
+            Spacer()
65
+        }
66
+        .onAppear {
67
+            self.imageManager.fetchImage(self.flower.imageURL)
68
+        }
69
+    }
70
+}
71
+
72
+// TODO: account for text overflow
73
+struct FlowerVerboseInformationPane: View {
74
+    var flower: FlowerVerbose
75
+    var body: some View {
76
+        HStack {
77
+            VStack(alignment: .leading, spacing: nil) {
78
+                Text("Scientific Name:")
79
+                    .bold()
80
+                Text("\(self.flower.scientificName)")
81
+                    .foregroundColor(Constants.Colors.blueGray)
82
+                    .padding(.bottom)
83
+                Text("Year Discovered:")
84
+                    .bold()
85
+                Text("\(self.flower.year ?? -999)")
86
+                    .foregroundColor(Constants.Colors.blueGray)
87
+            }
88
+            .foregroundColor(Constants.Colors.rausch)
89
+            .padding()
90
+            VStack(alignment: .leading, spacing: nil) {
91
+                Text("Bibliography:")
92
+                    .bold()
93
+                Text("\(self.flower.bibliography ?? "n/a")")
94
+                    .foregroundColor(Constants.Colors.blueGray)
95
+                    .padding(.bottom)
96
+                Text("Author:")
97
+                    .bold()
98
+                Text("\(self.flower.author ?? "n/a")")
99
+                    .foregroundColor(Constants.Colors.blueGray)
100
+            }
101
+            .foregroundColor(Constants.Colors.rausch)
102
+            .padding()
103
+        }
104
+        .padding(.vertical)
105
+    }
106
+}
107
+
108
+struct FlowerVerboseDetailView_Previews: PreviewProvider {
109
+    static var previews: some View {
110
+        FlowerDetailView(flower: dummyFlowers[0], hasBeenFoundLocal: true)
111
+    }
112
+}

+ 50
- 0
Flowerdex/Flowerdex/View/History Page/HistoryView.swift Vedi File

@@ -0,0 +1,50 @@
1
+//
2
+//  HistoryView.swift
3
+//  Flowerdex
4
+//
5
+//  Created by Víctor A. Hernández on 12/21/20.
6
+//
7
+
8
+import SwiftUI
9
+
10
+struct HistoryView: View {
11
+    @EnvironmentObject var hModel: HistoryService
12
+    var body: some View {
13
+        NavigationView {
14
+            ScrollableHistoryContent()
15
+        }
16
+        .onAppear {
17
+            self.hModel.getHistory()
18
+        }
19
+    }
20
+}
21
+
22
+struct ScrollableHistoryContent: View {
23
+    @EnvironmentObject var hModel: HistoryService
24
+    var body: some View {
25
+        if hModel.response == .success {
26
+            List {
27
+                HistoryList(flowerList: hModel.items)
28
+            }
29
+            .navigationBarTitle("Discover")
30
+        } else if hModel.response == .loading || hModel.response == .unfetched {
31
+            // TODO: make this centered vertically within parent ScrollView
32
+            ProgressView("Loading...")
33
+        } else if hModel.response == .failure {
34
+            // TODO: make this centered vertically within parent ScrollView
35
+            FailureIcon()
36
+        }
37
+    }
38
+}
39
+
40
+struct HistoryList: View {
41
+    var flowerList: [FlowerVerbose]
42
+    var body: some View {
43
+        ForEach(flowerList) { flower in
44
+            NavigationLink(destination: FlowerVerboseDetailView(flower: flower, hasBeenFoundLocal: flower.hasBeenFound)) {
45
+                Text(flower.commonName.capitalized)
46
+            }
47
+//                    .foregroundColor(Constants.Colors.blueGray)
48
+        }
49
+    }
50
+}

+ 27
- 0
Flowerdex/Flowerdex/View/MainView.swift Vedi File

@@ -0,0 +1,27 @@
1
+//
2
+//  MainView.swift
3
+//  Flowerdex
4
+//
5
+//  Created by Víctor A. Hernández on 12/21/20.
6
+//
7
+
8
+import SwiftUI
9
+
10
+struct MainView: View {
11
+    var body: some View {
12
+        TabView {
13
+            DiscoverView()
14
+                .tabItem {
15
+                    Image(systemName: "magnifyingglass")
16
+                    Text("Discover")
17
+                }
18
+            
19
+            HistoryView()
20
+                .tabItem {
21
+                    Image(systemName: "square.and.pencil")
22
+                    Text("History")
23
+                }
24
+        }
25
+        .accentColor(Constants.Colors.rausch)
26
+    }
27
+}