diff --git a/Secretly.xcodeproj/project.pbxproj b/Secretly.xcodeproj/project.pbxproj index 8dd4829..a555236 100644 --- a/Secretly.xcodeproj/project.pbxproj +++ b/Secretly.xcodeproj/project.pbxproj @@ -16,7 +16,7 @@ 302B584B267E658E007133E6 /* RequestBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302B5843267E658E007133E6 /* RequestBuilder.swift */; }; 302B5859267E720B007133E6 /* ResponseError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302B5850267E720B007133E6 /* ResponseError.swift */; }; 302BB610267C658600FD74F5 /* Amaca.plist in Resources */ = {isa = PBXBuildFile; fileRef = 302BB60F267C658600FD74F5 /* Amaca.plist */; }; - 302BB61C267D7CC800FD74F5 /* PreviewPostVIew.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302BB61B267D7CC800FD74F5 /* PreviewPostVIew.swift */; }; + 302BB61C267D7CC800FD74F5 /* PreviewPostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302BB61B267D7CC800FD74F5 /* PreviewPostView.swift */; }; 302BB61F267E316900FD74F5 /* PostInputViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302BB61D267E316900FD74F5 /* PostInputViewController.swift */; }; 302BB620267E316900FD74F5 /* PostInputViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 302BB61E267E316900FD74F5 /* PostInputViewController.xib */; }; 302BB622267E38E800FD74F5 /* PostInputViewController+UIImagePickerControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302BB621267E38E800FD74F5 /* PostInputViewController+UIImagePickerControllerDelegate.swift */; }; @@ -56,6 +56,19 @@ 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 */; }; + 5416A04926913C65009A3FA9 /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5416A04826913C65009A3FA9 /* ActivityIndicator.swift */; }; + 545F459A269B7E2C007191CC /* Like.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545F4599269B7E2C007191CC /* Like.swift */; }; + 545F459C269B7E6A007191CC /* LikeService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545F459B269B7E6A007191CC /* LikeService.swift */; }; + 545F459E269B81CB007191CC /* FeedCollectionViewController+PostCollectionViewCellDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545F459D269B81CB007191CC /* FeedCollectionViewController+PostCollectionViewCellDelegate.swift */; }; + 54A1CC55268F8D7800E2B643 /* CommentViewCellTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54A1CC54268F8D7800E2B643 /* CommentViewCellTableViewCell.swift */; }; + 54A1CC57268FA72A00E2B643 /* CommentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54A1CC56268FA72A00E2B643 /* CommentViewController.swift */; }; + 54A1CC59268FB44F00E2B643 /* CommentViewControllerDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54A1CC58268FB44F00E2B643 /* CommentViewControllerDataSource.swift */; }; + 54A1CC5B268FD21E00E2B643 /* CommentViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54A1CC5A268FD21E00E2B643 /* CommentViewControllerDelegate.swift */; }; + 54A1CC5D268FD55900E2B643 /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54A1CC5C268FD55900E2B643 /* Comment.swift */; }; + 54A1CC5F268FED3800E2B643 /* CommentService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54A1CC5E268FED3800E2B643 /* CommentService.swift */; }; + 54C7AAF22698B7AE00A6340E /* ViewTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54C7AAF12698B7AE00A6340E /* ViewTest.swift */; }; + 54C7AAF42698C84400A6340E /* CommentView+UITextFieldDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54C7AAF32698C84400A6340E /* CommentView+UITextFieldDelegate.swift */; }; + 54C7AAF62698CD2500A6340E /* CommentView+UISwipeGestures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54C7AAF52698CD2500A6340E /* CommentView+UISwipeGestures.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 */; }; @@ -85,7 +98,7 @@ 302B5843267E658E007133E6 /* RequestBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestBuilder.swift; sourceTree = ""; }; 302B5850267E720B007133E6 /* ResponseError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResponseError.swift; sourceTree = ""; }; 302BB60F267C658600FD74F5 /* Amaca.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Amaca.plist; sourceTree = ""; }; - 302BB61B267D7CC800FD74F5 /* PreviewPostVIew.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewPostVIew.swift; sourceTree = ""; }; + 302BB61B267D7CC800FD74F5 /* PreviewPostView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewPostView.swift; sourceTree = ""; }; 302BB61D267E316900FD74F5 /* PostInputViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostInputViewController.swift; sourceTree = ""; }; 302BB61E267E316900FD74F5 /* PostInputViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PostInputViewController.xib; sourceTree = ""; }; 302BB621267E38E800FD74F5 /* PostInputViewController+UIImagePickerControllerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PostInputViewController+UIImagePickerControllerDelegate.swift"; sourceTree = ""; }; @@ -125,6 +138,19 @@ 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 = ""; }; + 5416A04826913C65009A3FA9 /* ActivityIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityIndicator.swift; sourceTree = ""; }; + 545F4599269B7E2C007191CC /* Like.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Like.swift; sourceTree = ""; }; + 545F459B269B7E6A007191CC /* LikeService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LikeService.swift; sourceTree = ""; }; + 545F459D269B81CB007191CC /* FeedCollectionViewController+PostCollectionViewCellDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FeedCollectionViewController+PostCollectionViewCellDelegate.swift"; sourceTree = ""; }; + 54A1CC54268F8D7800E2B643 /* CommentViewCellTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentViewCellTableViewCell.swift; sourceTree = ""; }; + 54A1CC56268FA72A00E2B643 /* CommentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentViewController.swift; sourceTree = ""; }; + 54A1CC58268FB44F00E2B643 /* CommentViewControllerDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentViewControllerDataSource.swift; sourceTree = ""; }; + 54A1CC5A268FD21E00E2B643 /* CommentViewControllerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentViewControllerDelegate.swift; sourceTree = ""; }; + 54A1CC5C268FD55900E2B643 /* Comment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Comment.swift; sourceTree = ""; }; + 54A1CC5E268FED3800E2B643 /* CommentService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentService.swift; sourceTree = ""; }; + 54C7AAF12698B7AE00A6340E /* ViewTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewTest.swift; sourceTree = ""; }; + 54C7AAF32698C84400A6340E /* CommentView+UITextFieldDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CommentView+UITextFieldDelegate.swift"; sourceTree = ""; }; + 54C7AAF52698CD2500A6340E /* CommentView+UISwipeGestures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CommentView+UISwipeGestures.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 = ""; }; @@ -173,12 +199,18 @@ 303379582675371C0066D94A /* FeedCollectionViewController+UICollectionViewDataSource .swift */, 3033795A267537490066D94A /* FeedCollectionViewController+UICollectionViewDataSourcePrefetching.swift */, 3033795C267537B40066D94A /* FeedCollectionViewController+UICollectionViewDelegateFlowLayout .swift */, + 545F459D269B81CB007191CC /* FeedCollectionViewController+PostCollectionViewCellDelegate.swift */, 302BB61D267E316900FD74F5 /* PostInputViewController.swift */, 302BB61E267E316900FD74F5 /* PostInputViewController.xib */, 30337954267536980066D94A /* PostInputViewController+CLLocationManagerDelegate.swift */, 302BB621267E38E800FD74F5 /* PostInputViewController+UIImagePickerControllerDelegate.swift */, 302BB623267E3A8700FD74F5 /* PostInputViewController+UIColorPickerViewControllerDelegate.swift */, 302BB625267E447900FD74F5 /* PostInputViewController+UITextFieldDelegate.swift */, + 54A1CC56268FA72A00E2B643 /* CommentViewController.swift */, + 54C7AAF32698C84400A6340E /* CommentView+UITextFieldDelegate.swift */, + 54A1CC58268FB44F00E2B643 /* CommentViewControllerDataSource.swift */, + 54C7AAF52698CD2500A6340E /* CommentView+UISwipeGestures.swift */, + 54A1CC5A268FD21E00E2B643 /* CommentViewControllerDelegate.swift */, ); path = ViewControllers; sourceTree = ""; @@ -200,7 +232,8 @@ 307A305C2661CD510020DF8B /* PostCollectionViewCell.swift */, 307A305D2661CD510020DF8B /* PostCollectionViewCell.xib */, 307A306426629B990020DF8B /* AuthorView.swift */, - 302BB61B267D7CC800FD74F5 /* PreviewPostVIew.swift */, + 302BB61B267D7CC800FD74F5 /* PreviewPostView.swift */, + 54A1CC54268F8D7800E2B643 /* CommentViewCellTableViewCell.swift */, ); path = Views; sourceTree = ""; @@ -222,6 +255,8 @@ 30C77CAF266AD69700A888DC /* CurrentUserService.swift */, 304E06C726742BDA00A99128 /* CreatePostService.swift */, 304E06C926742CC500A99128 /* FeedService.swift */, + 54A1CC5E268FED3800E2B643 /* CommentService.swift */, + 545F459B269B7E6A007191CC /* LikeService.swift */, ); path = Services; sourceTree = ""; @@ -235,6 +270,7 @@ 304E06CB2674442800A99128 /* UIImage+encodeBase64.swift */, 30B9B93A268CA9E6007B1942 /* UIImage+PixelBuffer.swift */, 304E06CE267468DA00A99128 /* UIColor+Pastel.swift */, + 5416A04826913C65009A3FA9 /* ActivityIndicator.swift */, ); path = Extensions; sourceTree = ""; @@ -248,6 +284,8 @@ 307A30572661AD540020DF8B /* User.swift */, 30C77CB3266AF47300A888DC /* Credentials.swift */, 30C77CB5266AF48300A888DC /* CurrentUser.swift */, + 54A1CC5C268FD55900E2B643 /* Comment.swift */, + 545F4599269B7E2C007191CC /* Like.swift */, ); path = Models; sourceTree = ""; @@ -298,6 +336,7 @@ isa = PBXGroup; children = ( E021985D23FA35E20025C28E /* SecretlyTests.swift */, + 54C7AAF12698B7AE00A6340E /* ViewTest.swift */, E021985F23FA35E20025C28E /* Info.plist */, ); path = SecretlyTests; @@ -430,26 +469,34 @@ 302BB622267E38E800FD74F5 /* PostInputViewController+UIImagePickerControllerDelegate.swift in Sources */, 302B5845267E658E007133E6 /* HttpResponse.swift in Sources */, 302B584A267E658E007133E6 /* RestClient.swift in Sources */, + 54A1CC59268FB44F00E2B643 /* CommentViewControllerDataSource.swift in Sources */, 302B5848267E658E007133E6 /* HttpClient.swift in Sources */, 30B9B93D268CB063007B1942 /* NudityChecker.swift in Sources */, + 54C7AAF42698C84400A6340E /* CommentView+UITextFieldDelegate.swift in Sources */, + 545F459A269B7E2C007191CC /* Like.swift in Sources */, 30BC8B9D2662ACBC00F7E6A5 /* ImageLoader.swift in Sources */, + 54A1CC57268FA72A00E2B643 /* CommentViewController.swift in Sources */, 302B5859267E720B007133E6 /* ResponseError.swift in Sources */, 307A30622661E8A90020DF8B /* UIColor+Hex.swift in Sources */, 307A30542661ABD60020DF8B /* Post.swift in Sources */, 304E06C626741A5100A99128 /* UIColor+theme.swift in Sources */, 304E06C826742BDA00A99128 /* CreatePostService.swift in Sources */, E021984723FA35E00025C28E /* AppDelegate.swift in Sources */, + 54A1CC55268F8D7800E2B643 /* CommentViewCellTableViewCell.swift in Sources */, 30337955267536980066D94A /* PostInputViewController+CLLocationManagerDelegate.swift in Sources */, 307A30562661AD480020DF8B /* Image.swift in Sources */, + 54A1CC5F268FED3800E2B643 /* CommentService.swift in Sources */, 307A30582661AD540020DF8B /* User.swift in Sources */, 302B5849267E658E007133E6 /* RequestError.swift in Sources */, 302B5846267E658E007133E6 /* AmacaConfig.swift in Sources */, - 302BB61C267D7CC800FD74F5 /* PreviewPostVIew.swift in Sources */, + 54A1CC5D268FD55900E2B643 /* Comment.swift in Sources */, + 302BB61C267D7CC800FD74F5 /* PreviewPostView.swift in Sources */, 3033795D267537B40066D94A /* FeedCollectionViewController+UICollectionViewDelegateFlowLayout .swift in Sources */, 30BC8BA82662CEBA00F7E6A5 /* Checksum.swift in Sources */, 30FD0E722659645A006E309A /* Faker.swift in Sources */, 30B9B939268CA989007B1942 /* Nudity.mlmodel in Sources */, 30337957267536E30066D94A /* FeedCollectionViewController+UICollectionViewDelegate.swift in Sources */, + 545F459E269B81CB007191CC /* FeedCollectionViewController+PostCollectionViewCellDelegate.swift in Sources */, 307A305E2661CD510020DF8B /* PostCollectionViewCell.swift in Sources */, 302BB626267E447900FD74F5 /* PostInputViewController+UITextFieldDelegate.swift in Sources */, 30BC8BA02662B8A700F7E6A5 /* StorageType.swift in Sources */, @@ -459,18 +506,22 @@ 302B5847267E658E007133E6 /* StatusCode.swift in Sources */, 302B584B267E658E007133E6 /* RequestBuilder.swift in Sources */, 307A306526629B990020DF8B /* AuthorView.swift in Sources */, + 5416A04926913C65009A3FA9 /* ActivityIndicator.swift in Sources */, 30C77CB4266AF47300A888DC /* Credentials.swift in Sources */, 30BC8BA22662BB0000F7E6A5 /* DataContainer.swift in Sources */, E021984923FA35E00025C28E /* SceneDelegate.swift in Sources */, + 545F459C269B7E6A007191CC /* LikeService.swift in Sources */, 30C77CB0266AD69700A888DC /* CurrentUserService.swift in Sources */, 30C77CB6266AF48300A888DC /* CurrentUser.swift in Sources */, 302BB624267E3A8700FD74F5 /* PostInputViewController+UIColorPickerViewControllerDelegate.swift in Sources */, 304E06CF267468DA00A99128 /* UIColor+Pastel.swift in Sources */, 304E06C42674133D00A99128 /* String+isBlank.swift in Sources */, 30BC8BA42662BDEF00F7E6A5 /* ImageStore.swift in Sources */, + 54A1CC5B268FD21E00E2B643 /* CommentViewControllerDelegate.swift in Sources */, 3033795B267537490066D94A /* FeedCollectionViewController+UICollectionViewDataSourcePrefetching.swift in Sources */, 30B9B93B268CA9E6007B1942 /* UIImage+PixelBuffer.swift in Sources */, 303379592675371C0066D94A /* FeedCollectionViewController+UICollectionViewDataSource .swift in Sources */, + 54C7AAF62698CD2500A6340E /* CommentView+UISwipeGestures.swift in Sources */, 304E06CA26742CC500A99128 /* FeedService.swift in Sources */, 302BB61F267E316900FD74F5 /* PostInputViewController.swift in Sources */, ); @@ -480,6 +531,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 54C7AAF22698B7AE00A6340E /* ViewTest.swift in Sources */, E021985E23FA35E20025C28E /* SecretlyTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -635,13 +687,13 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = D3XL2U7DQC; + DEVELOPMENT_TEAM = 9DD36VF5BZ; INFOPLIST_FILE = Secretly/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = mx.unam.ioslab.Secretly; + PRODUCT_BUNDLE_IDENTIFIER = samart.Secretly; PRODUCT_NAME = Secretly; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; @@ -653,13 +705,13 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = D3XL2U7DQC; + DEVELOPMENT_TEAM = 9DD36VF5BZ; INFOPLIST_FILE = Secretly/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = mx.unam.ioslab.Secretly; + PRODUCT_BUNDLE_IDENTIFIER = samart.Secretly; PRODUCT_NAME = Secretly; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; diff --git a/Secretly/Base.lproj/Main.storyboard b/Secretly/Base.lproj/Main.storyboard index 4ed5b13..79cbd9a 100644 --- a/Secretly/Base.lproj/Main.storyboard +++ b/Secretly/Base.lproj/Main.storyboard @@ -69,6 +69,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -87,7 +159,7 @@ - + @@ -101,10 +173,10 @@ - - - - + + + + @@ -121,6 +193,7 @@ + @@ -170,6 +243,7 @@ + diff --git a/Secretly/Extensions/ActivityIndicator.swift b/Secretly/Extensions/ActivityIndicator.swift new file mode 100644 index 0000000..d5655f7 --- /dev/null +++ b/Secretly/Extensions/ActivityIndicator.swift @@ -0,0 +1,9 @@ +// +// ActivityIndicator.swift +// Secretly +// +// Created by Samuel Arturo Garrido Sánchez on 2021-07-03. +// Copyright © 2021 3zcurdia. All rights reserved. +// + +import Foundation diff --git a/Secretly/Info.plist b/Secretly/Info.plist index 154ea82..51ca4f3 100644 --- a/Secretly/Info.plist +++ b/Secretly/Info.plist @@ -2,12 +2,6 @@ - NSPhotoLibraryUsageDescription - To add some context to your posts - NSCameraUsageDescription - To add some context to your posts - NSLocationWhenInUseUsageDescription - To georeference posts within a region CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable @@ -26,6 +20,12 @@ 1 LSRequiresIPhoneOS + NSCameraUsageDescription + To add some context to your posts + NSLocationWhenInUseUsageDescription + To georeference posts within a region + NSPhotoLibraryUsageDescription + To add some context to your posts UIApplicationSceneManifest UIApplicationSupportsMultipleScenes diff --git a/Secretly/Models/Comment.swift b/Secretly/Models/Comment.swift new file mode 100644 index 0000000..37d9175 --- /dev/null +++ b/Secretly/Models/Comment.swift @@ -0,0 +1,30 @@ +// +// Comment.swift +// Secretly +// +// Created by Samuel Arturo Garrido Sánchez on 2021-07-02. +// Copyright © 2021 3zcurdia. All rights reserved. +// + +import UIKit + +struct Comment: Restable{ + var id: Int? + var autor:User? + var createdAt:Date? + var uptadedAt:Date? + var content:String + // autor: User? = nil, createdAt: Date? = nil, uptadedAt: Date? = nil + init(content: String){ + self.id = nil + self.autor = nil + self.createdAt = nil + self.uptadedAt = nil + self.content = content + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(content, forKey: .content) + } +} diff --git a/Secretly/Models/Like.swift b/Secretly/Models/Like.swift new file mode 100644 index 0000000..fc357bd --- /dev/null +++ b/Secretly/Models/Like.swift @@ -0,0 +1,26 @@ +// +// Like.swift +// Secretly +// +// Created by Samuel Arturo Garrido Sánchez on 2021-07-11. +// Copyright © 2021 3zcurdia. All rights reserved. +// + +import Foundation + +struct Like: Restable { + let id: Int? + let createdAt: Date? + let updatedAt: Date? + + init(){ + self.id = nil + self.createdAt = nil + self.updatedAt = nil + } + + 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..3b358bd 100644 --- a/Secretly/Models/Post.swift +++ b/Secretly/Models/Post.swift @@ -20,7 +20,9 @@ struct Post: Restable { let latitude: Double? let longitude: Double? let createdAt: Date? - let updatedAt: Date? + var updatedAt: Date? + var liked: Bool + var numLikes: Int? init(content: String, backgroundColor: String, latitude: Double? = nil, longitude: Double? = nil, image: UIImage? = nil) { self.content = content @@ -34,6 +36,9 @@ struct Post: Restable { self.commentsCount = nil self.createdAt = nil self.updatedAt = nil + self.liked = false + self.numLikes = 0 + } func encode(to encoder: Encoder) throws { diff --git a/Secretly/Services/CommentService.swift b/Secretly/Services/CommentService.swift new file mode 100644 index 0000000..432263f --- /dev/null +++ b/Secretly/Services/CommentService.swift @@ -0,0 +1,74 @@ +// +// CommentService.swift +// Secretly +// +// Created by Samuel Arturo Garrido Sánchez on 2021-07-02. +// Copyright © 2021 3zcurdia. All rights reserved. +// + +import Foundation + +struct CommentService { + private var endpoint: RestClient + private var post: Post? + + init(post: Post) { + endpoint = RestClient(client: AmacaConfig.shared.httpClient, path: "/api/v1/posts/\(post.id ?? 1)/comments") + self.post = post + } + + func create(_ model: Comment, complete: @escaping (Result) -> Void ) { + try? endpoint.create(model: model) { result in + DispatchQueue.main.async { complete(result) } + } + } + + func delete(_ model: Comment, complete: @escaping (Result) -> Void ) { + let deleteEndpoint = RestClient(client: AmacaConfig.shared.httpClient, path: "/api/v1/posts/\(post?.id ?? 1)/comments/\(model.id ?? -1)") + print(deleteEndpoint.path) + deleteEndpoint.delete(model: model, complete: { result in + DispatchQueue.main.async { + complete(result) + } + }) + } + + func update(_ model: Comment, complete: @escaping (Result) -> Void ) { + let updateEndpoint = RestClient(client: AmacaConfig.shared.httpClient, path: "/api/v1/posts/\(post?.id ?? 1)/comments/\(model.id ?? -1)") + print(updateEndpoint.path) + + try? updateEndpoint.update(model: model){ result in + DispatchQueue.main.async { + complete(result) + } + } + } + + + func load(completion: @escaping ([Comment]) -> Void) { + endpoint.list { result in + guard let posts = try? result.get() else { return } + DispatchQueue.main.async { completion(posts) } + } + } + + static func count(post:Post,completion: @escaping ([Comment]) -> Void){ + let endPoint = RestClient(client: AmacaConfig.shared.httpClient, path: "/api/v1/posts/\(post.id ?? 1)/comments") + endPoint.list { result in + guard let posts = try? result.get() else { return } + DispatchQueue.main.async { + completion(posts) + } + } + } + + static func countByID(id:Int,completion: @escaping ([Comment]) -> Void){ + let endPoint = RestClient(client: AmacaConfig.shared.httpClient, path: "/api/v1/posts/\(id)/comments") + endPoint.list { result in + guard let posts = try? result.get() else { return } + DispatchQueue.main.async { + completion(posts) + } + } + } +} diff --git a/Secretly/Services/LikeService.swift b/Secretly/Services/LikeService.swift new file mode 100644 index 0000000..bf14c00 --- /dev/null +++ b/Secretly/Services/LikeService.swift @@ -0,0 +1,30 @@ +// +// LikeService.swift +// Secretly +// +// Created by Samuel Arturo Garrido Sánchez on 2021-07-11. +// Copyright © 2021 3zcurdia. All rights reserved. +// + +import UIKit + +struct LikeService { + private var endpoint: RestClient + + init(post: Post) { + self.endpoint = RestClient(client: AmacaConfig.shared.httpClient, path: "/api/v1/posts/\(post.id ?? 1)/likes") + } + + func create(_ model: Like, complete: @escaping (Result) -> Void ) { + try? endpoint.create(model: model) { result in + DispatchQueue.main.async { complete(result) } + } + } + + func delete(_ model: Like, complete: @escaping (Result) -> Void) { + endpoint.delete(model: model) { result in + DispatchQueue.main.async { complete(result) } + } + } + +} diff --git a/Secretly/ViewControllers/CommentView+UISwipeGestures.swift b/Secretly/ViewControllers/CommentView+UISwipeGestures.swift new file mode 100644 index 0000000..4e299d2 --- /dev/null +++ b/Secretly/ViewControllers/CommentView+UISwipeGestures.swift @@ -0,0 +1,58 @@ +// +// CommentView+UISwipeGestures.swift +// Secretly +// +// Created by Samuel Arturo Garrido Sánchez on 2021-07-09. +// Copyright © 2021 3zcurdia. All rights reserved. +// + +import UIKit + +extension CommentViewController{ + +func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { + let editAction = UIContextualAction(style: .normal, title: "Editar") { [unowned self] (action, view, completion) in + + let cell = tableView.cellForRow(at: indexPath) as? CommentViewCellTableViewCell + + isEditingComment = true + commentEditing = cell?.comment + + tableView.selectRow(at: indexPath, animated: true, scrollPosition: .top) + commentTextField.becomeFirstResponder() + commentTextField.text = cell?.comment?.content + tableView.scrollToRow(at: indexPath, at: .top, animated: true) + completion(isEditingComment) + } + editAction.backgroundColor = .systemYellow + return UISwipeActionsConfiguration(actions: [editAction]) +} + +func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { + + let deleteAction = UIContextualAction(style: .destructive, title: "Delete") { [unowned self](action, view, completion) in + + let cell = tableView.cellForRow(at: indexPath) as? CommentViewCellTableViewCell + if let unwrapComment = cell?.comment{ + print(unwrapComment) + self.commentService?.delete(unwrapComment, complete: { [unowned self] result in + switch result { + case .success(let comment): + self.showAlert(title: "Comentario Eliminado", message: "\(comment?.id ?? 0)") + tableView.beginUpdates() + self.comments?.remove(at: indexPath.row) + self.tableView.deleteRows(at: [indexPath], with: .none) + tableView.endUpdates() + completion(true) + case .failure(let err): + self.errorAlert(err) + completion(false) + } + }) + } + completion(true) + } + deleteAction.backgroundColor = .red + return UISwipeActionsConfiguration(actions: [deleteAction]) +} +} diff --git a/Secretly/ViewControllers/CommentView+UITextFieldDelegate.swift b/Secretly/ViewControllers/CommentView+UITextFieldDelegate.swift new file mode 100644 index 0000000..87cebb7 --- /dev/null +++ b/Secretly/ViewControllers/CommentView+UITextFieldDelegate.swift @@ -0,0 +1,56 @@ +// +// CommentView+UITextFieldDelegate.swift +// Secretly +// +// Created by Samuel Arturo Garrido Sánchez on 2021-07-09. +// Copyright © 2021 3zcurdia. All rights reserved. +// + +import UIKit + +extension CommentViewController: UITextFieldDelegate{ + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + self.commentTextField.text = "" + self.commentTextField.endEditing(true) + } + + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + textField.resignFirstResponder() + print("Tecla return presionada") + return true + } +} + + +extension CommentViewController{ + func adjustInputText() { + NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil) + } + + @objc func keyboardWillShow(notification: NSNotification) { + if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue { + if self.view.frame.origin.y == 0 { + tableView.contentInset = UIEdgeInsets(top: keyboardSize.height - view.safeAreaInsets.top, left: 0, bottom: 0, right: 0) + self.view.frame.origin.y -= keyboardSize.height - view.safeAreaInsets.bottom + + } + } + } + + @objc func keyboardWillHide(notification: NSNotification) { + if self.view.frame.origin.y != 0 { + self.view.frame.origin.y = 0 + tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) + + } + } + + func showAlert(title: String, message: String){ + let alertVC = UIAlertController(title: title, message: message, preferredStyle: .alert) + let okAction = UIAlertAction(title: "OK", style: .default, handler: nil) + alertVC.addAction(okAction) + present(alertVC, animated: true, completion: nil) + } + +} diff --git a/Secretly/ViewControllers/CommentViewController.swift b/Secretly/ViewControllers/CommentViewController.swift new file mode 100644 index 0000000..b414674 --- /dev/null +++ b/Secretly/ViewControllers/CommentViewController.swift @@ -0,0 +1,143 @@ +// +// CommentViewController.swift +// Secretly +// +// Created by Samuel Arturo Garrido Sánchez on 2021-07-02. +// Copyright © 2021 3zcurdia. All rights reserved. +// + +import UIKit + +// MARK: TODO: +/* - Better organize methods in extensions + - Activity indicator, extension to VC, program fetch times + - Get CurrentUser to User(username,avatarUrl) +*/ +class CommentViewController: UIViewController{ + + var commentService:CommentService? = nil + var isEditingComment: Bool = false + + var commentEditing: Comment? + + var currentUserService:CurrentUserService? = nil + let fakeEndpoint = RestClient(client: AmacaConfig.shared.httpClient, path: "/api/v1/fake") + + @IBOutlet var CommentToolBar: UIToolbar! + @IBOutlet var tableView: UITableView! + @IBOutlet var commentTextField: UITextField! + + var activeUser:String? + + var post:Post? { + didSet{ + guard let postito = post else { return } + commentService = CommentService(post: postito) + reloadComments() + } + } + + func reloadComments(){ + commentService?.load { [unowned self] comments in + self.comments = comments + comments.forEach({ comn in + print(comn.autor) //CREO QUE EL BACK no está reconociento user en comments + }) + } + } + + public func clear() { + commentTextField?.text = "" + } + + var comments:[Comment]? { + didSet { + self.tableView.reloadData() + } + } + + override func viewDidLoad() { + super.viewDidLoad() + adjustInputText() + tableView.delegate = self + tableView.dataSource = self + commentTextField.delegate = self + } + + + @IBAction func didTapSendComment(_ sender: Any) { + do { + if isEditingComment{ + try self.updateComment() + }else{ + try self.createComment() + } + } catch let err { + self.errorAlert(err) + } + self.commentTextField.endEditing(true) + } + + func errorAlert(_ error: Error) { + let err = error as? Titleable + let alert = UIAlertController(title: (err?.title ?? "Error Servidor"), message: error.localizedDescription, preferredStyle: .alert) + let okAction = UIAlertAction(title: "OK", style: .default) + alert.addAction(okAction) + self.present(alert, animated: true, completion: nil) + } + + private func createComment() throws { + let comment = try self.buildComment() + guard let commmentM = comment else { return } + + commentService?.create(commmentM) { [unowned self] result in + switch result { + case .success(let commmentM): + self.showAlert(title: "Comentario publicado", message: commmentM?.content ?? "") + self.reloadComments() + case .failure(let err): + self.errorAlert(err) + } + } + } + + private func updateComment() throws { + if var commentEditing = commentEditing { + commentEditing.content = commentTextField.text ?? "" + self.commentService?.update(commentEditing, complete: { [unowned self] result in + switch result { + case .success(let comment): + self.showAlert(title: "Comentario actualizado", message: "\(comment?.content ?? "")") + isEditingComment = false + self.reloadComments() + case .failure(let err): + isEditingComment = false + self.errorAlert(err) + } + }) + } + + } + + private func buildComment() throws -> Comment? { + if let commentText = commentTextField.text{ + print(CurrentUser.load()?.username ?? "No hay current user") + var comment = Comment(content: commentText) + let autor = User(username: CurrentUser.load()?.username ?? "", avatarUrl: "") + comment.autor = autor + return comment + } else{ + let alert = UIAlertController(title: "Sin comentarios", message: "Llena por favor el comentario", preferredStyle: .alert) + let okAction = UIAlertAction(title: "OK", style: .default) + alert.addAction(okAction) + self.present(alert, animated: true, completion: nil) + return nil + } + } + + override func viewWillDisappear(_ animated: Bool) { + NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: self.view.window) + NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: self.view.window) + } +} + diff --git a/Secretly/ViewControllers/CommentViewControllerDataSource.swift b/Secretly/ViewControllers/CommentViewControllerDataSource.swift new file mode 100644 index 0000000..e3d91ed --- /dev/null +++ b/Secretly/ViewControllers/CommentViewControllerDataSource.swift @@ -0,0 +1,24 @@ +// +// CommentViewControllerDataSource.swift +// Secretly +// +// Created by Samuel Arturo Garrido Sánchez on 2021-07-02. +// Copyright © 2021 3zcurdia. All rights reserved. +// + +import UIKit + +extension CommentViewController: UITableViewDataSource { + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return comments?.count ?? 0 + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "commentCell") as! CommentViewCellTableViewCell + cell.comment = comments?[indexPath.row] + return cell + } + + +} diff --git a/Secretly/ViewControllers/CommentViewControllerDelegate.swift b/Secretly/ViewControllers/CommentViewControllerDelegate.swift new file mode 100644 index 0000000..3634429 --- /dev/null +++ b/Secretly/ViewControllers/CommentViewControllerDelegate.swift @@ -0,0 +1,25 @@ +// +// CommentViewControllerDelegate.swift +// Secretly +// +// Created by Samuel Arturo Garrido Sánchez on 2021-07-02. +// Copyright © 2021 3zcurdia. All rights reserved. +// + +import UIKit + +extension CommentViewController: UITableViewDelegate { + + func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { + return true + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + self.commentTextField.endEditing(true) + tableView.deselectRow(at: indexPath, animated: true) + } + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return 100.0; + } +} diff --git a/Secretly/ViewControllers/FeedCollectionViewController+PostCollectionViewCellDelegate.swift b/Secretly/ViewControllers/FeedCollectionViewController+PostCollectionViewCellDelegate.swift new file mode 100644 index 0000000..775e3e5 --- /dev/null +++ b/Secretly/ViewControllers/FeedCollectionViewController+PostCollectionViewCellDelegate.swift @@ -0,0 +1,18 @@ +// +// FeedCollectionViewController+PostCollectionViewCellDelegate.swift +// Secretly +// +// Created by Samuel Arturo Garrido Sánchez on 2021-07-11. +// Copyright © 2021 3zcurdia. All rights reserved. +// + +import Foundation + +extension FeedCollectionViewController: PostCollecionViewCellDelegate{ + + + func goComment(post: Post) { + performSegue(withIdentifier: "commentSegue", sender: post) + } + +} diff --git a/Secretly/ViewControllers/FeedCollectionViewController+UICollectionViewDataSource .swift b/Secretly/ViewControllers/FeedCollectionViewController+UICollectionViewDataSource .swift index 15100dd..902c72b 100644 --- a/Secretly/ViewControllers/FeedCollectionViewController+UICollectionViewDataSource .swift +++ b/Secretly/ViewControllers/FeedCollectionViewController+UICollectionViewDataSource .swift @@ -19,8 +19,14 @@ extension FeedCollectionViewController: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PostCollectionViewCell.reuseIdentifier, for: indexPath) as! PostCollectionViewCell - + cell.delegate = self cell.post = self.posts?[indexPath.row] + + CommentService.count(post: self.posts![indexPath.row], completion: {comments in + cell.updateCountedLikes(numLikes: comments.count) + }) return cell } + + } diff --git a/Secretly/ViewControllers/FeedCollectionViewController+UICollectionViewDelegate.swift b/Secretly/ViewControllers/FeedCollectionViewController+UICollectionViewDelegate.swift index 2b373e7..d26a371 100644 --- a/Secretly/ViewControllers/FeedCollectionViewController+UICollectionViewDelegate.swift +++ b/Secretly/ViewControllers/FeedCollectionViewController+UICollectionViewDelegate.swift @@ -16,12 +16,10 @@ extension FeedCollectionViewController: UICollectionViewDelegate { } */ - /* - // Uncomment this method to specify if the specified item should be selected - override func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool { - return true - } - */ + +// func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool { +// return true +// } /* // Uncomment these methods to specify if an action menu should be displayed for the specified item, and react to actions performed on the item @@ -37,4 +35,16 @@ extension FeedCollectionViewController: UICollectionViewDelegate { } */ + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + //TODO: TAP To Like + } + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + if segue.identifier == "commentSegue"{ + let vc = segue.destination as! CommentViewController + vc.post = sender as? Post + } + } + } diff --git a/Secretly/ViewControllers/FeedCollectionViewController+UICollectionViewDelegateFlowLayout .swift b/Secretly/ViewControllers/FeedCollectionViewController+UICollectionViewDelegateFlowLayout .swift index e6c7ccc..bbbd939 100644 --- a/Secretly/ViewControllers/FeedCollectionViewController+UICollectionViewDelegateFlowLayout .swift +++ b/Secretly/ViewControllers/FeedCollectionViewController+UICollectionViewDelegateFlowLayout .swift @@ -10,6 +10,13 @@ import UIKit extension FeedCollectionViewController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { - return CGSize(width: collectionView.bounds.width, height: 300) + return CGSize(width: view.frame.width, height: 300) + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { + return 0.0 + } + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { + return 1.0 } } diff --git a/Secretly/ViewControllers/FeedCollectionViewController.swift b/Secretly/ViewControllers/FeedCollectionViewController.swift index 9180d42..99219fc 100644 --- a/Secretly/ViewControllers/FeedCollectionViewController.swift +++ b/Secretly/ViewControllers/FeedCollectionViewController.swift @@ -10,7 +10,10 @@ import UIKit import CoreLocation class FeedCollectionViewController: UIViewController { + let feedService = FeedService() + var commentService:CommentService? + var posts: [Post]? { didSet { self.collectionView.reloadData() @@ -51,6 +54,7 @@ class FeedCollectionViewController: UIViewController { postInputView.clear() present(postInputView, animated: true) } + } extension FeedCollectionViewController: PostInputViewDelegate { diff --git a/Secretly/ViewControllers/PostInputViewController.swift b/Secretly/ViewControllers/PostInputViewController.swift index 676ce35..4b3cb08 100644 --- a/Secretly/ViewControllers/PostInputViewController.swift +++ b/Secretly/ViewControllers/PostInputViewController.swift @@ -163,3 +163,4 @@ class PostInputViewController: UIViewController, UINavigationControllerDelegate previewPost?.clear() } } + diff --git a/Secretly/Views/AuthorView.swift b/Secretly/Views/AuthorView.swift index 9d32ae3..3d3590c 100644 --- a/Secretly/Views/AuthorView.swift +++ b/Secretly/Views/AuthorView.swift @@ -8,6 +8,8 @@ import UIKit + +@IBDesignable class AuthorView: UIView { var author: User? { didSet { @@ -57,14 +59,6 @@ class AuthorView: UIView { setupConstraints() } - /* - // Only override draw() if you perform custom drawing. - // An empty implementation adversely affects performance during animation. - override func draw(_ rect: CGRect) { - // Drawing code - } - */ - func setupConstraints() { self.backgroundColor = .clear addSubview(stack) diff --git a/Secretly/Views/CommentViewCellTableViewCell.swift b/Secretly/Views/CommentViewCellTableViewCell.swift new file mode 100644 index 0000000..9a1765d --- /dev/null +++ b/Secretly/Views/CommentViewCellTableViewCell.swift @@ -0,0 +1,98 @@ +// +// CommentViewCellTableViewCell.swift +// Secretly +// +// Created by Samuel Arturo Garrido Sánchez on 2021-07-02. +// Copyright © 2021 3zcurdia. All rights reserved. +// + +import UIKit + +class CommentViewCellTableViewCell: UITableViewCell { + + var comment: Comment? { + didSet{ + guard let unwrapComment = comment else { return } + usernameLabel.text = unwrapComment.autor?.username ?? "Anonymous" + commentLabel.text = unwrapComment.content + if let unwrapImage = unwrapComment.autor?.avatarUrl{ + ImageLoader.load(unwrapImage) { [unowned self] img in self.imageUser.image = img } + }else{ + self.imageUser.image = UIImage(named: "animated") + } + activateConstraints() + } + } + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + } + + private let imageUser:UIImageView = { + let imageView = UIImageView() + imageView.contentMode = .scaleAspectFit + imageView.translatesAutoresizingMaskIntoConstraints = false + imageView.layer.cornerRadius = 13 + imageView.clipsToBounds = true + return imageView + }() + + private let usernameLabel:UILabel = { + let label = UILabel() + label.contentMode = .scaleAspectFit + label.translatesAutoresizingMaskIntoConstraints = false + label.font = .boldSystemFont(ofSize: 18) + label.sizeToFit() + label.numberOfLines = 1 + return label + }() + + private let commentLabel:UILabel = { + let label = UILabel() + label.contentMode = .scaleAspectFit + label.translatesAutoresizingMaskIntoConstraints = false + label.lineBreakMode = .byWordWrapping + label.numberOfLines = 0 + return label + }() + + private let horizontalStack: UIStackView = { + let stack = UIStackView() + stack.distribution = .fill + stack.translatesAutoresizingMaskIntoConstraints = false + stack.alignment = .fill + stack.axis = .horizontal + stack.spacing = 10 + return stack + }() + + private let verticalStack: UIStackView = { + let stack = UIStackView() + stack.distribution = .fillProportionally + stack.axis = .vertical + stack.translatesAutoresizingMaskIntoConstraints = false + stack.alignment = .leading + return stack + }() + + private func activateConstraints(){ + addSubview(verticalStack) + verticalStack.addArrangedSubview(horizontalStack) + horizontalStack.addArrangedSubview(imageUser) + horizontalStack.addArrangedSubview(usernameLabel) + verticalStack.addArrangedSubview(commentLabel) + + NSLayoutConstraint.activate([ + verticalStack.topAnchor.constraint(equalTo: topAnchor, constant: 10), + verticalStack.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10), + verticalStack.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10), + verticalStack.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -10), + imageUser.widthAnchor.constraint(equalToConstant: 25), + imageUser.heightAnchor.constraint(equalToConstant: 25), + ]) + } +} diff --git a/Secretly/Views/PostCollectionViewCell.swift b/Secretly/Views/PostCollectionViewCell.swift index ee08d53..de250e5 100644 --- a/Secretly/Views/PostCollectionViewCell.swift +++ b/Secretly/Views/PostCollectionViewCell.swift @@ -8,34 +8,74 @@ import UIKit +protocol PostCollecionViewCellDelegate: AnyObject{ + func goComment(post: Post) +} + class PostCollectionViewCell: UICollectionViewCell { static let reuseIdentifier = "feedPostCell" + + weak var delegate:PostCollecionViewCellDelegate? + var likeService: LikeService? + var post: Post? { didSet { updateView() } } + @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 var commentButton: UIButton! + @IBOutlet var numLikes: UILabel! + + @IBAction func onCommentPressed(_ sender: UIButton) { + guard let unwrapPost = post else { return } + delegate?.goComment(post: unwrapPost) + } + override func awakeFromNib() { super.awakeFromNib() + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(setLike)) + tapGesture.numberOfTapsRequired = 2 + self.imageView.addGestureRecognizer(tapGesture) } + + func updateCountedLikes(numLikes:Int){ + commentButton.setTitle("\(numLikes)", for: .normal) + } + func updateView() { - imageView.image = nil guard let post = post else { return } if let color = UIColor(hex: post.backgroundColor) { - self.backgroundColor = color + self.imageView.backgroundColor = color } self.contentLabel.text = post.content - self.commentCounter.text = String(describing: post.commentsCount ?? 0) if let postImg = post.image { ImageLoader.load(postImg.mediumUrl) { img in self.imageView.image = img } } self.authorView.author = post.user + + } + + @objc func setLike(){ + self.numLikes.text = "\(post?.numLikes ?? 0 + 1)" + self.likeState.tintColor = .red + likeService?.create(Like()) { [unowned self] result in + switch result{ + case .success(_): + self.numLikes.text = "\(post?.numLikes ?? 0 + 1)" + self.likeState.tintColor = .red + break + + case .failure(let error): + print(error) + + } + } } } diff --git a/Secretly/Views/PostCollectionViewCell.xib b/Secretly/Views/PostCollectionViewCell.xib index 7b3a4e2..2a4e842 100644 --- a/Secretly/Views/PostCollectionViewCell.xib +++ b/Secretly/Views/PostCollectionViewCell.xib @@ -1,6 +1,6 @@ - + @@ -10,88 +10,93 @@ - + - + - + - - + + - - + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + - + + - + diff --git a/Secretly/Views/PreviewPostVIew.swift b/Secretly/Views/PreviewPostView.swift similarity index 100% rename from Secretly/Views/PreviewPostVIew.swift rename to Secretly/Views/PreviewPostView.swift diff --git a/SecretlyTests/SecretlyTests.swift b/SecretlyTests/SecretlyTests.swift index 0c827a6..eafebc8 100644 --- a/SecretlyTests/SecretlyTests.swift +++ b/SecretlyTests/SecretlyTests.swift @@ -7,10 +7,26 @@ // import XCTest +import UIKit @testable import Secretly class SecretlyTests: XCTestCase { - func testExample() { - XCTAssert(true) + + + func checkUserLoged() { + let currentUser = CurrentUser.load()?.username + let username = UserDefaults.standard.string(forKey: "secretly.username") + XCTAssertEqual(currentUser, username) + } + + func testCreateLike() { + let createLike = CommentService(post: Post(content: "Hola", backgroundColor: "#FFFFFF")) + XCTAssertNoThrow(createLike) + } + + func checkSumComments(){ + CommentService.countByID(id: 78) { comments in + XCTAssertEqual(comments.count, 13) + } } } diff --git a/SecretlyTests/ViewTest.swift b/SecretlyTests/ViewTest.swift new file mode 100644 index 0000000..fa9c9c8 --- /dev/null +++ b/SecretlyTests/ViewTest.swift @@ -0,0 +1,34 @@ +// +// ViewTest.swift +// SecretlyTests +// +// Created by Samuel Arturo Garrido Sánchez on 2021-07-09. +// Copyright © 2021 3zcurdia. All rights reserved. +// + +import XCTest + +class ViewTest: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. + XCUIApplication().launch() + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // Use recording to get started writing UI tests. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + +}