From 6e435e0e2596de18a04d45f648f28571cf7ffeff Mon Sep 17 00:00:00 2001 From: Victor Aceves Date: Fri, 6 Aug 2021 21:08:05 -0500 Subject: [PATCH 1/9] Add data models to control Likes --- Secretly.xcodeproj/project.pbxproj | 8 ++++++++ Secretly/Models/Author.swift | 20 ++++++++++++++++++++ Secretly/Models/Like.swift | 18 ++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 Secretly/Models/Author.swift create mode 100644 Secretly/Models/Like.swift diff --git a/Secretly.xcodeproj/project.pbxproj b/Secretly.xcodeproj/project.pbxproj index 8dd4829..a0d4b7e 100644 --- a/Secretly.xcodeproj/project.pbxproj +++ b/Secretly.xcodeproj/project.pbxproj @@ -56,6 +56,8 @@ 30C77CB6266AF48300A888DC /* CurrentUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C77CB5266AF48300A888DC /* CurrentUser.swift */; }; 30C77CB8266BD44300A888DC /* CreatePostViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C77CB7266BD44300A888DC /* CreatePostViewController.swift */; }; 30FD0E722659645A006E309A /* Faker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30FD0E712659645A006E309A /* Faker.swift */; }; + 657AE4DF26BE032B00953718 /* Author.swift in Sources */ = {isa = PBXBuildFile; fileRef = 657AE4DE26BE032B00953718 /* Author.swift */; }; + 657AE4E126BE037300953718 /* Like.swift in Sources */ = {isa = PBXBuildFile; fileRef = 657AE4E026BE037300953718 /* Like.swift */; }; E021984723FA35E00025C28E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E021984623FA35E00025C28E /* AppDelegate.swift */; }; E021984923FA35E00025C28E /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E021984823FA35E00025C28E /* SceneDelegate.swift */; }; E021984B23FA35E00025C28E /* WelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E021984A23FA35E00025C28E /* WelcomeViewController.swift */; }; @@ -125,6 +127,8 @@ 30C77CB5266AF48300A888DC /* CurrentUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentUser.swift; sourceTree = ""; }; 30C77CB7266BD44300A888DC /* CreatePostViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreatePostViewController.swift; sourceTree = ""; }; 30FD0E712659645A006E309A /* Faker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Faker.swift; sourceTree = ""; }; + 657AE4DE26BE032B00953718 /* Author.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Author.swift; sourceTree = ""; }; + 657AE4E026BE037300953718 /* Like.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Like.swift; sourceTree = ""; }; E021984323FA35E00025C28E /* Secretly.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Secretly.app; sourceTree = BUILT_PRODUCTS_DIR; }; E021984623FA35E00025C28E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; E021984823FA35E00025C28E /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -248,6 +252,8 @@ 307A30572661AD540020DF8B /* User.swift */, 30C77CB3266AF47300A888DC /* Credentials.swift */, 30C77CB5266AF48300A888DC /* CurrentUser.swift */, + 657AE4DE26BE032B00953718 /* Author.swift */, + 657AE4E026BE037300953718 /* Like.swift */, ); path = Models; sourceTree = ""; @@ -445,6 +451,7 @@ 302B5849267E658E007133E6 /* RequestError.swift in Sources */, 302B5846267E658E007133E6 /* AmacaConfig.swift in Sources */, 302BB61C267D7CC800FD74F5 /* PreviewPostVIew.swift in Sources */, + 657AE4E126BE037300953718 /* Like.swift in Sources */, 3033795D267537B40066D94A /* FeedCollectionViewController+UICollectionViewDelegateFlowLayout .swift in Sources */, 30BC8BA82662CEBA00F7E6A5 /* Checksum.swift in Sources */, 30FD0E722659645A006E309A /* Faker.swift in Sources */, @@ -458,6 +465,7 @@ 30BC8BA62662C02300F7E6A5 /* CacheImage.swift in Sources */, 302B5847267E658E007133E6 /* StatusCode.swift in Sources */, 302B584B267E658E007133E6 /* RequestBuilder.swift in Sources */, + 657AE4DF26BE032B00953718 /* Author.swift in Sources */, 307A306526629B990020DF8B /* AuthorView.swift in Sources */, 30C77CB4266AF47300A888DC /* Credentials.swift in Sources */, 30BC8BA22662BB0000F7E6A5 /* DataContainer.swift in Sources */, diff --git a/Secretly/Models/Author.swift b/Secretly/Models/Author.swift new file mode 100644 index 0000000..7382f9b --- /dev/null +++ b/Secretly/Models/Author.swift @@ -0,0 +1,20 @@ +// +// Author.swift +// Secretly +// +// Created by Victor Aceves on 06/08/21. +// Copyright © 2021 3zcurdia. All rights reserved. +// + +import Foundation +import UIKit + +struct Author: Restable { + let name: String? + let id: String? + + init(id: String, name: String){ + self.name = name + self.id = id + } +} diff --git a/Secretly/Models/Like.swift b/Secretly/Models/Like.swift new file mode 100644 index 0000000..9fd0bcf --- /dev/null +++ b/Secretly/Models/Like.swift @@ -0,0 +1,18 @@ +// +// Like.swift +// Secretly +// +// Created by Victor Aceves on 06/08/21. +// Copyright © 2021 3zcurdia. All rights reserved. +// + +import Foundation +import UIKit + +struct Like: Restable { + let id: Int + let likeableType: String + let likeableId: Int + let createdAt, updatedAt: String + let user: User +} From 76c6d1f1d2dc6b467a780799ffb1f1b097a0ae5e Mon Sep 17 00:00:00 2001 From: Victor Aceves Date: Fri, 6 Aug 2021 21:09:23 -0500 Subject: [PATCH 2/9] Add LikesService --- Secretly.xcodeproj/project.pbxproj | 4 +++ Secretly/Services/LikesService.swift | 37 ++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 Secretly/Services/LikesService.swift diff --git a/Secretly.xcodeproj/project.pbxproj b/Secretly.xcodeproj/project.pbxproj index a0d4b7e..55229a0 100644 --- a/Secretly.xcodeproj/project.pbxproj +++ b/Secretly.xcodeproj/project.pbxproj @@ -58,6 +58,7 @@ 30FD0E722659645A006E309A /* Faker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30FD0E712659645A006E309A /* Faker.swift */; }; 657AE4DF26BE032B00953718 /* Author.swift in Sources */ = {isa = PBXBuildFile; fileRef = 657AE4DE26BE032B00953718 /* Author.swift */; }; 657AE4E126BE037300953718 /* Like.swift in Sources */ = {isa = PBXBuildFile; fileRef = 657AE4E026BE037300953718 /* Like.swift */; }; + 657AE4E926BE229E00953718 /* LikesService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 657AE4E826BE229E00953718 /* LikesService.swift */; }; E021984723FA35E00025C28E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E021984623FA35E00025C28E /* AppDelegate.swift */; }; E021984923FA35E00025C28E /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E021984823FA35E00025C28E /* SceneDelegate.swift */; }; E021984B23FA35E00025C28E /* WelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E021984A23FA35E00025C28E /* WelcomeViewController.swift */; }; @@ -129,6 +130,7 @@ 30FD0E712659645A006E309A /* Faker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Faker.swift; sourceTree = ""; }; 657AE4DE26BE032B00953718 /* Author.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Author.swift; sourceTree = ""; }; 657AE4E026BE037300953718 /* Like.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Like.swift; sourceTree = ""; }; + 657AE4E826BE229E00953718 /* LikesService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LikesService.swift; sourceTree = ""; }; E021984323FA35E00025C28E /* Secretly.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Secretly.app; sourceTree = BUILT_PRODUCTS_DIR; }; E021984623FA35E00025C28E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; E021984823FA35E00025C28E /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -226,6 +228,7 @@ 30C77CAF266AD69700A888DC /* CurrentUserService.swift */, 304E06C726742BDA00A99128 /* CreatePostService.swift */, 304E06C926742CC500A99128 /* FeedService.swift */, + 657AE4E826BE229E00953718 /* LikesService.swift */, ); path = Services; sourceTree = ""; @@ -460,6 +463,7 @@ 307A305E2661CD510020DF8B /* PostCollectionViewCell.swift in Sources */, 302BB626267E447900FD74F5 /* PostInputViewController+UITextFieldDelegate.swift in Sources */, 30BC8BA02662B8A700F7E6A5 /* StorageType.swift in Sources */, + 657AE4E926BE229E00953718 /* LikesService.swift in Sources */, 307A305B2661B7A20020DF8B /* FeedCollectionViewController.swift in Sources */, 304E06CC2674442800A99128 /* UIImage+encodeBase64.swift in Sources */, 30BC8BA62662C02300F7E6A5 /* CacheImage.swift in Sources */, diff --git a/Secretly/Services/LikesService.swift b/Secretly/Services/LikesService.swift new file mode 100644 index 0000000..6922fe5 --- /dev/null +++ b/Secretly/Services/LikesService.swift @@ -0,0 +1,37 @@ +// +// LikesService.swift +// Secretly +// +// Created by Victor Aceves on 06/08/21. +// Copyright © 2021 3zcurdia. All rights reserved. +// + +import Foundation + +struct LikesService { + private var likesEndpoint: RestClient + + init(postId: Int){ + likesEndpoint = RestClient(client: AmacaConfig.shared.httpClient, path: "/api/v1/posts/\(postId)/like") + } + + func get(completion: @escaping ([Like]) -> Void) { + likesEndpoint.list { result in + guard let likes = try? result.get() else { return } + DispatchQueue.main.async { completion(likes) } + } + } + + func add(_ model: Like, complete: @escaping (Result) -> Void ) { + try? likesEndpoint.create(model: model) { + result in + DispatchQueue.main.async { complete(result) } + } + } + + func delete(_ model: Like, complete: @escaping (Result) -> Void ) { + likesEndpoint.delete(model: model) { result in + DispatchQueue.main.async { complete(result) } + } + } +} From 1285ff16a9474e73f82b8f5b3d9d2646b87ef619 Mon Sep 17 00:00:00 2001 From: Victor Aceves Date: Fri, 6 Aug 2021 22:03:54 -0500 Subject: [PATCH 3/9] Update Post model and PostCollectionViewCell --- Secretly/Models/Post.swift | 6 +++++- Secretly/Views/PostCollectionViewCell.swift | 4 +++- Secretly/Views/PostCollectionViewCell.xib | 11 ++++++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/Secretly/Models/Post.swift b/Secretly/Models/Post.swift index eba5ff0..b616d66 100644 --- a/Secretly/Models/Post.swift +++ b/Secretly/Models/Post.swift @@ -21,8 +21,10 @@ struct Post: Restable { let longitude: Double? let createdAt: Date? let updatedAt: Date? + var likesCount: Int? + let isLiked: Bool? - init(content: String, backgroundColor: String, latitude: Double? = nil, longitude: Double? = nil, image: UIImage? = nil) { + init(content: String, backgroundColor: String, latitude: Double? = nil, longitude: Double? = nil, image: UIImage? = nil, likesCount: Int? = nil, isLiked: Bool? = nil) { self.content = content self.backgroundColor = backgroundColor self.id = nil @@ -34,6 +36,8 @@ struct Post: Restable { self.commentsCount = nil self.createdAt = nil self.updatedAt = nil + self.likesCount = likesCount + self.isLiked = isLiked } func encode(to encoder: Encoder) throws { diff --git a/Secretly/Views/PostCollectionViewCell.swift b/Secretly/Views/PostCollectionViewCell.swift index ee08d53..62734f1 100644 --- a/Secretly/Views/PostCollectionViewCell.swift +++ b/Secretly/Views/PostCollectionViewCell.swift @@ -20,7 +20,8 @@ class PostCollectionViewCell: UICollectionViewCell { @IBOutlet weak var imageView: UIImageView! @IBOutlet weak var likeState: UIImageView! @IBOutlet weak var commentCounter: UILabel! - + @IBOutlet weak var likesCounter: UILabel! + override func awakeFromNib() { super.awakeFromNib() } @@ -37,5 +38,6 @@ class PostCollectionViewCell: UICollectionViewCell { ImageLoader.load(postImg.mediumUrl) { img in self.imageView.image = img } } self.authorView.author = post.user + self.likesCounter.text = "\(post.likesCount ?? 0) Likes" } } diff --git a/Secretly/Views/PostCollectionViewCell.xib b/Secretly/Views/PostCollectionViewCell.xib index 7b3a4e2..2ba3c0c 100644 --- a/Secretly/Views/PostCollectionViewCell.xib +++ b/Secretly/Views/PostCollectionViewCell.xib @@ -62,6 +62,12 @@ + @@ -73,9 +79,11 @@ + + @@ -90,8 +98,9 @@ + - + From 1ddb18c190aa58b4f7d6aa2a3232fd879fd2c355 Mon Sep 17 00:00:00 2001 From: Victor Aceves Date: Sat, 28 Aug 2021 01:11:12 -0500 Subject: [PATCH 4/9] Update HttpResponse and RestClient to perform like requests --- Secretly/Network/HttpResponse.swift | 16 +++++++++++++--- Secretly/Network/RestClient.swift | 14 ++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/Secretly/Network/HttpResponse.swift b/Secretly/Network/HttpResponse.swift index eb0543a..c304312 100644 --- a/Secretly/Network/HttpResponse.swift +++ b/Secretly/Network/HttpResponse.swift @@ -12,14 +12,24 @@ struct HttpResponse { let httpUrlResponse: HTTPURLResponse init(response: URLResponse?) { - self.httpUrlResponse = (response as? HTTPURLResponse) ?? HTTPURLResponse() + httpUrlResponse = (response as? HTTPURLResponse) ?? HTTPURLResponse() } var status: StatusCode { - return StatusCode(rawValue: self.httpUrlResponse.statusCode) + return StatusCode(rawValue: httpUrlResponse.statusCode) } func result(for data: Data?) -> Result { - return status.result().map { _ in data } + if let udata = data, !udata.isEmpty { + let currentData = String(data: udata, encoding: .utf8) + debugPrint("Response: \(status) \(httpUrlResponse.statusCode) \(httpUrlResponse.url!) -d \(String(describing: currentData))") + } else { + debugPrint("Response: \(status) \(httpUrlResponse.statusCode) \(httpUrlResponse.url!)") + } + if let udata = data, !udata.isEmpty { + return status.result().map { _ in data } + } else { + return status.result().map { _ in nil } + } } } diff --git a/Secretly/Network/RestClient.swift b/Secretly/Network/RestClient.swift index 3048ccf..d602a83 100644 --- a/Secretly/Network/RestClient.swift +++ b/Secretly/Network/RestClient.swift @@ -51,6 +51,13 @@ struct RestClient { } } + func create(complete: @escaping (Result) -> Void) throws { + client.post(path: path, body: nil) { result in + let newResult = result.flatMap { parse(data: $0) } + complete(newResult) + } + } + func create(model: T, complete: @escaping (Result) -> Void) throws { let data = try encoder.encode(model) client.post(path: path, body: data) { result in @@ -67,6 +74,13 @@ struct RestClient { } } + func delete(complete: @escaping (Result) -> Void) { + client.delete(path: path) { result in + let newResult = result.flatMap { parse(data: $0) } + complete(newResult) + } + } + func delete(model: T, complete: @escaping (Result) -> Void) { client.delete(path: "\(path)/\(model.id)") { result in let newResult = result.flatMap { parse(data: $0) } From 7ae6187dc73b9380f8f267fc6e76cb7fa0601154 Mon Sep 17 00:00:00 2001 From: Victor Aceves Date: Sat, 28 Aug 2021 01:14:41 -0500 Subject: [PATCH 5/9] Update Post and LikesService --- Secretly/Models/Post.swift | 13 +++++++++- Secretly/Services/LikesService.swift | 38 ++++++++++++++-------------- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/Secretly/Models/Post.swift b/Secretly/Models/Post.swift index b616d66..6047a62 100644 --- a/Secretly/Models/Post.swift +++ b/Secretly/Models/Post.swift @@ -22,7 +22,7 @@ struct Post: Restable { let createdAt: Date? let updatedAt: Date? var likesCount: Int? - let isLiked: Bool? + var isLiked: Bool? init(content: String, backgroundColor: String, latitude: Double? = nil, longitude: Double? = nil, image: UIImage? = nil, likesCount: Int? = nil, isLiked: Bool? = nil) { self.content = content @@ -48,4 +48,15 @@ struct Post: Restable { try container.encode(latitude, forKey: .latitude) try container.encode(longitude, forKey: .longitude) } + + mutating func like() { + likesCount = (likesCount ?? 0) + 1 + isLiked = true + } + + mutating func dislike() { + likesCount = (likesCount ?? 0) - 1 + isLiked = false + } + } diff --git a/Secretly/Services/LikesService.swift b/Secretly/Services/LikesService.swift index 6922fe5..da2d4a8 100644 --- a/Secretly/Services/LikesService.swift +++ b/Secretly/Services/LikesService.swift @@ -9,29 +9,29 @@ import Foundation struct LikesService { - private var likesEndpoint: RestClient + private var likesEndpoint: RestClient? + var liked = false - init(postId: Int){ - likesEndpoint = RestClient(client: AmacaConfig.shared.httpClient, path: "/api/v1/posts/\(postId)/like") - } - - func get(completion: @escaping ([Like]) -> Void) { - likesEndpoint.list { result in - guard let likes = try? result.get() else { return } - DispatchQueue.main.async { completion(likes) } - } - } - - func add(_ model: Like, complete: @escaping (Result) -> Void ) { - try? likesEndpoint.create(model: model) { - result in - DispatchQueue.main.async { complete(result) } + init(post: Post?){ + guard let post = post, let postId = post.id else { + likesEndpoint = nil + return } + likesEndpoint = RestClient(client: AmacaConfig.shared.httpClient, path: "/api/v1/posts/\(postId)/likes") + liked = post.isLiked ?? false } - func delete(_ model: Like, complete: @escaping (Result) -> Void ) { - likesEndpoint.delete(model: model) { result in - DispatchQueue.main.async { complete(result) } + mutating func toggleLike(complete: @escaping (Result) -> Void) { + if liked { + liked = !liked + likesEndpoint?.delete { result in + DispatchQueue.main.async { complete(result) } + } + } else { + liked = !liked + try? likesEndpoint?.create { result in + DispatchQueue.main.async { complete(result) } + } } } } From a97a7cacd00df2c4db6ff4832876bf9fbeee0697 Mon Sep 17 00:00:00 2001 From: Victor Aceves Date: Sat, 28 Aug 2021 01:16:13 -0500 Subject: [PATCH 6/9] Add like functionality into PostCollectionViewCell --- Secretly/Views/PostCollectionViewCell.swift | 46 +++++++++++++++++++-- Secretly/Views/PostCollectionViewCell.xib | 37 ++++++++++------- 2 files changed, 63 insertions(+), 20 deletions(-) diff --git a/Secretly/Views/PostCollectionViewCell.swift b/Secretly/Views/PostCollectionViewCell.swift index 62734f1..9d11b72 100644 --- a/Secretly/Views/PostCollectionViewCell.swift +++ b/Secretly/Views/PostCollectionViewCell.swift @@ -10,22 +10,24 @@ import UIKit class PostCollectionViewCell: UICollectionViewCell { static let reuseIdentifier = "feedPostCell" + var likesService: LikesService? var post: Post? { didSet { - updateView() + updateView() + likesService = LikesService(post: post) } } @IBOutlet weak var authorView: AuthorView! @IBOutlet weak var contentLabel: UILabel! @IBOutlet weak var imageView: UIImageView! - @IBOutlet weak var likeState: UIImageView! @IBOutlet weak var commentCounter: UILabel! @IBOutlet weak var likesCounter: UILabel! + @IBOutlet weak var btnLike: UIButton! override func awakeFromNib() { super.awakeFromNib() } - + func updateView() { imageView.image = nil guard let post = post else { return } @@ -38,6 +40,42 @@ class PostCollectionViewCell: UICollectionViewCell { ImageLoader.load(postImg.mediumUrl) { img in self.imageView.image = img } } self.authorView.author = post.user - self.likesCounter.text = "\(post.likesCount ?? 0) Likes" + // Update likes counter label + if (post.likesCount ?? 0 > 0){ + self.likesCounter.text = "\(post.likesCount ?? 0) Me gusta" + }else{ + self.likesCounter.text = "" + } + // Update like image + if post.isLiked ?? false{ + btnLike.setImage(UIImage(systemName: "heart.fill"), for: .normal) + } else{ + btnLike.setImage(UIImage(systemName: "heart"), for: .normal) + } } + @IBAction func toggleLike(_ sender: UIButton) { + likesService?.toggleLike { [unowned self] result in + switch result { + case .success(nil): + btnLike.setImage(UIImage(systemName: "heart"), for: .normal) + post?.dislike() + if (post?.likesCount ?? 0 > 0){ + self.likesCounter.text = "\(post?.likesCount ?? 0) Me gusta" + }else{ + self.likesCounter.text = "" + } + case .success: + btnLike.setImage(UIImage(systemName: "heart.fill"), for: .normal) + post?.like() + if (post?.likesCount ?? 0 > 0){ + self.likesCounter.text = "\(post?.likesCount ?? 0) Me gusta" + }else{ + self.likesCounter.text = "" + } + case .failure: + print("Request fail \(result)") + } + } + } + } diff --git a/Secretly/Views/PostCollectionViewCell.xib b/Secretly/Views/PostCollectionViewCell.xib index 2ba3c0c..97738a8 100644 --- a/Secretly/Views/PostCollectionViewCell.xib +++ b/Secretly/Views/PostCollectionViewCell.xib @@ -30,14 +30,6 @@ - - - - - - - -