From cf97b1cd43a91725e0419a953815287fde0bf70f Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Thu, 17 Sep 2020 00:46:27 +0300 Subject: SwiftUI WIP --- Juick.xcodeproj/project.pbxproj | 165 +++++++++++++++++++++---- Juick/AppDelegate.swift | 125 +++++++++++++++++++ Juick/Helpers/Data+Hex.swift | 16 +++ Juick/Helpers/LoadableState.swift | 25 ++++ Juick/Helpers/NSAttributedString_Entities.h | 19 +++ Juick/Helpers/NSAttributedString_Entities.m | 90 ++++++++++++++ Juick/ImageFetcher.swift | 28 +++++ Juick/Main.storyboard | 16 +-- Juick/MessageFetcher.swift | 46 +++++++ Juick/Model/Attachment.m | 29 +++-- Juick/Model/Message.h | 12 +- Juick/Model/Message.m | 2 +- Juick/Model/User.h | 1 + Juick/SceneDelegate.swift | 63 ++++++++++ Juick/Supporting Files/Juick-Bridging-Header.h | 3 + Juick/Supporting Files/Juick-Info.plist | 17 +++ Juick/Supporting Files/main.m | 16 --- Juick/Views/ActivityIndicator.swift | 28 +++++ Juick/Views/AttributedLabelView.swift | 107 ++++++++++++++++ Juick/Views/ContentView.swift | 47 +++++++ Juick/Views/FeedView.swift | 66 ++++++++++ Juick/Views/LoadableImageView.swift | 35 ++++++ Juick/Views/MessageView.swift | 51 ++++++++ 23 files changed, 939 insertions(+), 68 deletions(-) create mode 100644 Juick/AppDelegate.swift create mode 100644 Juick/Helpers/Data+Hex.swift create mode 100644 Juick/Helpers/LoadableState.swift create mode 100644 Juick/Helpers/NSAttributedString_Entities.h create mode 100644 Juick/Helpers/NSAttributedString_Entities.m create mode 100644 Juick/ImageFetcher.swift create mode 100644 Juick/MessageFetcher.swift create mode 100644 Juick/SceneDelegate.swift delete mode 100644 Juick/Supporting Files/main.m create mode 100644 Juick/Views/ActivityIndicator.swift create mode 100644 Juick/Views/AttributedLabelView.swift create mode 100644 Juick/Views/ContentView.swift create mode 100644 Juick/Views/FeedView.swift create mode 100644 Juick/Views/LoadableImageView.swift create mode 100644 Juick/Views/MessageView.swift diff --git a/Juick.xcodeproj/project.pbxproj b/Juick.xcodeproj/project.pbxproj index 8ab2162..619ef46 100644 --- a/Juick.xcodeproj/project.pbxproj +++ b/Juick.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 52; objects = { /* Begin PBXBuildFile section */ @@ -14,7 +14,6 @@ 77317BAE181BBE8500D60005 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77317BAD181BBE8500D60005 /* CoreGraphics.framework */; }; 77317BB0181BBE8500D60005 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77317BAF181BBE8500D60005 /* UIKit.framework */; }; 77317BB6181BBE8500D60005 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 77317BB4181BBE8500D60005 /* InfoPlist.strings */; }; - 77317BB8181BBE8500D60005 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 77317BB7181BBE8500D60005 /* main.m */; }; 77317BBC181BBE8500D60005 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 77317BBB181BBE8500D60005 /* AppDelegate.m */; }; 77317BC2181BBE8500D60005 /* MessagesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 77317BC1181BBE8500D60005 /* MessagesViewController.m */; }; 77317BC7181BBE8500D60005 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 77317BC6181BBE8500D60005 /* Images.xcassets */; }; @@ -36,6 +35,8 @@ 774746AD239F82A10001C7F9 /* NSDate+TimeAgo.m in Sources */ = {isa = PBXBuildFile; fileRef = 774746AC239F82A10001C7F9 /* NSDate+TimeAgo.m */; }; 774746B6239F872A0001C7F9 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 774746B5239F872A0001C7F9 /* CoreServices.framework */; }; 774C98CD25126C070073C70A /* Service.swift in Sources */ = {isa = PBXBuildFile; fileRef = 774C98CC25126C070073C70A /* Service.swift */; }; + 774E6B52251AB5A4006B5D5F /* AttributedLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 774E6B51251AB5A4006B5D5F /* AttributedLabelView.swift */; }; + 774E6B64251AC4D2006B5D5F /* NSAttributedString_Entities.m in Sources */ = {isa = PBXBuildFile; fileRef = 774E6B57251AB743006B5D5F /* NSAttributedString_Entities.m */; }; 7761133821766A3000D350CD /* ContentLoadingCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 7761133621766A3000D350CD /* ContentLoadingCell.m */; }; 7761133921766A3000D350CD /* ContentLoadingCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7761133721766A3000D350CD /* ContentLoadingCell.xib */; }; 7761135D21790B0300D350CD /* JuickPush.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 7761135521790B0200D350CD /* JuickPush.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; @@ -60,9 +61,21 @@ 77C67EEC18283F2D00427098 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77C67EEB18283F2D00427098 /* QuartzCore.framework */; }; 77C6ADDE1F770EB2000AEA8C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 77C6ADDD1F770EB2000AEA8C /* Main.storyboard */; }; 77C6ADE41F7717BC000AEA8C /* ThreadViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 77C6ADE31F7717BC000AEA8C /* ThreadViewController.m */; }; + 77CEB65825129F550055FF30 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77CEB65425129F550055FF30 /* SceneDelegate.swift */; }; + 77CEB65925129F550055FF30 /* ImageFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77CEB65525129F550055FF30 /* ImageFetcher.swift */; }; + 77CEB65A25129F550055FF30 /* MessageFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77CEB65625129F550055FF30 /* MessageFetcher.swift */; }; + 77CEB65B25129F550055FF30 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77CEB65725129F550055FF30 /* AppDelegate.swift */; }; + 77CEB66425129F7E0055FF30 /* MessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77CEB65F25129F7E0055FF30 /* MessageView.swift */; }; + 77CEB66525129F7E0055FF30 /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77CEB66025129F7E0055FF30 /* ActivityIndicator.swift */; }; + 77CEB66625129F7E0055FF30 /* LoadableImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77CEB66125129F7E0055FF30 /* LoadableImageView.swift */; }; + 77CEB66725129F7E0055FF30 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77CEB66225129F7E0055FF30 /* ContentView.swift */; }; + 77CEB66825129F7E0055FF30 /* FeedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77CEB66325129F7E0055FF30 /* FeedView.swift */; }; + 77CEB66D25129F980055FF30 /* LoadableState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77CEB66C25129F980055FF30 /* LoadableState.swift */; }; + 77CEB6802512A8BF0055FF30 /* Data+Hex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77CEB67F2512A8BF0055FF30 /* Data+Hex.swift */; }; 77E35A82189A5B5A00B2D216 /* LoginViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 77E35A81189A5B5A00B2D216 /* LoginViewController.m */; }; 77E61A5B1FD467FC00B4E304 /* QuoteView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 77E61A5A1FD467FC00B4E304 /* QuoteView.xib */; }; 77E61A5E1FD4682B00B4E304 /* QuoteView.m in Sources */ = {isa = PBXBuildFile; fileRef = 77E61A5D1FD4682B00B4E304 /* QuoteView.m */; }; + 77E7E73A252CE95200957295 /* Atributika in Frameworks */ = {isa = PBXBuildFile; productRef = 77E7E739252CE95200957295 /* Atributika */; }; 77F2B6A2251249F300E42F6F /* JuickTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F2B6A1251249F300E42F6F /* JuickTests.swift */; }; 77FCADDF1D6A50DA00CBA649 /* API.m in Sources */ = {isa = PBXBuildFile; fileRef = 77FCADDE1D6A50DA00CBA649 /* API.m */; }; /* End PBXBuildFile section */ @@ -112,7 +125,6 @@ 77317BAF181BBE8500D60005 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 77317BB3181BBE8500D60005 /* Juick-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Juick-Info.plist"; sourceTree = ""; }; 77317BB5181BBE8500D60005 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; - 77317BB7181BBE8500D60005 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 77317BB9181BBE8500D60005 /* Juick-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Juick-Prefix.pch"; sourceTree = ""; }; 77317BBA181BBE8500D60005 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 77317BBB181BBE8500D60005 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -149,6 +161,9 @@ 774746B5239F872A0001C7F9 /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/CoreServices.framework; sourceTree = DEVELOPER_DIR; }; 774C98C0251263720073C70A /* Juick-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Juick-Bridging-Header.h"; sourceTree = ""; }; 774C98CC25126C070073C70A /* Service.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Service.swift; sourceTree = ""; }; + 774E6B51251AB5A4006B5D5F /* AttributedLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedLabelView.swift; sourceTree = ""; }; + 774E6B56251AB70B006B5D5F /* NSAttributedString_Entities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NSAttributedString_Entities.h; sourceTree = ""; }; + 774E6B57251AB743006B5D5F /* NSAttributedString_Entities.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NSAttributedString_Entities.m; sourceTree = ""; }; 7761133521766A3000D350CD /* ContentLoadingCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ContentLoadingCell.h; sourceTree = ""; }; 7761133621766A3000D350CD /* ContentLoadingCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ContentLoadingCell.m; sourceTree = ""; }; 7761133721766A3000D350CD /* ContentLoadingCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ContentLoadingCell.xib; sourceTree = ""; }; @@ -191,6 +206,18 @@ 77C6ADDD1F770EB2000AEA8C /* Main.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; 77C6ADE21F7717BC000AEA8C /* ThreadViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ThreadViewController.h; sourceTree = ""; }; 77C6ADE31F7717BC000AEA8C /* ThreadViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ThreadViewController.m; sourceTree = ""; }; + 77CEB65425129F550055FF30 /* SceneDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 77CEB65525129F550055FF30 /* ImageFetcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageFetcher.swift; sourceTree = ""; }; + 77CEB65625129F550055FF30 /* MessageFetcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageFetcher.swift; sourceTree = ""; }; + 77CEB65725129F550055FF30 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 77CEB65F25129F7E0055FF30 /* MessageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageView.swift; sourceTree = ""; }; + 77CEB66025129F7E0055FF30 /* ActivityIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityIndicator.swift; sourceTree = ""; }; + 77CEB66125129F7E0055FF30 /* LoadableImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadableImageView.swift; sourceTree = ""; }; + 77CEB66225129F7E0055FF30 /* ContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 77CEB66325129F7E0055FF30 /* FeedView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedView.swift; sourceTree = ""; }; + 77CEB66C25129F980055FF30 /* LoadableState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadableState.swift; sourceTree = ""; }; + 77CEB67F2512A8BF0055FF30 /* Data+Hex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+Hex.swift"; sourceTree = ""; }; + 77CEB6872512AB680055FF30 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/iOSSupport/System/Library/Frameworks/SwiftUI.framework; sourceTree = DEVELOPER_DIR; }; 77D40AB8218B5BD60074E14F /* Local.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Local.xcconfig; sourceTree = ""; }; 77D40ABB218B5CC90074E14F /* Production.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Production.xcconfig; sourceTree = ""; }; 77E35A80189A5B5A00B2D216 /* LoginViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LoginViewController.h; sourceTree = ""; }; @@ -218,6 +245,7 @@ 77317BB0181BBE8500D60005 /* UIKit.framework in Frameworks */, 77317BAC181BBE8500D60005 /* Foundation.framework in Frameworks */, 774746B6239F872A0001C7F9 /* CoreServices.framework in Frameworks */, + 77E7E73A252CE95200957295 /* Atributika in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -263,6 +291,7 @@ 77317BAA181BBE8500D60005 /* Frameworks */ = { isa = PBXGroup; children = ( + 77CEB6872512AB680055FF30 /* SwiftUI.framework */, 774746B5239F872A0001C7F9 /* CoreServices.framework */, 774528CB1F93EE9F004D110B /* libz.tbd */, 774528C91F93EE83004D110B /* AssetsLibrary.framework */, @@ -283,23 +312,27 @@ isa = PBXGroup; children = ( 774528CD1F96B39C004D110B /* Juick.entitlements */, + 77FCADE01D6A50EC00CBA649 /* API.h */, + 77317BBA181BBE8500D60005 /* AppDelegate.h */, + 77FCADDE1D6A50DA00CBA649 /* API.m */, + 77317BBB181BBE8500D60005 /* AppDelegate.m */, 773F23341F76ED5D00B5B0DF /* Splash.png */, 773F23351F76ED5D00B5B0DF /* Splash@2x.png */, 773F23361F76ED5E00B5B0DF /* Splash@3x.png */, 773F23321F76ECAC00B5B0DF /* LaunchScreen.storyboard */, + 77C6ADDD1F770EB2000AEA8C /* Main.storyboard */, + 77CEB65725129F550055FF30 /* AppDelegate.swift */, + 77CEB65525129F550055FF30 /* ImageFetcher.swift */, + 77CEB65625129F550055FF30 /* MessageFetcher.swift */, + 77CEB65425129F550055FF30 /* SceneDelegate.swift */, + 774C98CC25126C070073C70A /* Service.swift */, + 77C364912243FAEF0017522C /* Colors.xcassets */, + 77317BC6181BBE8500D60005 /* Images.xcassets */, 774DD4601D735E1D00C7F290 /* Helpers */, 774DD45D1D735DDD00C7F290 /* Model */, 77317BB2181BBE8500D60005 /* Supporting Files */, 774DD45F1D735E0300C7F290 /* ViewControllers */, 77FFC0151D5FD13C003BD81A /* Views */, - 77FCADE01D6A50EC00CBA649 /* API.h */, - 77FCADDE1D6A50DA00CBA649 /* API.m */, - 77317BBA181BBE8500D60005 /* AppDelegate.h */, - 77317BBB181BBE8500D60005 /* AppDelegate.m */, - 77317BC6181BBE8500D60005 /* Images.xcassets */, - 77C6ADDD1F770EB2000AEA8C /* Main.storyboard */, - 77C364912243FAEF0017522C /* Colors.xcassets */, - 774C98CC25126C070073C70A /* Service.swift */, ); path = Juick; sourceTree = ""; @@ -312,7 +345,6 @@ 77317BB4181BBE8500D60005 /* InfoPlist.strings */, 77317BB3181BBE8500D60005 /* Juick-Info.plist */, 77317BB9181BBE8500D60005 /* Juick-Prefix.pch */, - 77317BB7181BBE8500D60005 /* main.m */, 77D40AB8218B5BD60074E14F /* Local.xcconfig */, 77D40ABB218B5CC90074E14F /* Production.xcconfig */, ); @@ -379,6 +411,7 @@ 774DD4601D735E1D00C7F290 /* Helpers */ = { isa = PBXGroup; children = ( + 77CEB66C25129F980055FF30 /* LoadableState.swift */, 77C67EE31828342000427098 /* NSURL+PathParameters.h */, 77C67EE41828342000427098 /* NSURL+PathParameters.m */, 77B09992189D0B9900A84F59 /* UIImage+Utils.h */, @@ -387,6 +420,9 @@ 7785605F2343D24E00BB37A2 /* NSData+Hex.m */, 774746AB239F82A10001C7F9 /* NSDate+TimeAgo.h */, 774746AC239F82A10001C7F9 /* NSDate+TimeAgo.m */, + 77CEB67F2512A8BF0055FF30 /* Data+Hex.swift */, + 774E6B56251AB70B006B5D5F /* NSAttributedString_Entities.h */, + 774E6B57251AB743006B5D5F /* NSAttributedString_Entities.m */, ); path = Helpers; sourceTree = ""; @@ -414,6 +450,11 @@ 77FFC0151D5FD13C003BD81A /* Views */ = { isa = PBXGroup; children = ( + 77CEB66025129F7E0055FF30 /* ActivityIndicator.swift */, + 77CEB66225129F7E0055FF30 /* ContentView.swift */, + 77CEB66325129F7E0055FF30 /* FeedView.swift */, + 77CEB66125129F7E0055FF30 /* LoadableImageView.swift */, + 77CEB65F25129F7E0055FF30 /* MessageView.swift */, 773E63A1204BE036008B8F8D /* BubbleMessageCell.h */, 773E63A2204BE036008B8F8D /* BubbleMessageCell.m */, 773E6391204BCAD6008B8F8D /* BubbleMessageCell.xib */, @@ -432,6 +473,7 @@ 7761133521766A3000D350CD /* ContentLoadingCell.h */, 7761133621766A3000D350CD /* ContentLoadingCell.m */, 7761133721766A3000D350CD /* ContentLoadingCell.xib */, + 774E6B51251AB5A4006B5D5F /* AttributedLabelView.swift */, ); path = Views; sourceTree = ""; @@ -454,6 +496,9 @@ 7761135C21790B0300D350CD /* PBXTargetDependency */, ); name = Juick; + packageProductDependencies = ( + 77E7E739252CE95200957295 /* Atributika */, + ); productName = Juick; productReference = 77317BA8181BBE8500D60005 /* Juick.app */; productType = "com.apple.product-type.application"; @@ -541,6 +586,9 @@ Base, ); mainGroup = 77317B9F181BBE8500D60005; + packageReferences = ( + 77E7E738252CE95200957295 /* XCRemoteSwiftPackageReference "Atributika" */, + ); productRefGroup = 77317BA9181BBE8500D60005 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -596,9 +644,15 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 774E6B64251AC4D2006B5D5F /* NSAttributedString_Entities.m in Sources */, 77C36498224417FC0017522C /* DiscussionsController.m in Sources */, + 77CEB65A25129F550055FF30 /* MessageFetcher.swift in Sources */, + 77CEB66825129F7E0055FF30 /* FeedView.swift in Sources */, + 77CEB66525129F7E0055FF30 /* ActivityIndicator.swift in Sources */, 770C86E325117D78009B6404 /* UIView+Shimmer.m in Sources */, + 77CEB66D25129F980055FF30 /* LoadableState.swift in Sources */, 7761133821766A3000D350CD /* ContentLoadingCell.m in Sources */, + 77CEB65925129F550055FF30 /* ImageFetcher.swift in Sources */, 77E61A5E1FD4682B00B4E304 /* QuoteView.m in Sources */, 77317BBC181BBE8500D60005 /* AppDelegate.m in Sources */, 776C41C11FD3FF6E0063B82E /* FeedViewController.m in Sources */, @@ -606,8 +660,12 @@ 77A0954A181F1F25002BDECD /* Message.m in Sources */, 773E63A0204BDF16008B8F8D /* ChatViewController.m in Sources */, 773E639D204BD0F2008B8F8D /* Chat.m in Sources */, + 77CEB65B25129F550055FF30 /* AppDelegate.swift in Sources */, + 77CEB66425129F7E0055FF30 /* MessageView.swift in Sources */, 77B8DCD62093FC03000DBB04 /* BlogViewController.m in Sources */, 773E639A204BCE6D008B8F8D /* DialogsViewController.m in Sources */, + 774E6B52251AB5A4006B5D5F /* AttributedLabelView.swift in Sources */, + 77CEB6802512A8BF0055FF30 /* Data+Hex.swift in Sources */, 772B4E6C2199811E0029706E /* Entity.m in Sources */, 77B09994189D0B9900A84F59 /* UIImage+Utils.m in Sources */, 77317BC2181BBE8500D60005 /* MessagesViewController.m in Sources */, @@ -617,14 +675,16 @@ 77975A1D182B6E9A00410C2B /* NewPostViewController.m in Sources */, 774746AD239F82A10001C7F9 /* NSDate+TimeAgo.m in Sources */, 77C36495224417E90017522C /* DiscoverViewController.m in Sources */, + 77CEB66725129F7E0055FF30 /* ContentView.swift in Sources */, 77C67EE51828342000427098 /* NSURL+PathParameters.m in Sources */, 77E35A82189A5B5A00B2D216 /* LoginViewController.m in Sources */, 773E6397204BCB64008B8F8D /* ConversationCell.m in Sources */, 778560602343D24E00BB37A2 /* NSData+Hex.m in Sources */, 774C98CD25126C070073C70A /* Service.swift in Sources */, + 77CEB66625129F7E0055FF30 /* LoadableImageView.swift in Sources */, 776C41BD1FD3EF180063B82E /* MessageCell.m in Sources */, - 77317BB8181BBE8500D60005 /* main.m in Sources */, 77B8B39C207A5629005CB20C /* MessageInputView.m in Sources */, + 77CEB65825129F550055FF30 /* SceneDelegate.swift in Sources */, 77C3648C2241B3060017522C /* DeviceRegistration.m in Sources */, 774528C21F930C06004D110B /* Attachment.m in Sources */, 778560632344CF6F00BB37A2 /* JuickNavigationController.m in Sources */, @@ -726,7 +786,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; }; @@ -776,7 +836,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; }; @@ -797,7 +857,10 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Juick/Supporting Files/Juick-Prefix.pch"; INFOPLIST_FILE = "Juick/Supporting Files/Juick-Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = "com.juick.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; @@ -823,7 +886,10 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Juick/Supporting Files/Juick-Prefix.pch"; INFOPLIST_FILE = "Juick/Supporting Files/Juick-Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = "com.juick.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; @@ -856,7 +922,11 @@ GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = JuickPush/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.juick.Juick.JuickPush; @@ -889,7 +959,11 @@ GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = JuickPush/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.juick.Juick.JuickPush; @@ -953,7 +1027,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; }; @@ -974,7 +1048,10 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Juick/Supporting Files/Juick-Prefix.pch"; INFOPLIST_FILE = "Juick/Supporting Files/Juick-Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = "com.juick.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; @@ -1008,7 +1085,11 @@ GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = JuickPush/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.juick.Juick.JuickPush; @@ -1038,7 +1119,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = JuickTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.juick.JuickTests; @@ -1067,7 +1152,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = JuickTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.juick.JuickTests; @@ -1096,12 +1185,17 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = JuickTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.juick.JuickTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = Juick; @@ -1152,6 +1246,25 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 77E7E738252CE95200957295 /* XCRemoteSwiftPackageReference "Atributika" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/psharanda/Atributika.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 4.9.9; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 77E7E739252CE95200957295 /* Atributika */ = { + isa = XCSwiftPackageProductDependency; + package = 77E7E738252CE95200957295 /* XCRemoteSwiftPackageReference "Atributika" */; + productName = Atributika; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 77317BA0181BBE8500D60005 /* Project object */; } diff --git a/Juick/AppDelegate.swift b/Juick/AppDelegate.swift new file mode 100644 index 0000000..d1062f4 --- /dev/null +++ b/Juick/AppDelegate.swift @@ -0,0 +1,125 @@ +// +// AppDelegate.swift +// tst +// +// Created by Vitaly Takmazov on 10.12.2019. +// Copyright © 2019 com.juick. All rights reserved. +// + +import UIKit +import CoreData + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate { + lazy var api : API = { + return API() + }() + lazy var sharedDateFormatter : DateFormatter = { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" + dateFormatter.timeZone = TimeZone(abbreviation: "UTC") + return dateFormatter + }() + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // cleanup synchronized credentials which are not used anymore + let allCreds = URLCredentialStorage.shared.allCredentials + for (space, _) in allCreds { + if space.host == "api.juick.com" { + allCreds[space]?.values.forEach { + URLCredentialStorage.shared.remove($0, for: space, options: [NSURLCredentialStorageRemoveSynchronizableCredentials:true]) + } + } + } + #if !targetEnvironment(simulator) + registerForRemoteNotifications() + #endif + if let userInfo = launchOptions?[UIApplication.LaunchOptionsKey.remoteNotification] { + parseNotificationPayload(userInfo: userInfo as! [AnyHashable : Any]) + } + return true + } + + func registerForRemoteNotifications() { + let center = UNUserNotificationCenter.current() + center.delegate = self + center.requestAuthorization(options: [.sound, .alert, .badge]) { (granted, error) in + if (error == nil) { + OperationQueue.main.addOperation { + UIApplication.shared.registerForRemoteNotifications() + } + } + } + } + + func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { + let token = deviceToken.hexString + OperationQueue().addOperation { + let registration = DeviceRegistration() + registration.type = "apns" + registration.token = token + self.api.refreshDeviceRegistration(registration, callback: { + (success) in + debugPrint("Successfully refreshed registration with \(token)") + }) + } + } + + func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { + debugPrint("APNS error: \(error.localizedDescription)") + } + func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { + if userInfo["service"] as? Bool ?? false { + if let user = User.fromJSON(userInfo["user"] as? [AnyHashable : Any]) { + application.applicationIconBadgeNumber = user.unreadCount + } + } + } + // Called when a notification is delivered to a foreground app. + func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { + completionHandler([.sound, .alert, .badge]) + } + //Called to let your app know which action was selected by the user for a given notification. + func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { + let userInfo = response.notification.request.content.userInfo + parseNotificationPayload(userInfo: userInfo) + OperationQueue.main.addOperation { + // TODO: initialize correct tab + } + } + + var pushedThread : Int? + var pushedReplyId : Int? + var pushedUname : String? + + func parseNotificationPayload(userInfo:[AnyHashable: Any]) { + self.pushedThread = userInfo["mid"] as? Int; + self.pushedUname = userInfo["uname"] as? String; + self.pushedReplyId = userInfo["rid"] as? Int; + } + + func cleanupPushedData() { + self.pushedUname = nil; + self.pushedThread = nil; + self.pushedReplyId = nil; + } + + static var shared : AppDelegate { + return UIApplication.shared.delegate as! AppDelegate + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } + +} + diff --git a/Juick/Helpers/Data+Hex.swift b/Juick/Helpers/Data+Hex.swift new file mode 100644 index 0000000..69c170d --- /dev/null +++ b/Juick/Helpers/Data+Hex.swift @@ -0,0 +1,16 @@ +// +// Data+Hex.swift +// Juick +// +// Created by Vitaly Takmazov on 16.09.2020. +// Copyright © 2020 com.juick. All rights reserved. +// + +import Foundation + +extension Data { + var hexString: String { + let hexString = map { String(format: "%02.2hhx", $0) }.joined() + return hexString + } +} diff --git a/Juick/Helpers/LoadableState.swift b/Juick/Helpers/LoadableState.swift new file mode 100644 index 0000000..a45edb2 --- /dev/null +++ b/Juick/Helpers/LoadableState.swift @@ -0,0 +1,25 @@ +// +// LoadableState.swift +// tst +// +// Created by Vitaly Takmazov on 10.12.2019. +// Copyright © 2019 com.juick. All rights reserved. +// + +import Foundation + +enum LoadableState { + case loading + case fetched(Result) +} + +enum FetchError: Error { + case error(String) + + var localizedDescription: String { + switch self { + case .error(let message): + return message + } + } +} diff --git a/Juick/Helpers/NSAttributedString_Entities.h b/Juick/Helpers/NSAttributedString_Entities.h new file mode 100644 index 0000000..691d3d4 --- /dev/null +++ b/Juick/Helpers/NSAttributedString_Entities.h @@ -0,0 +1,19 @@ +// +// NSAttributedString+NSAttributedString_Entities.h +// Juick +// +// Created by Vitaly Takmazov on 23.09.2020. +// Copyright © 2020 com.juick. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSAttributedString (Entities) + ++(NSAttributedString *) attributedStringFromMessage:(Message *)message; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Juick/Helpers/NSAttributedString_Entities.m b/Juick/Helpers/NSAttributedString_Entities.m new file mode 100644 index 0000000..3f7159b --- /dev/null +++ b/Juick/Helpers/NSAttributedString_Entities.m @@ -0,0 +1,90 @@ +// +// NSAttributedString+NSAttributedString.h +// Juick +// +// Created by Vitaly Takmazov on 23.09.2020. +// Copyright © 2020 com.juick. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@implementation NSAttributedString (Entities) + +NSMutableParagraphStyle *quoteStyle; +UIFont *boldFont; +UIFont *italicFont; + +__attribute__((constructor)) +static void initialize_fonts() { + quoteStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; + quoteStyle.firstLineHeadIndent = 12.0f; + quoteStyle.headIndent = 12.0f; + quoteStyle.paragraphSpacing = 6.0f; + UIFontDescriptor* fontDescriptor = [UIFontDescriptor + preferredFontDescriptorWithTextStyle:UIFontTextStyleBody]; + UIFontDescriptor* boldFontDescriptor = [fontDescriptor + fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold]; + boldFont = [UIFont fontWithDescriptor:boldFontDescriptor size: 0.0]; + UIFontDescriptor* italicFontDescriptor = [fontDescriptor + fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitItalic]; + italicFont = [UIFont fontWithDescriptor:italicFontDescriptor size: 0.0]; +} + ++(NSAttributedString *) attributedStringFromMessage:(Message *)msg { + if (msg.text) { + NSMutableAttributedString *txt = [[NSMutableAttributedString alloc] + initWithString:msg.text + attributes:@{NSFontAttributeName:[UIFont preferredFontForTextStyle:UIFontTextStyleBody], + NSForegroundColorAttributeName:[UIColor colorNamed:@"Text"] + }]; + [txt beginEditing]; + for (Entity *entity in msg.entities) { + NSUInteger start = entity.start ? [entity.start unsignedIntegerValue] : 0; + NSUInteger end = entity.end ? [entity.end unsignedIntegerValue] : 0; + NSString *text = entity.text ? entity.text : @""; + NSRange currentRange = NSMakeRange(start, end - start); + [txt addAttribute:@"displayText" value:text range:currentRange]; + if ([entity.type isEqualToString:@"a"]) { + [txt addAttribute:NSLinkAttributeName value:entity.link range:currentRange]; + } + if ([entity.type isEqualToString:@"q"]) { + [txt addAttribute:NSForegroundColorAttributeName value:[UIColor colorNamed:@"Muted"] range:currentRange]; + [txt addAttribute:NSParagraphStyleAttributeName value:quoteStyle range:currentRange]; + } + if ([entity.type isEqualToString:@"u"]) { + [txt addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlineStyleSingle) range:currentRange]; + } + + if ([entity.type isEqualToString:@"b"]) { + [txt addAttribute:NSFontAttributeName value:boldFont range:currentRange]; + } + if ([entity.type isEqualToString:@"i"]) { + [txt addAttribute:NSFontAttributeName value:italicFont range:currentRange]; + } + } + [txt enumerateAttribute:@"displayText" inRange:NSMakeRange(0, [txt length]) options:0 usingBlock:^(id _Nullable value, NSRange range, BOOL * _Nonnull stop) { + if (value) { + [txt replaceCharactersInRange:range withString:value]; + } + }]; + if ([msg.tags count] > 0) { + NSString *tagsList = [NSString stringWithFormat:@"%@\n", [msg.tags componentsJoinedByString:@", "]]; + [txt insertAttributedString:[[NSAttributedString alloc] + initWithString:tagsList + attributes:@{ + NSFontAttributeName:italicFont, + NSForegroundColorAttributeName:[UIColor colorNamed:@"Muted"] + }] atIndex:0]; + } + [txt endEditing]; + return txt; + } else { + return [[NSAttributedString alloc] initWithString:@""]; + } +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Juick/ImageFetcher.swift b/Juick/ImageFetcher.swift new file mode 100644 index 0000000..f76c0ba --- /dev/null +++ b/Juick/ImageFetcher.swift @@ -0,0 +1,28 @@ +// +// ImageFetcher.swift +// tst +// +// Created by Vitaly Takmazov on 10.12.2019. +// Copyright © 2019 com.juick. All rights reserved. +// + +import Foundation +import Combine + +class ImageFetcher: ObservableObject { + + @Published var data: Data = Data() + + init(url: String) { + guard let imageUrl = URL(string: url) else { + return + } + + URLSession.shared.dataTask(with: imageUrl) { (data, _, _) in + guard let data = data else { return } + DispatchQueue.main.async { [weak self] in + self?.data = data + } + }.resume() + } +} diff --git a/Juick/Main.storyboard b/Juick/Main.storyboard index 5d99c97..3e7e571 100644 --- a/Juick/Main.storyboard +++ b/Juick/Main.storyboard @@ -1,9 +1,9 @@ - + - + @@ -96,7 +96,7 @@ - + @@ -127,7 +127,7 @@ -