diff --git a/Secretly.xcodeproj/project.pbxproj b/Secretly.xcodeproj/project.pbxproj index 8dd4829..e6f4bec 100644 --- a/Secretly.xcodeproj/project.pbxproj +++ b/Secretly.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 1445C8C226EAD8C600769390 /* Like.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1445C8C126EAD8C600769390 /* Like.swift */; }; + 1445C8C626EAD9F700769390 /* LikeService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1445C8C526EAD9F700769390 /* LikeService.swift */; }; 302B5845267E658E007133E6 /* HttpResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302B583D267E658E007133E6 /* HttpResponse.swift */; }; 302B5846267E658E007133E6 /* AmacaConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302B583E267E658E007133E6 /* AmacaConfig.swift */; }; 302B5847267E658E007133E6 /* StatusCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302B583F267E658E007133E6 /* StatusCode.swift */; }; @@ -76,6 +78,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 1445C8C126EAD8C600769390 /* Like.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Like.swift; sourceTree = ""; }; + 1445C8C526EAD9F700769390 /* LikeService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LikeService.swift; sourceTree = ""; }; 302B583D267E658E007133E6 /* HttpResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpResponse.swift; sourceTree = ""; }; 302B583E267E658E007133E6 /* AmacaConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AmacaConfig.swift; sourceTree = ""; }; 302B583F267E658E007133E6 /* StatusCode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusCode.swift; sourceTree = ""; }; @@ -222,6 +226,7 @@ 30C77CAF266AD69700A888DC /* CurrentUserService.swift */, 304E06C726742BDA00A99128 /* CreatePostService.swift */, 304E06C926742CC500A99128 /* FeedService.swift */, + 1445C8C526EAD9F700769390 /* LikeService.swift */, ); path = Services; sourceTree = ""; @@ -248,6 +253,7 @@ 307A30572661AD540020DF8B /* User.swift */, 30C77CB3266AF47300A888DC /* Credentials.swift */, 30C77CB5266AF48300A888DC /* CurrentUser.swift */, + 1445C8C126EAD8C600769390 /* Like.swift */, ); path = Models; sourceTree = ""; @@ -428,6 +434,7 @@ E021984B23FA35E00025C28E /* WelcomeViewController.swift in Sources */, 3072FBDF2680FA5A00B35C8C /* ImageProcessor.swift in Sources */, 302BB622267E38E800FD74F5 /* PostInputViewController+UIImagePickerControllerDelegate.swift in Sources */, + 1445C8C226EAD8C600769390 /* Like.swift in Sources */, 302B5845267E658E007133E6 /* HttpResponse.swift in Sources */, 302B584A267E658E007133E6 /* RestClient.swift in Sources */, 302B5848267E658E007133E6 /* HttpClient.swift in Sources */, @@ -466,6 +473,7 @@ 30C77CB6266AF48300A888DC /* CurrentUser.swift in Sources */, 302BB624267E3A8700FD74F5 /* PostInputViewController+UIColorPickerViewControllerDelegate.swift in Sources */, 304E06CF267468DA00A99128 /* UIColor+Pastel.swift in Sources */, + 1445C8C626EAD9F700769390 /* LikeService.swift in Sources */, 304E06C42674133D00A99128 /* String+isBlank.swift in Sources */, 30BC8BA42662BDEF00F7E6A5 /* ImageStore.swift in Sources */, 3033795B267537490066D94A /* FeedCollectionViewController+UICollectionViewDataSourcePrefetching.swift in Sources */, diff --git a/Secretly/Models/Like.swift b/Secretly/Models/Like.swift new file mode 100644 index 0000000..4428412 --- /dev/null +++ b/Secretly/Models/Like.swift @@ -0,0 +1,19 @@ +// +// Like.swift +// Secretly +// +// Created by Orlando Ortega on 09/09/21. +// Copyright © 2021 3zcurdia. All rights reserved. +// + +import Foundation + +struct Like: Restable { + let id: Int + let createdAt, updatedAt: String + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(id, forKey: .id) + } +} diff --git a/Secretly/Models/Post.swift b/Secretly/Models/Post.swift index eba5ff0..705a3d2 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 likeCount: Int? + var liked: 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? = false) { 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.likeCount = nil + self.liked = isLiked } func encode(to encoder: Encoder) throws { @@ -43,5 +47,17 @@ struct Post: Restable { try container.encode(imageData, forKey: .imageData) try container.encode(latitude, forKey: .latitude) try container.encode(longitude, forKey: .longitude) + try container.encode(likeCount, forKey: .likeCount) + try container.encode(liked, forKey: .liked) + } + + mutating func onLikeOrDislike(likeOrUnlike lou: Bool) { + if lou { + likeCount = (likeCount ?? 0) + 1 + liked = true + } else { + likeCount = (likeCount ?? 0) - 1 + liked = false + } } } diff --git a/Secretly/Network/HttpResponse.swift b/Secretly/Network/HttpResponse.swift index eb0543a..48630f3 100644 --- a/Secretly/Network/HttpResponse.swift +++ b/Secretly/Network/HttpResponse.swift @@ -20,6 +20,10 @@ struct HttpResponse { } func result(for data: Data?) -> Result { - return status.result().map { _ in data } + if let data = data, !data.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..47355d1 100644 --- a/Secretly/Network/RestClient.swift +++ b/Secretly/Network/RestClient.swift @@ -58,6 +58,13 @@ struct RestClient { complete(newResult) } } + + func create(complete: @escaping (Result) -> Void) throws { + client.post(path: path, body: nil) { (result) in + let res = result.flatMap { parse(data: $0) } + complete(res) + } + } func update(model: T, complete: @escaping (Result) -> Void) throws { let data = try encoder.encode(model) @@ -73,6 +80,13 @@ struct RestClient { complete(newResult) } } + + func delete(complete: @escaping (Result) -> Void) throws { + client.delete(path: path) { (result) in + let res = result.flatMap { parse(data: $0) } + complete(res) + } + } private func parseList(data: Data?) -> Result<[T], Error> { if let data = data { diff --git a/Secretly/Services/LikeService.swift b/Secretly/Services/LikeService.swift new file mode 100644 index 0000000..d17c079 --- /dev/null +++ b/Secretly/Services/LikeService.swift @@ -0,0 +1,41 @@ +// +// LikeService.swift +// Secretly +// +// Created by Orlando Ortega on 09/09/21. +// Copyright © 2021 3zcurdia. All rights reserved. +// + +import Foundation + +struct LikeService { + var likeEndpoint: RestClient? + var isLiked = false + + init(post: Post?) { + guard let post = post, let postId = post.id else { + self.likeEndpoint = nil + return + } + self.likeEndpoint = RestClient(client: AmacaConfig.shared.httpClient, path: "/api/v1/posts/\(postId)/like") + isLiked = post.liked ?? false + } + + mutating func likeOrDislikePost(complete: @escaping(Result) -> Void) { + if !isLiked { + isLiked = !isLiked + try? likeEndpoint?.create(complete: { (res) in + DispatchQueue.main.async { + complete(res) + } + }) + } else { + isLiked = !isLiked + try? likeEndpoint?.delete(complete: { (res) in + DispatchQueue.main.async { + complete(res) + } + }) + } + } +} diff --git a/Secretly/Views/PostCollectionViewCell.swift b/Secretly/Views/PostCollectionViewCell.swift index ee08d53..5e1d091 100644 --- a/Secretly/Views/PostCollectionViewCell.swift +++ b/Secretly/Views/PostCollectionViewCell.swift @@ -10,16 +10,19 @@ import UIKit class PostCollectionViewCell: UICollectionViewCell { static let reuseIdentifier = "feedPostCell" + var likeService: LikeService? var post: Post? { didSet { updateView() + likeService = LikeService(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 likeCount: UILabel! + @IBOutlet weak var likeBtn: UIButton! override func awakeFromNib() { super.awakeFromNib() @@ -38,4 +41,32 @@ class PostCollectionViewCell: UICollectionViewCell { } self.authorView.author = post.user } + + @IBAction func onLikeTapped(_ sender: UIButton) { + var lod = false + likeService?.likeOrDislikePost(complete: { [unowned self] res in + switch res { + case .success(nil): + likeBtn.setImage(UIImage(systemName: "heart"), for: .normal) + post?.onLikeOrDislike(likeOrUnlike: lod) + lod = !lod + if(post?.likeCount ?? 0 > 0) { + self.likeCount.text = "\(post?.likeCount ?? 0) Likes" + } else { + self.likeCount.text = "" + } + case .success: + likeBtn.setImage(UIImage(systemName: "heart.fill"), for: .normal) + post?.onLikeOrDislike(likeOrUnlike: lod) + lod = !lod + if(post?.likeCount ?? 0 > 0) { + self.likeCount.text = "\(post?.likeCount ?? 0) Likes" + } else { + self.likeCount.text = "" + } + case .failure: + print("Request failed, cause: \(res)") + } + }) + } } diff --git a/Secretly/Views/PostCollectionViewCell.xib b/Secretly/Views/PostCollectionViewCell.xib index 7b3a4e2..86ab6a9 100644 --- a/Secretly/Views/PostCollectionViewCell.xib +++ b/Secretly/Views/PostCollectionViewCell.xib @@ -1,8 +1,8 @@ - + - + @@ -30,14 +30,6 @@ - - - - - - - -