From 6cc93c13b3e6b2a801df1e4c0957fb4011630151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=81=E4=B8=9A=E5=8D=87?= Date: Sun, 25 Apr 2021 15:04:15 +0800 Subject: [PATCH 1/2] New macOS target --- Source/Hollow.xcodeproj/project.pbxproj | 1387 ++++++++++++----- Source/Hollow/App/AppDelegate.swift | 128 +- Source/Hollow/App/AppModel.swift | 6 + Source/Hollow/App/AppModelBehaviour.swift | 1 + Source/Hollow/App/AppModelEnvironment.swift | 4 + Source/Hollow/App/HollowApp.swift | 2 - .../Model/Extensions/UIDevice+isPad.swift | 4 - .../Hollow/View/Components/LoadingLabel.swift | 2 +- Source/Hollow/View/Components/SearchBar.swift | 2 +- .../Content/HollowVoteContentView.swift | 5 +- .../View/Hierarchy/Login/LoginSubViews.swift | 38 +- Source/HollowMac/App/AppDelegate.swift | 23 + Source/HollowMac/App/AppModel.swift | 14 + Source/HollowMac/App/AppModelBehaviour.swift | 56 + .../HollowMac/App/AppModelEnvironment.swift | 46 + Source/HollowMac/App/HollowMacApp.swift | 35 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 58 + .../HollowMac/Assets.xcassets/Contents.json | 6 + Source/HollowMac/ContentView.swift | 22 + Source/HollowMac/HollowMac.entitlements | 12 + Source/HollowMac/Info.plist | 39 + .../View/Hierarchy/Login/LoginSubViews.swift | 79 + .../View/Hierarchy/Login/LoginView.swift | 128 ++ .../View/Hierarchy/Login/WelcomeView.swift | 72 + .../View/Hierarchy/Main/PostListView.swift | 25 + .../View/Modifiers/View+roundedCorner.swift | 15 + .../View/Utilities/View+showAlert.swift | 86 + Source/Shared/App/AppDelegateShared.swift | 156 ++ .../CrossPlatform/CrossPlatformImage.swift | 37 + .../CrossPlatform/CrossPlatformTypes.swift | 18 + .../{Hollow => Shared}/Model/Constants.swift | 18 +- .../Extensions/Array+removeDuplicates.swift | 0 .../Model/Extensions/Array+sortedRange.swift | 0 .../Model/Extensions/Bool+int.swift | 0 .../Extensions/Data+hexEncodedString.swift | 0 .../Model/Extensions/DateExtensions.swift | 2 +- .../Model/Extensions/DefaultsKeys.swift | 6 + .../Model/Extensions/Double+int.swift | 0 .../Model/Extensions/Int+bool.swift | 0 .../Model/Extensions/Int+string.swift | 0 .../Extensions/PublisherExtensions.swift | 2 +- .../Model/Extensions/String+Date.swift | 0 .../Model/Extensions/String+SHA256.swift | 0 .../Model/Extensions/String+regex.swift | 0 .../Extensions/String+removeLineBreak.swift | 2 +- .../Model/Net/Config/GetConfigRequest.swift | 0 .../Model/Net/Config/GetPushRequest.swift | 0 .../Model/Net/Config/SetPushRequest.swift | 0 .../Net/Contents/AttentionListRequest.swift | 0 .../Contents/AttentionListSearchRequest.swift | 0 .../Net/Contents/PostDetailRequest.swift | 0 .../Model/Net/Contents/PostListRequest.swift | 1 - .../Net/Contents/RandomListRequest.swift | 0 .../Net/Contents/SearchHistoryRequest.swift | 0 .../Model/Net/Contents/SearchRequest.swift | 0 .../Net/Contents/SystemMessageRequest.swift | 0 .../Model/Net/DefaultRequest.swift | 0 .../Model/Net/Edit/EditAttentionRequest.swift | 0 .../Model/Net/Edit/ReportCommentRequest.swift | 0 .../Model/Net/Edit/ReportPostRequest.swift | 0 .../Model/Net/Other/FetchImageRequest.swift | 4 +- .../Net/Other/PostListRequestGroup.swift | 0 .../Model/Net/Other/ReportRequestGroup.swift | 0 Source/{Hollow => Shared}/Model/Net/README.md | 0 .../Model/Net/Request+publisher.swift | 0 .../Model/Net/Request.swift | 0 .../Model/Net/RequestError.swift | 0 .../Model/Net/RequestPublisher.swift | 0 .../Net/Security/AccountCreationRequest.swift | 1 - .../Net/Security/ChangePasswordRequest.swift | 0 .../Net/Security/DeviceListRequest.swift | 0 .../Security/DeviceTerminationRequest.swift | 0 .../Net/Security/EmailCheckRequest.swift | 0 .../Model/Net/Security/LoginRequest.swift | 2 +- .../Model/Net/Security/LogoutRequest.swift | 0 .../Security/UpdateDeviceTokenRequest.swift | 0 .../Model/Net/Send/SendCommentRequest.swift | 0 .../Model/Net/Send/SendPostRequest.swift | 0 .../Model/Net/Send/SendVoteRequest.swift | 0 .../Model/Types/Comment.swift | 0 .../Model/Types/Data/CommentData.swift | 2 +- .../Model/Types/Data/HollowImage.swift | 21 +- .../Model/Types/Data/PostData.swift | 2 +- .../Model/Types/Data/VoteData.swift | 0 .../Model/Types/DeviceInformationType.swift | 0 .../Model/Types/HollowConfig.swift | 0 .../Model/Types/HollowType.swift | 0 .../Model/Types/ImageMetadata.swift | 3 +- .../{Hollow => Shared}/Model/Types/Post.swift | 0 .../Model/Types/PushNotificationType.swift | 0 .../Model/Types/SystemMessage.swift | 0 .../{Hollow => Shared}/Model/Types/Vote.swift | 0 .../Model/Utilities/AvatarGenerator.swift | 0 .../Utilities/DeviceModelUtilities.swift | 6 - .../Model/Utilities/HollowDateFormatter.swift | 0 .../Model/Utilities/ImageCompressor.swift | 20 +- .../Model/Utilities/LineSwitchManager.swift | 0 .../Model/Utilities/OpenURLHelper.swift | 12 +- .../Model/Utilities/PostCache.swift | 0 Source/Shared/View/ReCAPTCHAPageView.swift | 58 + .../View}/ReCAPTCHAWebView.swift | 32 +- .../View/ViewConstants.swift | 0 .../View}/ViewLayouts.swift | 0 .../ViewModel/Account/AccountInfoStore.swift | 4 + .../ViewModel/Account/DeviceListStore.swift | 0 .../ViewModel/Account/MessageStore.swift | 0 .../ViewModel/Hollow/HollowDetailStore.swift | 15 +- .../ViewModel/Hollow/HollowInputStore.swift | 9 +- .../ViewModel/Hollow/ImageCompressStore.swift | 30 +- .../Hollow/PostListRequestStore.swift | 2 +- .../ViewModel/Login/LoginStore.swift | 2 +- .../ViewModel/Login/WelcomeStore.swift | 6 + 113 files changed, 2115 insertions(+), 664 deletions(-) create mode 100644 Source/HollowMac/App/AppDelegate.swift create mode 100644 Source/HollowMac/App/AppModel.swift create mode 100644 Source/HollowMac/App/AppModelBehaviour.swift create mode 100644 Source/HollowMac/App/AppModelEnvironment.swift create mode 100644 Source/HollowMac/App/HollowMacApp.swift create mode 100644 Source/HollowMac/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 Source/HollowMac/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Source/HollowMac/Assets.xcassets/Contents.json create mode 100644 Source/HollowMac/ContentView.swift create mode 100644 Source/HollowMac/HollowMac.entitlements create mode 100644 Source/HollowMac/Info.plist create mode 100644 Source/HollowMac/View/Hierarchy/Login/LoginSubViews.swift create mode 100644 Source/HollowMac/View/Hierarchy/Login/LoginView.swift create mode 100644 Source/HollowMac/View/Hierarchy/Login/WelcomeView.swift create mode 100644 Source/HollowMac/View/Hierarchy/Main/PostListView.swift create mode 100644 Source/HollowMac/View/Modifiers/View+roundedCorner.swift create mode 100644 Source/HollowMac/View/Utilities/View+showAlert.swift create mode 100644 Source/Shared/App/AppDelegateShared.swift create mode 100644 Source/Shared/CrossPlatform/CrossPlatformImage.swift create mode 100644 Source/Shared/CrossPlatform/CrossPlatformTypes.swift rename Source/{Hollow => Shared}/Model/Constants.swift (76%) rename Source/{Hollow => Shared}/Model/Extensions/Array+removeDuplicates.swift (100%) rename Source/{Hollow => Shared}/Model/Extensions/Array+sortedRange.swift (100%) rename Source/{Hollow => Shared}/Model/Extensions/Bool+int.swift (100%) rename Source/{Hollow => Shared}/Model/Extensions/Data+hexEncodedString.swift (100%) rename Source/{Hollow => Shared}/Model/Extensions/DateExtensions.swift (90%) rename Source/{Hollow => Shared}/Model/Extensions/DefaultsKeys.swift (98%) rename Source/{Hollow => Shared}/Model/Extensions/Double+int.swift (100%) rename Source/{Hollow => Shared}/Model/Extensions/Int+bool.swift (100%) rename Source/{Hollow => Shared}/Model/Extensions/Int+string.swift (100%) rename Source/{Hollow => Shared}/Model/Extensions/PublisherExtensions.swift (99%) rename Source/{Hollow => Shared}/Model/Extensions/String+Date.swift (100%) rename Source/{Hollow => Shared}/Model/Extensions/String+SHA256.swift (100%) rename Source/{Hollow => Shared}/Model/Extensions/String+regex.swift (100%) rename Source/{Hollow => Shared}/Model/Extensions/String+removeLineBreak.swift (90%) rename Source/{Hollow => Shared}/Model/Net/Config/GetConfigRequest.swift (100%) rename Source/{Hollow => Shared}/Model/Net/Config/GetPushRequest.swift (100%) rename Source/{Hollow => Shared}/Model/Net/Config/SetPushRequest.swift (100%) rename Source/{Hollow => Shared}/Model/Net/Contents/AttentionListRequest.swift (100%) rename Source/{Hollow => Shared}/Model/Net/Contents/AttentionListSearchRequest.swift (100%) rename Source/{Hollow => Shared}/Model/Net/Contents/PostDetailRequest.swift (100%) rename Source/{Hollow => Shared}/Model/Net/Contents/PostListRequest.swift (99%) rename Source/{Hollow => Shared}/Model/Net/Contents/RandomListRequest.swift (100%) rename Source/{Hollow => Shared}/Model/Net/Contents/SearchHistoryRequest.swift (100%) rename Source/{Hollow => Shared}/Model/Net/Contents/SearchRequest.swift (100%) rename Source/{Hollow => Shared}/Model/Net/Contents/SystemMessageRequest.swift (100%) rename Source/{Hollow => Shared}/Model/Net/DefaultRequest.swift (100%) rename Source/{Hollow => Shared}/Model/Net/Edit/EditAttentionRequest.swift (100%) rename Source/{Hollow => Shared}/Model/Net/Edit/ReportCommentRequest.swift (100%) rename Source/{Hollow => Shared}/Model/Net/Edit/ReportPostRequest.swift (100%) rename Source/{Hollow => Shared}/Model/Net/Other/FetchImageRequest.swift (97%) rename Source/{Hollow => Shared}/Model/Net/Other/PostListRequestGroup.swift (100%) rename Source/{Hollow => Shared}/Model/Net/Other/ReportRequestGroup.swift (100%) rename Source/{Hollow => Shared}/Model/Net/README.md (100%) rename Source/{Hollow => Shared}/Model/Net/Request+publisher.swift (100%) rename Source/{Hollow => Shared}/Model/Net/Request.swift (100%) rename Source/{Hollow => Shared}/Model/Net/RequestError.swift (100%) rename Source/{Hollow => Shared}/Model/Net/RequestPublisher.swift (100%) rename Source/{Hollow => Shared}/Model/Net/Security/AccountCreationRequest.swift (99%) rename Source/{Hollow => Shared}/Model/Net/Security/ChangePasswordRequest.swift (100%) rename Source/{Hollow => Shared}/Model/Net/Security/DeviceListRequest.swift (100%) rename Source/{Hollow => Shared}/Model/Net/Security/DeviceTerminationRequest.swift (100%) rename Source/{Hollow => Shared}/Model/Net/Security/EmailCheckRequest.swift (100%) rename Source/{Hollow => Shared}/Model/Net/Security/LoginRequest.swift (99%) rename Source/{Hollow => Shared}/Model/Net/Security/LogoutRequest.swift (100%) rename Source/{Hollow => Shared}/Model/Net/Security/UpdateDeviceTokenRequest.swift (100%) rename Source/{Hollow => Shared}/Model/Net/Send/SendCommentRequest.swift (100%) rename Source/{Hollow => Shared}/Model/Net/Send/SendPostRequest.swift (100%) rename Source/{Hollow => Shared}/Model/Net/Send/SendVoteRequest.swift (100%) rename Source/{Hollow => Shared}/Model/Types/Comment.swift (100%) rename Source/{Hollow => Shared}/Model/Types/Data/CommentData.swift (98%) rename Source/{Hollow => Shared}/Model/Types/Data/HollowImage.swift (93%) rename Source/{Hollow => Shared}/Model/Types/Data/PostData.swift (99%) rename Source/{Hollow => Shared}/Model/Types/Data/VoteData.swift (100%) rename Source/{Hollow => Shared}/Model/Types/DeviceInformationType.swift (100%) rename Source/{Hollow => Shared}/Model/Types/HollowConfig.swift (100%) rename Source/{Hollow => Shared}/Model/Types/HollowType.swift (100%) rename Source/{Hollow => Shared}/Model/Types/ImageMetadata.swift (88%) rename Source/{Hollow => Shared}/Model/Types/Post.swift (100%) rename Source/{Hollow => Shared}/Model/Types/PushNotificationType.swift (100%) rename Source/{Hollow => Shared}/Model/Types/SystemMessage.swift (100%) rename Source/{Hollow => Shared}/Model/Types/Vote.swift (100%) rename Source/{Hollow => Shared}/Model/Utilities/AvatarGenerator.swift (100%) rename Source/{Hollow => Shared}/Model/Utilities/DeviceModelUtilities.swift (75%) rename Source/{Hollow => Shared}/Model/Utilities/HollowDateFormatter.swift (100%) rename Source/{Hollow => Shared}/Model/Utilities/ImageCompressor.swift (85%) rename Source/{Hollow => Shared}/Model/Utilities/LineSwitchManager.swift (100%) rename Source/{Hollow => Shared}/Model/Utilities/OpenURLHelper.swift (92%) rename Source/{Hollow => Shared}/Model/Utilities/PostCache.swift (100%) create mode 100644 Source/Shared/View/ReCAPTCHAPageView.swift rename Source/{Hollow/View/Integration => Shared/View}/ReCAPTCHAWebView.swift (76%) rename Source/{Hollow => Shared}/View/ViewConstants.swift (100%) rename Source/{Hollow/View/Utilities => Shared/View}/ViewLayouts.swift (100%) rename Source/{Hollow => Shared}/ViewModel/Account/AccountInfoStore.swift (96%) rename Source/{Hollow => Shared}/ViewModel/Account/DeviceListStore.swift (100%) rename Source/{Hollow => Shared}/ViewModel/Account/MessageStore.swift (100%) rename Source/{Hollow => Shared}/ViewModel/Hollow/HollowDetailStore.swift (97%) rename Source/{Hollow => Shared}/ViewModel/Hollow/HollowInputStore.swift (93%) rename Source/{Hollow => Shared}/ViewModel/Hollow/ImageCompressStore.swift (79%) rename Source/{Hollow => Shared}/ViewModel/Hollow/PostListRequestStore.swift (99%) rename Source/{Hollow => Shared}/ViewModel/Login/LoginStore.swift (98%) rename Source/{Hollow => Shared}/ViewModel/Login/WelcomeStore.swift (93%) diff --git a/Source/Hollow.xcodeproj/project.pbxproj b/Source/Hollow.xcodeproj/project.pbxproj index 677cc960..05d6d258 100644 --- a/Source/Hollow.xcodeproj/project.pbxproj +++ b/Source/Hollow.xcodeproj/project.pbxproj @@ -7,18 +7,10 @@ objects = { /* Begin PBXBuildFile section */ - 00AA103525E74212006B3D6D /* LineSwitchManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00AA103425E74212006B3D6D /* LineSwitchManager.swift */; }; - 00B014ED25E1205A004E416E /* PostCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00B014EC25E1205A004E416E /* PostCache.swift */; }; - 00EFC07125DFC71500536B52 /* Vote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00EFC07025DFC71500536B52 /* Vote.swift */; }; - 00EFC07425DFC73800536B52 /* ImageMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00EFC07325DFC73800536B52 /* ImageMetadata.swift */; }; 0E09B7AA25D4D73100DC5615 /* ImageViewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E09B7A925D4D73100DC5615 /* ImageViewer.swift */; }; 0E09B7B125D4E7E800DC5615 /* BarImageButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E09B7B025D4E7E800DC5615 /* BarImageButton.swift */; }; - 0E09B7BB25D52F4F00DC5615 /* HollowInputStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E09B7BA25D52F4F00DC5615 /* HollowInputStore.swift */; }; 0E186C39262C60A1001A5855 /* View+showToast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E186C38262C60A1001A5855 /* View+showToast.swift */; }; 0E1DD7D625D983BE000D27BA /* Avatar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1DD7D525D983BE000D27BA /* Avatar.swift */; }; - 0E1DD7D925DA4B91000D27BA /* AppModelBehaviour.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1DD7D825DA4B91000D27BA /* AppModelBehaviour.swift */; }; - 0E1DD7DC25DA4BE5000D27BA /* AppModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1DD7DB25DA4BE5000D27BA /* AppModel.swift */; }; - 0E1DD7DF25DA4E5C000D27BA /* AppModelEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1DD7DE25DA4E5C000D27BA /* AppModelEnvironment.swift */; }; 0E28BC1625F4ED1700A97820 /* WaterfallGrid in Frameworks */ = {isa = PBXBuildFile; productRef = 0E28BC1525F4ED1700A97820 /* WaterfallGrid */; }; 0E28BC1925F4ED2D00A97820 /* Defaults in Frameworks */ = {isa = PBXBuildFile; productRef = 0E28BC1825F4ED2D00A97820 /* Defaults */; }; 0E28BC1C25F4ED8200A97820 /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 0E28BC1B25F4ED8200A97820 /* Alamofire */; }; @@ -29,29 +21,21 @@ 0E28BC3525F4EFA400A97820 /* ImageScrollView in Frameworks */ = {isa = PBXBuildFile; productRef = 0E28BC3425F4EFA400A97820 /* ImageScrollView */; }; 0E28BC4225F5E29D00A97820 /* CustomColorScheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E28BC4125F5E29D00A97820 /* CustomColorScheme.swift */; }; 0E28BC4525F5F8A000A97820 /* View+defaultListStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E28BC4425F5F8A000A97820 /* View+defaultListStyle.swift */; }; - 0E28BC4A25F60EB400A97820 /* AccountInfoStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E28BC4925F60EB400A97820 /* AccountInfoStore.swift */; }; - 0E294C0D25E2570D00A9875A /* HollowDetailStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E294C0C25E2570D00A9875A /* HollowDetailStore.swift */; }; - 0E294E9725F1F448001E4C08 /* ReportRequestGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E294E9625F1F448001E4C08 /* ReportRequestGroup.swift */; }; 0E294E9C25F2405C001E4C08 /* View+roundedCorner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E294E9B25F2405C001E4C08 /* View+roundedCorner.swift */; }; 0E2C1EE425D7A04C00A296E9 /* MyButtonDefaultStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2C1EE325D7A04C00A296E9 /* MyButtonDefaultStyle.swift */; }; 0E36F54025D0E02F001AE852 /* GetSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E36F53F25D0E02F001AE852 /* GetSize.swift */; }; 0E36F54625D0F0FB001AE852 /* GetFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E36F54525D0F0FB001AE852 /* GetFrame.swift */; }; 0E386D8E2613101B000568B6 /* View+refreshNavigationItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E386D8D2613101B000568B6 /* View+refreshNavigationItem.swift */; }; - 0E3AC40025F7778400A4278A /* AvatarGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3AC3FF25F7778400A4278A /* AvatarGenerator.swift */; }; 0E3DA2A825FF379000ECEFAC /* ProviderInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3DA2A725FF379000ECEFAC /* ProviderInfoView.swift */; }; 0E3E694B260DF2C8005E8DA2 /* HollowDetailView_iPad.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3E694A260DF2C8005E8DA2 /* HollowDetailView_iPad.swift */; }; 0E3E694E260DFCE8005E8DA2 /* Text+dateText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3E694D260DFCE8005E8DA2 /* Text+dateText.swift */; }; - 0E3E6955260E08EC005E8DA2 /* UIDevice+isPad.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3E6954260E08EC005E8DA2 /* UIDevice+isPad.swift */; }; 0E3E6958260E1312005E8DA2 /* MainSubViews_iPad.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3E6957260E1312005E8DA2 /* MainSubViews_iPad.swift */; }; - 0E3E695B260E166D005E8DA2 /* HollowDateFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3E695A260E166D005E8DA2 /* HollowDateFormatter.swift */; }; 0E3E6960260E3676005E8DA2 /* SplitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3E695F260E3676005E8DA2 /* SplitView.swift */; }; 0E40F74825D7C997003A2342 /* HollowInputSubViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E40F74725D7C997003A2342 /* HollowInputSubViews.swift */; }; - 0E415A7425CD769B00351672 /* HollowApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E415A7325CD769B00351672 /* HollowApp.swift */; }; 0E415A7825CD769F00351672 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0E415A7725CD769F00351672 /* Assets.xcassets */; }; 0E415B4225CD772200351672 /* ViewConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E415AE925CD772200351672 /* ViewConstants.swift */; }; 0E415B4325CD772200351672 /* ErrorAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E415AEB25CD772200351672 /* ErrorAlert.swift */; }; 0E415B4425CD772200351672 /* CustomTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E415AED25CD772200351672 /* CustomTabView.swift */; }; - 0E415B4525CD772200351672 /* ReCAPTCHAWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E415AEE25CD772200351672 /* ReCAPTCHAWebView.swift */; }; 0E415B4625CD772200351672 /* CustomScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E415AEF25CD772200351672 /* CustomScrollView.swift */; }; 0E415B4725CD772200351672 /* Blur.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E415AF025CD772200351672 /* Blur.swift */; }; 0E415B4A25CD772200351672 /* Color+uiColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E415AF425CD772200351672 /* Color+uiColor.swift */; }; @@ -89,15 +73,7 @@ 0E415B7925CD772200351672 /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E415B2F25CD772200351672 /* LoginView.swift */; }; 0E415B7A25CD772200351672 /* LoginSubViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E415B3025CD772200351672 /* LoginSubViews.swift */; }; 0E415B7B25CD772200351672 /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E415B3125CD772200351672 /* WelcomeView.swift */; }; - 0E415B8025CD772200351672 /* PostListRequestStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E415B3C25CD772200351672 /* PostListRequestStore.swift */; }; - 0E415B8325CD772200351672 /* LoginStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E415B4025CD772200351672 /* LoginStore.swift */; }; - 0E415B8425CD772200351672 /* WelcomeStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E415B4125CD772200351672 /* WelcomeStore.swift */; }; - 0E415B9B25CD93C000351672 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E415B9A25CD93C000351672 /* AppDelegate.swift */; }; - 0E4786C825E3A93E0004F8FA /* RequestPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E4786C725E3A93E0004F8FA /* RequestPublisher.swift */; }; - 0E4786D925E3E1870004F8FA /* FetchImageRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E4786D825E3E1870004F8FA /* FetchImageRequest.swift */; }; - 0E527CD225F3D84800D91706 /* Array+removeDuplicates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E527CD125F3D84800D91706 /* Array+removeDuplicates.swift */; }; - 0E527CD625F4825700D91706 /* OpenURLHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E527CD525F4825700D91706 /* OpenURLHelper.swift */; }; - 0E58810A25E9E61B006F6A94 /* ConnectivityPublisherExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E58810925E9E61B006F6A94 /* ConnectivityPublisherExtensions.swift */; }; + 0E4E4BC52634619A00B32F7F /* View+roundedCorner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E4E4BC42634619A00B32F7F /* View+roundedCorner.swift */; }; 0E58810F25EA0F47006F6A94 /* View+presentPopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E58810E25EA0F47006F6A94 /* View+presentPopover.swift */; }; 0E671AC9260F394500590736 /* Cache in Frameworks */ = {isa = PBXBuildFile; productRef = 0E671AC8260F394500590736 /* Cache */; }; 0E671AD5260F6E8C00590736 /* View+onClickGesture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E671AD4260F6E8C00590736 /* View+onClickGesture.swift */; }; @@ -111,71 +87,13 @@ 0E85DFC525DB71F2003D134B /* IndicatorOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E85DFC425DB71F2003D134B /* IndicatorOverlay.swift */; }; 0E8BCFE325E095A90056D809 /* StyledAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8BCFE225E095A90056D809 /* StyledAlert.swift */; }; 0E8BCFE725E0A4720056D809 /* IntegrationUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8BCFE625E0A4720056D809 /* IntegrationUtilities.swift */; }; - 0E8CFBB8260B2E700050FDD2 /* HollowConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8CFBB7260B2E6F0050FDD2 /* HollowConfig.swift */; }; - 0E906CFB25DCEADD0034053C /* PostDetailRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CBB25DCEADC0034053C /* PostDetailRequest.swift */; }; - 0E906CFD25DCEADD0034053C /* SearchHistoryRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CBD25DCEADC0034053C /* SearchHistoryRequest.swift */; }; - 0E906CFE25DCEADD0034053C /* SearchRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CBE25DCEADC0034053C /* SearchRequest.swift */; }; - 0E906CFF25DCEADD0034053C /* PostListRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CBF25DCEADC0034053C /* PostListRequest.swift */; }; - 0E906D0025DCEADD0034053C /* SystemMessageRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CC025DCEADC0034053C /* SystemMessageRequest.swift */; }; - 0E906D0125DCEADD0034053C /* AttentionListRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CC125DCEADC0034053C /* AttentionListRequest.swift */; }; - 0E906D0225DCEADD0034053C /* RandomListRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CC225DCEADC0034053C /* RandomListRequest.swift */; }; - 0E906D0425DCEADD0034053C /* AttentionListSearchRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CC425DCEADC0034053C /* AttentionListSearchRequest.swift */; }; - 0E906D0525DCEADD0034053C /* DefaultRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CC525DCEADC0034053C /* DefaultRequest.swift */; }; - 0E906D0625DCEADD0034053C /* GetConfigRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CC725DCEADC0034053C /* GetConfigRequest.swift */; }; - 0E906D0725DCEADD0034053C /* GetPushRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CC825DCEADC0034053C /* GetPushRequest.swift */; }; - 0E906D0825DCEADD0034053C /* SetPushRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CC925DCEADC0034053C /* SetPushRequest.swift */; }; - 0E906D0925DCEADD0034053C /* LoginRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CCB25DCEADC0034053C /* LoginRequest.swift */; }; - 0E906D0A25DCEADD0034053C /* LogoutRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CCC25DCEADC0034053C /* LogoutRequest.swift */; }; - 0E906D0B25DCEADD0034053C /* EmailCheckRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CCD25DCEADC0034053C /* EmailCheckRequest.swift */; }; - 0E906D0C25DCEADD0034053C /* DeviceListRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CCE25DCEADC0034053C /* DeviceListRequest.swift */; }; - 0E906D0D25DCEADD0034053C /* UpdateDeviceTokenRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CCF25DCEADC0034053C /* UpdateDeviceTokenRequest.swift */; }; - 0E906D0E25DCEADD0034053C /* DeviceTerminationRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CD025DCEADC0034053C /* DeviceTerminationRequest.swift */; }; - 0E906D0F25DCEADD0034053C /* AccountCreationRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CD125DCEADC0034053C /* AccountCreationRequest.swift */; }; - 0E906D1025DCEADD0034053C /* ChangePasswordRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CD225DCEADC0034053C /* ChangePasswordRequest.swift */; }; - 0E906D1225DCEADD0034053C /* ReportPostRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CD525DCEADC0034053C /* ReportPostRequest.swift */; }; - 0E906D1325DCEADD0034053C /* ReportCommentRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CD625DCEADC0034053C /* ReportCommentRequest.swift */; }; - 0E906D1425DCEADD0034053C /* EditAttentionRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CD725DCEADC0034053C /* EditAttentionRequest.swift */; }; - 0E906D1525DCEADD0034053C /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CD825DCEADC0034053C /* Request.swift */; }; - 0E906D1625DCEADD0034053C /* SendPostRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CDA25DCEADC0034053C /* SendPostRequest.swift */; }; - 0E906D1725DCEADD0034053C /* SendCommentRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CDB25DCEADC0034053C /* SendCommentRequest.swift */; }; - 0E906D1825DCEADD0034053C /* SendVoteRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CDC25DCEADC0034053C /* SendVoteRequest.swift */; }; - 0E906D1925DCEADD0034053C /* RequestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CDD25DCEADC0034053C /* RequestError.swift */; }; - 0E906D1A25DCEADD0034053C /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CDF25DCEADC0034053C /* Comment.swift */; }; - 0E906D1B25DCEADD0034053C /* SystemMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CE025DCEADC0034053C /* SystemMessage.swift */; }; - 0E906D1C25DCEADD0034053C /* PushNotificationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CE125DCEADC0034053C /* PushNotificationType.swift */; }; - 0E906D1D25DCEADD0034053C /* Post.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CE225DCEADC0034053C /* Post.swift */; }; - 0E906D1E25DCEADD0034053C /* DeviceInformationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CE325DCEADC0034053C /* DeviceInformationType.swift */; }; - 0E906D1F25DCEADD0034053C /* HollowType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CE425DCEADC0034053C /* HollowType.swift */; }; - 0E906D2025DCEADD0034053C /* VoteData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CE625DCEADC0034053C /* VoteData.swift */; }; - 0E906D2125DCEADD0034053C /* HollowImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CE725DCEADC0034053C /* HollowImage.swift */; }; - 0E906D2325DCEADD0034053C /* CommentData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CE925DCEADC0034053C /* CommentData.swift */; }; - 0E906D2425DCEADD0034053C /* PostData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CEA25DCEADC0034053C /* PostData.swift */; }; - 0E906D2525DCEADD0034053C /* String+SHA256.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CEC25DCEADC0034053C /* String+SHA256.swift */; }; - 0E906D2625DCEADD0034053C /* Data+hexEncodedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CED25DCEADC0034053C /* Data+hexEncodedString.swift */; }; - 0E906D2725DCEADD0034053C /* String+regex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CEE25DCEADC0034053C /* String+regex.swift */; }; - 0E906D2925DCEADD0034053C /* DefaultsKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CF025DCEADC0034053C /* DefaultsKeys.swift */; }; - 0E906D2A25DCEADD0034053C /* Int+bool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CF125DCEADC0034053C /* Int+bool.swift */; }; - 0E906D2B25DCEADD0034053C /* String+removeLineBreak.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CF225DCEADC0034053C /* String+removeLineBreak.swift */; }; - 0E906D2C25DCEADD0034053C /* Int+string.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CF325DCEADC0034053C /* Int+string.swift */; }; - 0E906D2D25DCEADD0034053C /* Bool+int.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CF425DCEADC0034053C /* Bool+int.swift */; }; - 0E906D2E25DCEADD0034053C /* String+Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CF525DCEADC0034053C /* String+Date.swift */; }; - 0E906D3025DCEADD0034053C /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CF725DCEADD0034053C /* Constants.swift */; }; - 0E906D3125DCEADD0034053C /* ImageCompressor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CF925DCEADD0034053C /* ImageCompressor.swift */; }; - 0E906D3225DCEADD0034053C /* ImageSaver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906CFA25DCEADD0034053C /* ImageSaver.swift */; }; - 0E906D5025DD18050034053C /* ImageCompressStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E906D4F25DD18050034053C /* ImageCompressStore.swift */; }; 0E95A1E6262C7F9300079050 /* Toast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E95A1E5262C7F9300079050 /* Toast.swift */; }; - 0EA2D50C25EF8125003D539E /* MessageStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA2D50B25EF8125003D539E /* MessageStore.swift */; }; 0EA2D50F25EF9F41003D539E /* SearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA2D50E25EF9F41003D539E /* SearchBar.swift */; }; 0EA2D51225F0A53F003D539E /* View+swipeToDismiss.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA2D51125F0A53F003D539E /* View+swipeToDismiss.swift */; }; 0EA2D51A25F0FA05003D539E /* View+defaultBlurBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA2D51925F0FA05003D539E /* View+defaultBlurBackground.swift */; }; 0EA2D51D25F11EA9003D539E /* ReportMenuContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA2D51C25F11EA9003D539E /* ReportMenuContent.swift */; }; 0EA3C15E25FEFBAA00865B4D /* View+singleLineText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA3C15D25FEFBAA00865B4D /* View+singleLineText.swift */; }; - 0EA3C16225FEFCA600865B4D /* DeviceModelUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA3C16125FEFCA600865B4D /* DeviceModelUtilities.swift */; }; - 0EA4298B25EB6F4D00B74D5D /* Double+int.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA4298A25EB6F4D00B74D5D /* Double+int.swift */; }; - 0EA4298E25EBBB8B00B74D5D /* DateExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA4298D25EBBB8B00B74D5D /* DateExtensions.swift */; }; 0EA6E98B260DD9FE00A151D7 /* MainView_iPad.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA6E98A260DD9FE00A151D7 /* MainView_iPad.swift */; }; - 0EAA1B3B25E511BA00904296 /* PublisherExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EAA1B3A25E511BA00904296 /* PublisherExtensions.swift */; }; - 0EAA1B4725E6422E00904296 /* Request+publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EAA1B4625E6422E00904296 /* Request+publisher.swift */; }; 0EAA1B4B25E6562B00904296 /* View+flash.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EAA1B4A25E6562B00904296 /* View+flash.swift */; }; 0EAA1B4F25E6971500904296 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0EAA1B4D25E6971500904296 /* Localizable.strings */; }; 0EAA1B5825E7694400904296 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0EAA1B5625E7694400904296 /* InfoPlist.strings */; }; @@ -183,21 +101,213 @@ 0EAC359025E8CCA7000E9F36 /* HollowDetailSubViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EAC358F25E8CCA7000E9F36 /* HollowDetailSubViews.swift */; }; 0EB45E3E261209A300409227 /* View+presentStyledAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB45E3D261209A300409227 /* View+presentStyledAlert.swift */; }; 0EB4F9A225F37A0E00BDAFF1 /* Text+highlight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB4F9A125F37A0E00BDAFF1 /* Text+highlight.swift */; }; - 0EB4F9A625F3837C00BDAFF1 /* Array+sortedRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB4F9A525F3837C00BDAFF1 /* Array+sortedRange.swift */; }; + 0EC244C5263400C6001AFC4B /* HollowMacApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC244C4263400C6001AFC4B /* HollowMacApp.swift */; }; + 0EC244C7263400C6001AFC4B /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC244C6263400C6001AFC4B /* ContentView.swift */; }; + 0EC244C9263400C7001AFC4B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0EC244C8263400C7001AFC4B /* Assets.xcassets */; }; + 0EC244E026340172001AFC4B /* CrossPlatformTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC244DE26340172001AFC4B /* CrossPlatformTypes.swift */; }; + 0EC244E126340172001AFC4B /* CrossPlatformTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC244DE26340172001AFC4B /* CrossPlatformTypes.swift */; }; + 0EC244E226340172001AFC4B /* CrossPlatformImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC244DF26340172001AFC4B /* CrossPlatformImage.swift */; }; + 0EC244E326340172001AFC4B /* CrossPlatformImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC244DF26340172001AFC4B /* CrossPlatformImage.swift */; }; + 0EC244EB263401E4001AFC4B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC244EA263401E4001AFC4B /* AppDelegate.swift */; }; + 0EC244F626340211001AFC4B /* AppModelBehaviour.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC244F226340211001AFC4B /* AppModelBehaviour.swift */; }; + 0EC244F826340211001AFC4B /* AppModelEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC244F326340211001AFC4B /* AppModelEnvironment.swift */; }; + 0EC244FA26340211001AFC4B /* AppModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC244F426340211001AFC4B /* AppModel.swift */; }; + 0EC244FC26340211001AFC4B /* AppDelegateShared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC244F526340211001AFC4B /* AppDelegateShared.swift */; }; + 0EC244FD26340211001AFC4B /* AppDelegateShared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC244F526340211001AFC4B /* AppDelegateShared.swift */; }; + 0EC245032634022C001AFC4B /* HollowApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC245012634022C001AFC4B /* HollowApp.swift */; }; + 0EC245042634022C001AFC4B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC245022634022C001AFC4B /* AppDelegate.swift */; }; + 0EC245092634029D001AFC4B /* WaterfallGrid in Frameworks */ = {isa = PBXBuildFile; productRef = 0EC245082634029D001AFC4B /* WaterfallGrid */; }; + 0EC2450B2634029D001AFC4B /* Defaults in Frameworks */ = {isa = PBXBuildFile; productRef = 0EC2450A2634029D001AFC4B /* Defaults */; }; + 0EC2450D2634029D001AFC4B /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 0EC2450C2634029D001AFC4B /* Alamofire */; }; + 0EC2450F2634029D001AFC4B /* AppCenterAnalytics in Frameworks */ = {isa = PBXBuildFile; productRef = 0EC2450E2634029D001AFC4B /* AppCenterAnalytics */; }; + 0EC245112634029D001AFC4B /* AppCenterCrashes in Frameworks */ = {isa = PBXBuildFile; productRef = 0EC245102634029D001AFC4B /* AppCenterCrashes */; }; + 0EC245132634029D001AFC4B /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 0EC245122634029D001AFC4B /* Kingfisher */; }; + 0EC245152634029D001AFC4B /* Cache in Frameworks */ = {isa = PBXBuildFile; productRef = 0EC245142634029D001AFC4B /* Cache */; }; + 0EC2452226340301001AFC4B /* UIDevice+isPad.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2451C26340301001AFC4B /* UIDevice+isPad.swift */; }; + 0EC2452326340301001AFC4B /* ConnectivityPublisherExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2451D26340301001AFC4B /* ConnectivityPublisherExtensions.swift */; }; + 0EC2452426340301001AFC4B /* ConnectivityStatus+isConnected.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2451E26340301001AFC4B /* ConnectivityStatus+isConnected.swift */; }; + 0EC2452526340301001AFC4B /* ImageCompressor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2452026340301001AFC4B /* ImageCompressor.swift */; }; + 0EC2452626340301001AFC4B /* ImageSaver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2452126340301001AFC4B /* ImageSaver.swift */; }; + 0EC2457926340311001AFC4B /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2452A26340311001AFC4B /* Constants.swift */; }; + 0EC2457A26340311001AFC4B /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2452A26340311001AFC4B /* Constants.swift */; }; + 0EC2457B26340311001AFC4B /* HollowConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2452C26340311001AFC4B /* HollowConfig.swift */; }; + 0EC2457C26340311001AFC4B /* HollowConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2452C26340311001AFC4B /* HollowConfig.swift */; }; + 0EC2457D26340311001AFC4B /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2452D26340311001AFC4B /* Comment.swift */; }; + 0EC2457E26340311001AFC4B /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2452D26340311001AFC4B /* Comment.swift */; }; + 0EC2457F26340311001AFC4B /* ImageMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2452E26340311001AFC4B /* ImageMetadata.swift */; }; + 0EC2458026340311001AFC4B /* ImageMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2452E26340311001AFC4B /* ImageMetadata.swift */; }; + 0EC2458126340311001AFC4B /* SystemMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2452F26340311001AFC4B /* SystemMessage.swift */; }; + 0EC2458226340311001AFC4B /* SystemMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2452F26340311001AFC4B /* SystemMessage.swift */; }; + 0EC2458326340311001AFC4B /* PushNotificationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2453026340311001AFC4B /* PushNotificationType.swift */; }; + 0EC2458426340311001AFC4B /* PushNotificationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2453026340311001AFC4B /* PushNotificationType.swift */; }; + 0EC2458526340311001AFC4B /* Post.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2453126340311001AFC4B /* Post.swift */; }; + 0EC2458626340311001AFC4B /* Post.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2453126340311001AFC4B /* Post.swift */; }; + 0EC2458726340311001AFC4B /* Vote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2453226340311001AFC4B /* Vote.swift */; }; + 0EC2458826340311001AFC4B /* Vote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2453226340311001AFC4B /* Vote.swift */; }; + 0EC2458926340311001AFC4B /* DeviceInformationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2453326340311001AFC4B /* DeviceInformationType.swift */; }; + 0EC2458A26340311001AFC4B /* DeviceInformationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2453326340311001AFC4B /* DeviceInformationType.swift */; }; + 0EC2458B26340311001AFC4B /* VoteData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2453526340311001AFC4B /* VoteData.swift */; }; + 0EC2458C26340311001AFC4B /* VoteData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2453526340311001AFC4B /* VoteData.swift */; }; + 0EC2458D26340311001AFC4B /* HollowImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2453626340311001AFC4B /* HollowImage.swift */; }; + 0EC2458E26340311001AFC4B /* HollowImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2453626340311001AFC4B /* HollowImage.swift */; }; + 0EC2458F26340311001AFC4B /* CommentData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2453726340311001AFC4B /* CommentData.swift */; }; + 0EC2459026340311001AFC4B /* CommentData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2453726340311001AFC4B /* CommentData.swift */; }; + 0EC2459126340311001AFC4B /* PostData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2453826340311001AFC4B /* PostData.swift */; }; + 0EC2459226340311001AFC4B /* PostData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2453826340311001AFC4B /* PostData.swift */; }; + 0EC2459326340311001AFC4B /* HollowType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2453926340311001AFC4B /* HollowType.swift */; }; + 0EC2459426340311001AFC4B /* HollowType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2453926340311001AFC4B /* HollowType.swift */; }; + 0EC2459526340311001AFC4B /* PostDetailRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2453C26340311001AFC4B /* PostDetailRequest.swift */; }; + 0EC2459626340311001AFC4B /* PostDetailRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2453C26340311001AFC4B /* PostDetailRequest.swift */; }; + 0EC2459726340311001AFC4B /* SearchHistoryRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2453D26340311001AFC4B /* SearchHistoryRequest.swift */; }; + 0EC2459826340311001AFC4B /* SearchHistoryRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2453D26340311001AFC4B /* SearchHistoryRequest.swift */; }; + 0EC2459926340311001AFC4B /* SearchRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2453E26340311001AFC4B /* SearchRequest.swift */; }; + 0EC2459A26340311001AFC4B /* SearchRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2453E26340311001AFC4B /* SearchRequest.swift */; }; + 0EC2459B26340311001AFC4B /* PostListRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2453F26340311001AFC4B /* PostListRequest.swift */; }; + 0EC2459C26340311001AFC4B /* PostListRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2453F26340311001AFC4B /* PostListRequest.swift */; }; + 0EC2459D26340311001AFC4B /* SystemMessageRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2454026340311001AFC4B /* SystemMessageRequest.swift */; }; + 0EC2459E26340311001AFC4B /* SystemMessageRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2454026340311001AFC4B /* SystemMessageRequest.swift */; }; + 0EC2459F26340311001AFC4B /* AttentionListRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2454126340311001AFC4B /* AttentionListRequest.swift */; }; + 0EC245A026340311001AFC4B /* AttentionListRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2454126340311001AFC4B /* AttentionListRequest.swift */; }; + 0EC245A126340311001AFC4B /* RandomListRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2454226340311001AFC4B /* RandomListRequest.swift */; }; + 0EC245A226340311001AFC4B /* RandomListRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2454226340311001AFC4B /* RandomListRequest.swift */; }; + 0EC245A326340311001AFC4B /* AttentionListSearchRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2454326340311001AFC4B /* AttentionListSearchRequest.swift */; }; + 0EC245A426340311001AFC4B /* AttentionListSearchRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2454326340311001AFC4B /* AttentionListSearchRequest.swift */; }; + 0EC245A526340311001AFC4B /* DefaultRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2454426340311001AFC4B /* DefaultRequest.swift */; }; + 0EC245A626340311001AFC4B /* DefaultRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2454426340311001AFC4B /* DefaultRequest.swift */; }; + 0EC245A726340311001AFC4B /* GetConfigRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2454626340311001AFC4B /* GetConfigRequest.swift */; }; + 0EC245A826340311001AFC4B /* GetConfigRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2454626340311001AFC4B /* GetConfigRequest.swift */; }; + 0EC245A926340311001AFC4B /* GetPushRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2454726340311001AFC4B /* GetPushRequest.swift */; }; + 0EC245AA26340311001AFC4B /* GetPushRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2454726340311001AFC4B /* GetPushRequest.swift */; }; + 0EC245AB26340311001AFC4B /* SetPushRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2454826340311001AFC4B /* SetPushRequest.swift */; }; + 0EC245AC26340311001AFC4B /* SetPushRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2454826340311001AFC4B /* SetPushRequest.swift */; }; + 0EC245AD26340311001AFC4B /* LoginRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2454A26340311001AFC4B /* LoginRequest.swift */; }; + 0EC245AE26340311001AFC4B /* LoginRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2454A26340311001AFC4B /* LoginRequest.swift */; }; + 0EC245AF26340311001AFC4B /* LogoutRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2454B26340311001AFC4B /* LogoutRequest.swift */; }; + 0EC245B026340311001AFC4B /* LogoutRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2454B26340311001AFC4B /* LogoutRequest.swift */; }; + 0EC245B126340311001AFC4B /* EmailCheckRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2454C26340311001AFC4B /* EmailCheckRequest.swift */; }; + 0EC245B226340311001AFC4B /* EmailCheckRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2454C26340311001AFC4B /* EmailCheckRequest.swift */; }; + 0EC245B326340311001AFC4B /* DeviceListRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2454D26340311001AFC4B /* DeviceListRequest.swift */; }; + 0EC245B426340311001AFC4B /* DeviceListRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2454D26340311001AFC4B /* DeviceListRequest.swift */; }; + 0EC245B526340311001AFC4B /* UpdateDeviceTokenRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2454E26340311001AFC4B /* UpdateDeviceTokenRequest.swift */; }; + 0EC245B626340311001AFC4B /* UpdateDeviceTokenRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2454E26340311001AFC4B /* UpdateDeviceTokenRequest.swift */; }; + 0EC245B726340311001AFC4B /* DeviceTerminationRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2454F26340311001AFC4B /* DeviceTerminationRequest.swift */; }; + 0EC245B826340311001AFC4B /* DeviceTerminationRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2454F26340311001AFC4B /* DeviceTerminationRequest.swift */; }; + 0EC245B926340311001AFC4B /* AccountCreationRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2455026340311001AFC4B /* AccountCreationRequest.swift */; }; + 0EC245BA26340312001AFC4B /* AccountCreationRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2455026340311001AFC4B /* AccountCreationRequest.swift */; }; + 0EC245BB26340312001AFC4B /* ChangePasswordRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2455126340311001AFC4B /* ChangePasswordRequest.swift */; }; + 0EC245BC26340312001AFC4B /* ChangePasswordRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2455126340311001AFC4B /* ChangePasswordRequest.swift */; }; + 0EC245BD26340312001AFC4B /* PostListRequestGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2455326340311001AFC4B /* PostListRequestGroup.swift */; }; + 0EC245BE26340312001AFC4B /* PostListRequestGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2455326340311001AFC4B /* PostListRequestGroup.swift */; }; + 0EC245BF26340312001AFC4B /* FetchImageRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2455426340311001AFC4B /* FetchImageRequest.swift */; }; + 0EC245C026340312001AFC4B /* FetchImageRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2455426340311001AFC4B /* FetchImageRequest.swift */; }; + 0EC245C126340312001AFC4B /* ReportRequestGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2455526340311001AFC4B /* ReportRequestGroup.swift */; }; + 0EC245C226340312001AFC4B /* ReportRequestGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2455526340311001AFC4B /* ReportRequestGroup.swift */; }; + 0EC245C326340312001AFC4B /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 0EC2455626340311001AFC4B /* README.md */; }; + 0EC245C426340312001AFC4B /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 0EC2455626340311001AFC4B /* README.md */; }; + 0EC245C526340312001AFC4B /* ReportPostRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2455826340311001AFC4B /* ReportPostRequest.swift */; }; + 0EC245C626340312001AFC4B /* ReportPostRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2455826340311001AFC4B /* ReportPostRequest.swift */; }; + 0EC245C726340312001AFC4B /* ReportCommentRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2455926340311001AFC4B /* ReportCommentRequest.swift */; }; + 0EC245C826340312001AFC4B /* ReportCommentRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2455926340311001AFC4B /* ReportCommentRequest.swift */; }; + 0EC245C926340312001AFC4B /* EditAttentionRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2455A26340311001AFC4B /* EditAttentionRequest.swift */; }; + 0EC245CA26340312001AFC4B /* EditAttentionRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2455A26340311001AFC4B /* EditAttentionRequest.swift */; }; + 0EC245CB26340312001AFC4B /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2455B26340311001AFC4B /* Request.swift */; }; + 0EC245CC26340312001AFC4B /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2455B26340311001AFC4B /* Request.swift */; }; + 0EC245CD26340312001AFC4B /* SendPostRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2455D26340311001AFC4B /* SendPostRequest.swift */; }; + 0EC245CE26340312001AFC4B /* SendPostRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2455D26340311001AFC4B /* SendPostRequest.swift */; }; + 0EC245CF26340312001AFC4B /* SendCommentRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2455E26340311001AFC4B /* SendCommentRequest.swift */; }; + 0EC245D026340312001AFC4B /* SendCommentRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2455E26340311001AFC4B /* SendCommentRequest.swift */; }; + 0EC245D126340312001AFC4B /* SendVoteRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2455F26340311001AFC4B /* SendVoteRequest.swift */; }; + 0EC245D226340312001AFC4B /* SendVoteRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2455F26340311001AFC4B /* SendVoteRequest.swift */; }; + 0EC245D326340312001AFC4B /* Request+publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456026340311001AFC4B /* Request+publisher.swift */; }; + 0EC245D426340312001AFC4B /* Request+publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456026340311001AFC4B /* Request+publisher.swift */; }; + 0EC245D526340312001AFC4B /* RequestPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456126340311001AFC4B /* RequestPublisher.swift */; }; + 0EC245D626340312001AFC4B /* RequestPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456126340311001AFC4B /* RequestPublisher.swift */; }; + 0EC245D726340312001AFC4B /* RequestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456226340311001AFC4B /* RequestError.swift */; }; + 0EC245D826340312001AFC4B /* RequestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456226340311001AFC4B /* RequestError.swift */; }; + 0EC245D926340312001AFC4B /* String+SHA256.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456426340311001AFC4B /* String+SHA256.swift */; }; + 0EC245DA26340312001AFC4B /* String+SHA256.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456426340311001AFC4B /* String+SHA256.swift */; }; + 0EC245DB26340312001AFC4B /* Data+hexEncodedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456526340311001AFC4B /* Data+hexEncodedString.swift */; }; + 0EC245DC26340312001AFC4B /* Data+hexEncodedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456526340311001AFC4B /* Data+hexEncodedString.swift */; }; + 0EC245DD26340312001AFC4B /* DefaultsKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456626340311001AFC4B /* DefaultsKeys.swift */; }; + 0EC245DE26340312001AFC4B /* DefaultsKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456626340311001AFC4B /* DefaultsKeys.swift */; }; + 0EC245DF26340312001AFC4B /* Int+bool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456726340311001AFC4B /* Int+bool.swift */; }; + 0EC245E026340312001AFC4B /* Int+bool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456726340311001AFC4B /* Int+bool.swift */; }; + 0EC245E126340312001AFC4B /* String+removeLineBreak.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456826340311001AFC4B /* String+removeLineBreak.swift */; }; + 0EC245E226340312001AFC4B /* String+removeLineBreak.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456826340311001AFC4B /* String+removeLineBreak.swift */; }; + 0EC245E326340312001AFC4B /* Int+string.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456926340311001AFC4B /* Int+string.swift */; }; + 0EC245E426340312001AFC4B /* Int+string.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456926340311001AFC4B /* Int+string.swift */; }; + 0EC245E526340312001AFC4B /* Array+removeDuplicates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456A26340311001AFC4B /* Array+removeDuplicates.swift */; }; + 0EC245E626340312001AFC4B /* Array+removeDuplicates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456A26340311001AFC4B /* Array+removeDuplicates.swift */; }; + 0EC245E726340312001AFC4B /* String+regex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456B26340311001AFC4B /* String+regex.swift */; }; + 0EC245E826340312001AFC4B /* String+regex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456B26340311001AFC4B /* String+regex.swift */; }; + 0EC245E926340312001AFC4B /* Double+int.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456C26340311001AFC4B /* Double+int.swift */; }; + 0EC245EA26340312001AFC4B /* Double+int.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456C26340311001AFC4B /* Double+int.swift */; }; + 0EC245EB26340312001AFC4B /* Array+sortedRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456D26340311001AFC4B /* Array+sortedRange.swift */; }; + 0EC245EC26340312001AFC4B /* Array+sortedRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456D26340311001AFC4B /* Array+sortedRange.swift */; }; + 0EC245ED26340312001AFC4B /* Bool+int.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456E26340311001AFC4B /* Bool+int.swift */; }; + 0EC245EE26340312001AFC4B /* Bool+int.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456E26340311001AFC4B /* Bool+int.swift */; }; + 0EC245EF26340312001AFC4B /* String+Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456F26340311001AFC4B /* String+Date.swift */; }; + 0EC245F026340312001AFC4B /* String+Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2456F26340311001AFC4B /* String+Date.swift */; }; + 0EC245F126340312001AFC4B /* DateExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2457026340311001AFC4B /* DateExtensions.swift */; }; + 0EC245F226340312001AFC4B /* DateExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2457026340311001AFC4B /* DateExtensions.swift */; }; + 0EC245F326340312001AFC4B /* PublisherExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2457126340311001AFC4B /* PublisherExtensions.swift */; }; + 0EC245F426340312001AFC4B /* PublisherExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2457126340311001AFC4B /* PublisherExtensions.swift */; }; + 0EC245F526340312001AFC4B /* HollowDateFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2457326340311001AFC4B /* HollowDateFormatter.swift */; }; + 0EC245F626340312001AFC4B /* HollowDateFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2457326340311001AFC4B /* HollowDateFormatter.swift */; }; + 0EC245F726340312001AFC4B /* AvatarGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2457426340311001AFC4B /* AvatarGenerator.swift */; }; + 0EC245F826340312001AFC4B /* AvatarGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2457426340311001AFC4B /* AvatarGenerator.swift */; }; + 0EC245F926340312001AFC4B /* OpenURLHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2457526340311001AFC4B /* OpenURLHelper.swift */; }; + 0EC245FA26340312001AFC4B /* OpenURLHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2457526340311001AFC4B /* OpenURLHelper.swift */; }; + 0EC245FB26340312001AFC4B /* LineSwitchManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2457626340311001AFC4B /* LineSwitchManager.swift */; }; + 0EC245FC26340312001AFC4B /* LineSwitchManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2457626340311001AFC4B /* LineSwitchManager.swift */; }; + 0EC245FD26340312001AFC4B /* DeviceModelUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2457726340311001AFC4B /* DeviceModelUtilities.swift */; }; + 0EC245FE26340312001AFC4B /* DeviceModelUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2457726340311001AFC4B /* DeviceModelUtilities.swift */; }; + 0EC245FF26340312001AFC4B /* PostCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2457826340311001AFC4B /* PostCache.swift */; }; + 0EC2460026340312001AFC4B /* PostCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2457826340311001AFC4B /* PostCache.swift */; }; + 0EC2460926340358001AFC4B /* ViewConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E415AE925CD772200351672 /* ViewConstants.swift */; }; + 0EC2461E2634040D001AFC4B /* ImageCompressStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC246112634040D001AFC4B /* ImageCompressStore.swift */; }; + 0EC2461F2634040D001AFC4B /* ImageCompressStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC246112634040D001AFC4B /* ImageCompressStore.swift */; }; + 0EC246202634040D001AFC4B /* HollowInputStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC246122634040D001AFC4B /* HollowInputStore.swift */; }; + 0EC246212634040D001AFC4B /* HollowInputStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC246122634040D001AFC4B /* HollowInputStore.swift */; }; + 0EC246222634040D001AFC4B /* PostListRequestStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC246132634040D001AFC4B /* PostListRequestStore.swift */; }; + 0EC246232634040D001AFC4B /* PostListRequestStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC246132634040D001AFC4B /* PostListRequestStore.swift */; }; + 0EC246242634040D001AFC4B /* HollowDetailStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC246142634040D001AFC4B /* HollowDetailStore.swift */; }; + 0EC246252634040D001AFC4B /* HollowDetailStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC246142634040D001AFC4B /* HollowDetailStore.swift */; }; + 0EC246262634040D001AFC4B /* AccountInfoStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC246162634040D001AFC4B /* AccountInfoStore.swift */; }; + 0EC246272634040D001AFC4B /* AccountInfoStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC246162634040D001AFC4B /* AccountInfoStore.swift */; }; + 0EC246282634040D001AFC4B /* DeviceListStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC246172634040D001AFC4B /* DeviceListStore.swift */; }; + 0EC246292634040D001AFC4B /* DeviceListStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC246172634040D001AFC4B /* DeviceListStore.swift */; }; + 0EC2462A2634040D001AFC4B /* MessageStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC246182634040D001AFC4B /* MessageStore.swift */; }; + 0EC2462B2634040D001AFC4B /* MessageStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC246182634040D001AFC4B /* MessageStore.swift */; }; + 0EC2462C2634040D001AFC4B /* WelcomeStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2461A2634040D001AFC4B /* WelcomeStore.swift */; }; + 0EC2462D2634040D001AFC4B /* WelcomeStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2461A2634040D001AFC4B /* WelcomeStore.swift */; }; + 0EC2462E2634040D001AFC4B /* LoginStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2461B2634040D001AFC4B /* LoginStore.swift */; }; + 0EC2462F2634040D001AFC4B /* LoginStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2461B2634040D001AFC4B /* LoginStore.swift */; }; + 0EC2463426340481001AFC4B /* ImageCompressor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2452026340301001AFC4B /* ImageCompressor.swift */; }; + 0EC2463F26340770001AFC4B /* AppModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2463E26340770001AFC4B /* AppModel.swift */; }; + 0EC24649263407D3001AFC4B /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC24648263407D3001AFC4B /* WelcomeView.swift */; }; + 0EC2464D263407E4001AFC4B /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2464C263407E4001AFC4B /* LoginView.swift */; }; + 0EC24651263408CC001AFC4B /* AppModelEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC24650263408CC001AFC4B /* AppModelEnvironment.swift */; }; + 0EC2465626340B71001AFC4B /* View+showAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2465526340B71001AFC4B /* View+showAlert.swift */; }; + 0EC2465C2634116A001AFC4B /* AppModelBehaviour.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2465B2634116A001AFC4B /* AppModelBehaviour.swift */; }; 0EC2AE36260F761A0099B500 /* View+conditionalSizeCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC2AE35260F761A0099B500 /* View+conditionalSizeCategory.swift */; }; 0EC4B67825D644CA0065BB82 /* CustomTextEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC4B67725D644CA0065BB82 /* CustomTextEditor.swift */; }; 0EC4B67F25D670070065BB82 /* ImagePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC4B67E25D670070065BB82 /* ImagePicker.swift */; }; + 0ED087BF263480C9003C2E11 /* LoginSubViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED087BE263480C9003C2E11 /* LoginSubViews.swift */; }; + 0ED087D126354556003C2E11 /* PostListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED087D026354556003C2E11 /* PostListView.swift */; }; + 0ED1A16026346C37001453A7 /* ReCAPTCHAWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED1A15F26346C37001453A7 /* ReCAPTCHAWebView.swift */; }; + 0ED1A16126346C37001453A7 /* ReCAPTCHAWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED1A15F26346C37001453A7 /* ReCAPTCHAWebView.swift */; }; + 0ED1A16726346CB1001453A7 /* ReCAPTCHAPageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED1A16626346CB1001453A7 /* ReCAPTCHAPageView.swift */; }; + 0ED1A16826346CB1001453A7 /* ReCAPTCHAPageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED1A16626346CB1001453A7 /* ReCAPTCHAPageView.swift */; }; + 0ED1A16B26346D28001453A7 /* ViewLayouts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E415AF625CD772200351672 /* ViewLayouts.swift */; }; 0ED82F0026103FB600A799C0 /* View+defaultPadding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED82EFF26103FB600A799C0 /* View+defaultPadding.swift */; }; 0EDA262725D22C8600B67292 /* DeviceListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDA262625D22C8600B67292 /* DeviceListView.swift */; }; - 0EDA262B25D22CBF00B67292 /* DeviceListStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDA262A25D22CBF00B67292 /* DeviceListStore.swift */; }; 0EDA263125D2465100B67292 /* LoadingIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDA263025D2465100B67292 /* LoadingIndicator.swift */; }; 0EE60EFD25FCCCD700D97E7A /* CheckmarkButtonImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EE60EFC25FCCCD700D97E7A /* CheckmarkButtonImage.swift */; }; 0EF10F6626330F29004D6538 /* HollowDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EF10F6526330F29004D6538 /* HollowDetailViewController.swift */; }; 0EFA3DDE25D3A03D0003C76B /* LoadingLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFA3DDD25D3A03D0003C76B /* LoadingLabel.swift */; }; 0EFB54DA25E8CF4B001BC98A /* HollowInputComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFB54D925E8CF4B001BC98A /* HollowInputComponents.swift */; }; 0EFB54E525E8D9FD001BC98A /* View+modalPresent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFB54E425E8D9FD001BC98A /* View+modalPresent.swift */; }; - 0EFB54EC25E90B55001BC98A /* ConnectivityStatus+isConnected.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFB54EB25E90B55001BC98A /* ConnectivityStatus+isConnected.swift */; }; - 0EFB54F425E926A1001BC98A /* PostListRequestGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFB54F325E926A1001BC98A /* PostListRequestGroup.swift */; }; 0EFB54FA25E92CB0001BC98A /* SearchSubViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFB54F925E92CB0001BC98A /* SearchSubViews.swift */; }; 0EFB550525E93E24001BC98A /* PostListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFB550425E93E24001BC98A /* PostListView.swift */; }; 0EFE13F4262876FE007B0045 /* View+imageSaver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFE13F3262876FE007B0045 /* View+imageSaver.swift */; }; @@ -218,45 +328,29 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 00AA103425E74212006B3D6D /* LineSwitchManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineSwitchManager.swift; sourceTree = ""; }; - 00B014EC25E1205A004E416E /* PostCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostCache.swift; sourceTree = ""; }; - 00EFC07025DFC71500536B52 /* Vote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Vote.swift; sourceTree = ""; }; - 00EFC07325DFC73800536B52 /* ImageMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageMetadata.swift; sourceTree = ""; }; 0E09B7A925D4D73100DC5615 /* ImageViewer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageViewer.swift; sourceTree = ""; }; 0E09B7B025D4E7E800DC5615 /* BarImageButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarImageButton.swift; sourceTree = ""; }; - 0E09B7BA25D52F4F00DC5615 /* HollowInputStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HollowInputStore.swift; sourceTree = ""; }; 0E186C38262C60A1001A5855 /* View+showToast.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "View+showToast.swift"; sourceTree = ""; }; 0E1DD7D525D983BE000D27BA /* Avatar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Avatar.swift; sourceTree = ""; }; - 0E1DD7D825DA4B91000D27BA /* AppModelBehaviour.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppModelBehaviour.swift; sourceTree = ""; }; - 0E1DD7DB25DA4BE5000D27BA /* AppModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppModel.swift; sourceTree = ""; }; - 0E1DD7DE25DA4E5C000D27BA /* AppModelEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppModelEnvironment.swift; sourceTree = ""; }; 0E28BC4125F5E29D00A97820 /* CustomColorScheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomColorScheme.swift; sourceTree = ""; }; 0E28BC4425F5F8A000A97820 /* View+defaultListStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+defaultListStyle.swift"; sourceTree = ""; }; - 0E28BC4925F60EB400A97820 /* AccountInfoStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountInfoStore.swift; sourceTree = ""; }; - 0E294C0C25E2570D00A9875A /* HollowDetailStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HollowDetailStore.swift; sourceTree = ""; }; - 0E294E9625F1F448001E4C08 /* ReportRequestGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportRequestGroup.swift; sourceTree = ""; }; 0E294E9B25F2405C001E4C08 /* View+roundedCorner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+roundedCorner.swift"; sourceTree = ""; }; 0E2C1EE325D7A04C00A296E9 /* MyButtonDefaultStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyButtonDefaultStyle.swift; sourceTree = ""; }; 0E36F53F25D0E02F001AE852 /* GetSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetSize.swift; sourceTree = ""; }; 0E36F54525D0F0FB001AE852 /* GetFrame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetFrame.swift; sourceTree = ""; }; 0E386D8D2613101B000568B6 /* View+refreshNavigationItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+refreshNavigationItem.swift"; sourceTree = ""; }; - 0E3AC3FF25F7778400A4278A /* AvatarGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarGenerator.swift; sourceTree = ""; }; 0E3DA2A725FF379000ECEFAC /* ProviderInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProviderInfoView.swift; sourceTree = ""; }; 0E3E694A260DF2C8005E8DA2 /* HollowDetailView_iPad.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HollowDetailView_iPad.swift; sourceTree = ""; }; 0E3E694D260DFCE8005E8DA2 /* Text+dateText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Text+dateText.swift"; sourceTree = ""; }; - 0E3E6954260E08EC005E8DA2 /* UIDevice+isPad.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIDevice+isPad.swift"; sourceTree = ""; }; 0E3E6957260E1312005E8DA2 /* MainSubViews_iPad.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainSubViews_iPad.swift; sourceTree = ""; }; - 0E3E695A260E166D005E8DA2 /* HollowDateFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HollowDateFormatter.swift; sourceTree = ""; }; 0E3E695F260E3676005E8DA2 /* SplitView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitView.swift; sourceTree = ""; }; 0E40F74725D7C997003A2342 /* HollowInputSubViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HollowInputSubViews.swift; sourceTree = ""; }; 0E415A7025CD769B00351672 /* Hollow.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Hollow.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 0E415A7325CD769B00351672 /* HollowApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HollowApp.swift; sourceTree = ""; }; 0E415A7725CD769F00351672 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 0E415A7C25CD769F00351672 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 0E415AE925CD772200351672 /* ViewConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewConstants.swift; sourceTree = ""; }; 0E415AEB25CD772200351672 /* ErrorAlert.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErrorAlert.swift; sourceTree = ""; }; 0E415AED25CD772200351672 /* CustomTabView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomTabView.swift; sourceTree = ""; }; - 0E415AEE25CD772200351672 /* ReCAPTCHAWebView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReCAPTCHAWebView.swift; sourceTree = ""; }; 0E415AEF25CD772200351672 /* CustomScrollView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomScrollView.swift; sourceTree = ""; }; 0E415AF025CD772200351672 /* Blur.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Blur.swift; sourceTree = ""; }; 0E415AF425CD772200351672 /* Color+uiColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Color+uiColor.swift"; sourceTree = ""; }; @@ -294,16 +388,8 @@ 0E415B2F25CD772200351672 /* LoginView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = ""; }; 0E415B3025CD772200351672 /* LoginSubViews.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginSubViews.swift; sourceTree = ""; }; 0E415B3125CD772200351672 /* WelcomeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = ""; }; - 0E415B3C25CD772200351672 /* PostListRequestStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostListRequestStore.swift; sourceTree = ""; }; - 0E415B4025CD772200351672 /* LoginStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginStore.swift; sourceTree = ""; }; - 0E415B4125CD772200351672 /* WelcomeStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WelcomeStore.swift; sourceTree = ""; }; - 0E415B9A25CD93C000351672 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 0E4284BC25CE2A96008F134A /* Hollow.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Hollow.entitlements; sourceTree = ""; }; - 0E4786C725E3A93E0004F8FA /* RequestPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestPublisher.swift; sourceTree = ""; }; - 0E4786D825E3E1870004F8FA /* FetchImageRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchImageRequest.swift; sourceTree = ""; }; - 0E527CD125F3D84800D91706 /* Array+removeDuplicates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+removeDuplicates.swift"; sourceTree = ""; }; - 0E527CD525F4825700D91706 /* OpenURLHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenURLHelper.swift; sourceTree = ""; }; - 0E58810925E9E61B006F6A94 /* ConnectivityPublisherExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectivityPublisherExtensions.swift; sourceTree = ""; }; + 0E4E4BC42634619A00B32F7F /* View+roundedCorner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+roundedCorner.swift"; sourceTree = ""; }; 0E58810E25EA0F47006F6A94 /* View+presentPopover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+presentPopover.swift"; sourceTree = ""; }; 0E58811925EA674B006F6A94 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = ""; }; 0E58811A25EA674B006F6A94 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; @@ -318,72 +404,13 @@ 0E85DFC425DB71F2003D134B /* IndicatorOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IndicatorOverlay.swift; sourceTree = ""; }; 0E8BCFE225E095A90056D809 /* StyledAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StyledAlert.swift; sourceTree = ""; }; 0E8BCFE625E0A4720056D809 /* IntegrationUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegrationUtilities.swift; sourceTree = ""; }; - 0E8CFBB7260B2E6F0050FDD2 /* HollowConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HollowConfig.swift; sourceTree = ""; }; - 0E906CBB25DCEADC0034053C /* PostDetailRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostDetailRequest.swift; sourceTree = ""; }; - 0E906CBD25DCEADC0034053C /* SearchHistoryRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchHistoryRequest.swift; sourceTree = ""; }; - 0E906CBE25DCEADC0034053C /* SearchRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchRequest.swift; sourceTree = ""; }; - 0E906CBF25DCEADC0034053C /* PostListRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostListRequest.swift; sourceTree = ""; }; - 0E906CC025DCEADC0034053C /* SystemMessageRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SystemMessageRequest.swift; sourceTree = ""; }; - 0E906CC125DCEADC0034053C /* AttentionListRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttentionListRequest.swift; sourceTree = ""; }; - 0E906CC225DCEADC0034053C /* RandomListRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RandomListRequest.swift; sourceTree = ""; }; - 0E906CC425DCEADC0034053C /* AttentionListSearchRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttentionListSearchRequest.swift; sourceTree = ""; }; - 0E906CC525DCEADC0034053C /* DefaultRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultRequest.swift; sourceTree = ""; }; - 0E906CC725DCEADC0034053C /* GetConfigRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetConfigRequest.swift; sourceTree = ""; }; - 0E906CC825DCEADC0034053C /* GetPushRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetPushRequest.swift; sourceTree = ""; }; - 0E906CC925DCEADC0034053C /* SetPushRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SetPushRequest.swift; sourceTree = ""; }; - 0E906CCB25DCEADC0034053C /* LoginRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginRequest.swift; sourceTree = ""; }; - 0E906CCC25DCEADC0034053C /* LogoutRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogoutRequest.swift; sourceTree = ""; }; - 0E906CCD25DCEADC0034053C /* EmailCheckRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmailCheckRequest.swift; sourceTree = ""; }; - 0E906CCE25DCEADC0034053C /* DeviceListRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceListRequest.swift; sourceTree = ""; }; - 0E906CCF25DCEADC0034053C /* UpdateDeviceTokenRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdateDeviceTokenRequest.swift; sourceTree = ""; }; - 0E906CD025DCEADC0034053C /* DeviceTerminationRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceTerminationRequest.swift; sourceTree = ""; }; - 0E906CD125DCEADC0034053C /* AccountCreationRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountCreationRequest.swift; sourceTree = ""; }; - 0E906CD225DCEADC0034053C /* ChangePasswordRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChangePasswordRequest.swift; sourceTree = ""; }; - 0E906CD325DCEADC0034053C /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - 0E906CD525DCEADC0034053C /* ReportPostRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReportPostRequest.swift; sourceTree = ""; }; - 0E906CD625DCEADC0034053C /* ReportCommentRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReportCommentRequest.swift; sourceTree = ""; }; - 0E906CD725DCEADC0034053C /* EditAttentionRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditAttentionRequest.swift; sourceTree = ""; }; - 0E906CD825DCEADC0034053C /* Request.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Request.swift; sourceTree = ""; }; - 0E906CDA25DCEADC0034053C /* SendPostRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendPostRequest.swift; sourceTree = ""; }; - 0E906CDB25DCEADC0034053C /* SendCommentRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendCommentRequest.swift; sourceTree = ""; }; - 0E906CDC25DCEADC0034053C /* SendVoteRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendVoteRequest.swift; sourceTree = ""; }; - 0E906CDD25DCEADC0034053C /* RequestError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestError.swift; sourceTree = ""; }; - 0E906CDF25DCEADC0034053C /* Comment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Comment.swift; sourceTree = ""; }; - 0E906CE025DCEADC0034053C /* SystemMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SystemMessage.swift; sourceTree = ""; }; - 0E906CE125DCEADC0034053C /* PushNotificationType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PushNotificationType.swift; sourceTree = ""; }; - 0E906CE225DCEADC0034053C /* Post.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Post.swift; sourceTree = ""; }; - 0E906CE325DCEADC0034053C /* DeviceInformationType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceInformationType.swift; sourceTree = ""; }; - 0E906CE425DCEADC0034053C /* HollowType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HollowType.swift; sourceTree = ""; }; - 0E906CE625DCEADC0034053C /* VoteData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VoteData.swift; sourceTree = ""; }; - 0E906CE725DCEADC0034053C /* HollowImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HollowImage.swift; sourceTree = ""; }; - 0E906CE925DCEADC0034053C /* CommentData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommentData.swift; sourceTree = ""; }; - 0E906CEA25DCEADC0034053C /* PostData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostData.swift; sourceTree = ""; }; - 0E906CEC25DCEADC0034053C /* String+SHA256.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+SHA256.swift"; sourceTree = ""; }; - 0E906CED25DCEADC0034053C /* Data+hexEncodedString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+hexEncodedString.swift"; sourceTree = ""; }; - 0E906CEE25DCEADC0034053C /* String+regex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+regex.swift"; sourceTree = ""; }; - 0E906CF025DCEADC0034053C /* DefaultsKeys.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultsKeys.swift; sourceTree = ""; }; - 0E906CF125DCEADC0034053C /* Int+bool.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Int+bool.swift"; sourceTree = ""; }; - 0E906CF225DCEADC0034053C /* String+removeLineBreak.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+removeLineBreak.swift"; sourceTree = ""; }; - 0E906CF325DCEADC0034053C /* Int+string.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Int+string.swift"; sourceTree = ""; }; - 0E906CF425DCEADC0034053C /* Bool+int.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Bool+int.swift"; sourceTree = ""; }; - 0E906CF525DCEADC0034053C /* String+Date.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Date.swift"; sourceTree = ""; }; - 0E906CF725DCEADD0034053C /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; - 0E906CF925DCEADD0034053C /* ImageCompressor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageCompressor.swift; sourceTree = ""; }; - 0E906CFA25DCEADD0034053C /* ImageSaver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageSaver.swift; sourceTree = ""; }; - 0E906D4F25DD18050034053C /* ImageCompressStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCompressStore.swift; sourceTree = ""; }; 0E95A1E5262C7F9300079050 /* Toast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toast.swift; sourceTree = ""; }; - 0EA2D50B25EF8125003D539E /* MessageStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageStore.swift; sourceTree = ""; }; 0EA2D50E25EF9F41003D539E /* SearchBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBar.swift; sourceTree = ""; }; 0EA2D51125F0A53F003D539E /* View+swipeToDismiss.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+swipeToDismiss.swift"; sourceTree = ""; }; 0EA2D51925F0FA05003D539E /* View+defaultBlurBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+defaultBlurBackground.swift"; sourceTree = ""; }; 0EA2D51C25F11EA9003D539E /* ReportMenuContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportMenuContent.swift; sourceTree = ""; }; 0EA3C15D25FEFBAA00865B4D /* View+singleLineText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+singleLineText.swift"; sourceTree = ""; }; - 0EA3C16125FEFCA600865B4D /* DeviceModelUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceModelUtilities.swift; sourceTree = ""; }; - 0EA4298A25EB6F4D00B74D5D /* Double+int.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Double+int.swift"; sourceTree = ""; }; - 0EA4298D25EBBB8B00B74D5D /* DateExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateExtensions.swift; sourceTree = ""; }; 0EA6E98A260DD9FE00A151D7 /* MainView_iPad.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView_iPad.swift; sourceTree = ""; }; - 0EAA1B3A25E511BA00904296 /* PublisherExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublisherExtensions.swift; sourceTree = ""; }; - 0EAA1B4625E6422E00904296 /* Request+publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Request+publisher.swift"; sourceTree = ""; }; 0EAA1B4A25E6562B00904296 /* View+flash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+flash.swift"; sourceTree = ""; }; 0EAA1B4E25E6971500904296 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; 0EAA1B5725E7694400904296 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; @@ -391,21 +418,124 @@ 0EAC358F25E8CCA7000E9F36 /* HollowDetailSubViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HollowDetailSubViews.swift; sourceTree = ""; }; 0EB45E3D261209A300409227 /* View+presentStyledAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+presentStyledAlert.swift"; sourceTree = ""; }; 0EB4F9A125F37A0E00BDAFF1 /* Text+highlight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Text+highlight.swift"; sourceTree = ""; }; - 0EB4F9A525F3837C00BDAFF1 /* Array+sortedRange.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+sortedRange.swift"; sourceTree = ""; }; + 0EC244C2263400C5001AFC4B /* HollowMac.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HollowMac.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 0EC244C4263400C6001AFC4B /* HollowMacApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HollowMacApp.swift; sourceTree = ""; }; + 0EC244C6263400C6001AFC4B /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 0EC244C8263400C7001AFC4B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 0EC244CD263400C7001AFC4B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 0EC244CE263400C7001AFC4B /* HollowMac.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = HollowMac.entitlements; sourceTree = ""; }; + 0EC244DE26340172001AFC4B /* CrossPlatformTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CrossPlatformTypes.swift; sourceTree = ""; }; + 0EC244DF26340172001AFC4B /* CrossPlatformImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CrossPlatformImage.swift; sourceTree = ""; }; + 0EC244EA263401E4001AFC4B /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 0EC244F226340211001AFC4B /* AppModelBehaviour.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppModelBehaviour.swift; sourceTree = ""; }; + 0EC244F326340211001AFC4B /* AppModelEnvironment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppModelEnvironment.swift; sourceTree = ""; }; + 0EC244F426340211001AFC4B /* AppModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppModel.swift; sourceTree = ""; }; + 0EC244F526340211001AFC4B /* AppDelegateShared.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegateShared.swift; sourceTree = ""; }; + 0EC245012634022C001AFC4B /* HollowApp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HollowApp.swift; sourceTree = ""; }; + 0EC245022634022C001AFC4B /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 0EC2451C26340301001AFC4B /* UIDevice+isPad.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIDevice+isPad.swift"; sourceTree = ""; }; + 0EC2451D26340301001AFC4B /* ConnectivityPublisherExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectivityPublisherExtensions.swift; sourceTree = ""; }; + 0EC2451E26340301001AFC4B /* ConnectivityStatus+isConnected.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ConnectivityStatus+isConnected.swift"; sourceTree = ""; }; + 0EC2452026340301001AFC4B /* ImageCompressor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageCompressor.swift; sourceTree = ""; }; + 0EC2452126340301001AFC4B /* ImageSaver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageSaver.swift; sourceTree = ""; }; + 0EC2452A26340311001AFC4B /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; + 0EC2452C26340311001AFC4B /* HollowConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HollowConfig.swift; sourceTree = ""; }; + 0EC2452D26340311001AFC4B /* Comment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Comment.swift; sourceTree = ""; }; + 0EC2452E26340311001AFC4B /* ImageMetadata.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageMetadata.swift; sourceTree = ""; }; + 0EC2452F26340311001AFC4B /* SystemMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SystemMessage.swift; sourceTree = ""; }; + 0EC2453026340311001AFC4B /* PushNotificationType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PushNotificationType.swift; sourceTree = ""; }; + 0EC2453126340311001AFC4B /* Post.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Post.swift; sourceTree = ""; }; + 0EC2453226340311001AFC4B /* Vote.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Vote.swift; sourceTree = ""; }; + 0EC2453326340311001AFC4B /* DeviceInformationType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceInformationType.swift; sourceTree = ""; }; + 0EC2453526340311001AFC4B /* VoteData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VoteData.swift; sourceTree = ""; }; + 0EC2453626340311001AFC4B /* HollowImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HollowImage.swift; sourceTree = ""; }; + 0EC2453726340311001AFC4B /* CommentData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommentData.swift; sourceTree = ""; }; + 0EC2453826340311001AFC4B /* PostData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostData.swift; sourceTree = ""; }; + 0EC2453926340311001AFC4B /* HollowType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HollowType.swift; sourceTree = ""; }; + 0EC2453C26340311001AFC4B /* PostDetailRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostDetailRequest.swift; sourceTree = ""; }; + 0EC2453D26340311001AFC4B /* SearchHistoryRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchHistoryRequest.swift; sourceTree = ""; }; + 0EC2453E26340311001AFC4B /* SearchRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchRequest.swift; sourceTree = ""; }; + 0EC2453F26340311001AFC4B /* PostListRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostListRequest.swift; sourceTree = ""; }; + 0EC2454026340311001AFC4B /* SystemMessageRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SystemMessageRequest.swift; sourceTree = ""; }; + 0EC2454126340311001AFC4B /* AttentionListRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttentionListRequest.swift; sourceTree = ""; }; + 0EC2454226340311001AFC4B /* RandomListRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RandomListRequest.swift; sourceTree = ""; }; + 0EC2454326340311001AFC4B /* AttentionListSearchRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttentionListSearchRequest.swift; sourceTree = ""; }; + 0EC2454426340311001AFC4B /* DefaultRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultRequest.swift; sourceTree = ""; }; + 0EC2454626340311001AFC4B /* GetConfigRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetConfigRequest.swift; sourceTree = ""; }; + 0EC2454726340311001AFC4B /* GetPushRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetPushRequest.swift; sourceTree = ""; }; + 0EC2454826340311001AFC4B /* SetPushRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SetPushRequest.swift; sourceTree = ""; }; + 0EC2454A26340311001AFC4B /* LoginRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginRequest.swift; sourceTree = ""; }; + 0EC2454B26340311001AFC4B /* LogoutRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogoutRequest.swift; sourceTree = ""; }; + 0EC2454C26340311001AFC4B /* EmailCheckRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmailCheckRequest.swift; sourceTree = ""; }; + 0EC2454D26340311001AFC4B /* DeviceListRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceListRequest.swift; sourceTree = ""; }; + 0EC2454E26340311001AFC4B /* UpdateDeviceTokenRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdateDeviceTokenRequest.swift; sourceTree = ""; }; + 0EC2454F26340311001AFC4B /* DeviceTerminationRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceTerminationRequest.swift; sourceTree = ""; }; + 0EC2455026340311001AFC4B /* AccountCreationRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountCreationRequest.swift; sourceTree = ""; }; + 0EC2455126340311001AFC4B /* ChangePasswordRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChangePasswordRequest.swift; sourceTree = ""; }; + 0EC2455326340311001AFC4B /* PostListRequestGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostListRequestGroup.swift; sourceTree = ""; }; + 0EC2455426340311001AFC4B /* FetchImageRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchImageRequest.swift; sourceTree = ""; }; + 0EC2455526340311001AFC4B /* ReportRequestGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReportRequestGroup.swift; sourceTree = ""; }; + 0EC2455626340311001AFC4B /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + 0EC2455826340311001AFC4B /* ReportPostRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReportPostRequest.swift; sourceTree = ""; }; + 0EC2455926340311001AFC4B /* ReportCommentRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReportCommentRequest.swift; sourceTree = ""; }; + 0EC2455A26340311001AFC4B /* EditAttentionRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditAttentionRequest.swift; sourceTree = ""; }; + 0EC2455B26340311001AFC4B /* Request.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Request.swift; sourceTree = ""; }; + 0EC2455D26340311001AFC4B /* SendPostRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendPostRequest.swift; sourceTree = ""; }; + 0EC2455E26340311001AFC4B /* SendCommentRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendCommentRequest.swift; sourceTree = ""; }; + 0EC2455F26340311001AFC4B /* SendVoteRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendVoteRequest.swift; sourceTree = ""; }; + 0EC2456026340311001AFC4B /* Request+publisher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Request+publisher.swift"; sourceTree = ""; }; + 0EC2456126340311001AFC4B /* RequestPublisher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestPublisher.swift; sourceTree = ""; }; + 0EC2456226340311001AFC4B /* RequestError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestError.swift; sourceTree = ""; }; + 0EC2456426340311001AFC4B /* String+SHA256.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+SHA256.swift"; sourceTree = ""; }; + 0EC2456526340311001AFC4B /* Data+hexEncodedString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+hexEncodedString.swift"; sourceTree = ""; }; + 0EC2456626340311001AFC4B /* DefaultsKeys.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultsKeys.swift; sourceTree = ""; }; + 0EC2456726340311001AFC4B /* Int+bool.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Int+bool.swift"; sourceTree = ""; }; + 0EC2456826340311001AFC4B /* String+removeLineBreak.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+removeLineBreak.swift"; sourceTree = ""; }; + 0EC2456926340311001AFC4B /* Int+string.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Int+string.swift"; sourceTree = ""; }; + 0EC2456A26340311001AFC4B /* Array+removeDuplicates.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+removeDuplicates.swift"; sourceTree = ""; }; + 0EC2456B26340311001AFC4B /* String+regex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+regex.swift"; sourceTree = ""; }; + 0EC2456C26340311001AFC4B /* Double+int.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Double+int.swift"; sourceTree = ""; }; + 0EC2456D26340311001AFC4B /* Array+sortedRange.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+sortedRange.swift"; sourceTree = ""; }; + 0EC2456E26340311001AFC4B /* Bool+int.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Bool+int.swift"; sourceTree = ""; }; + 0EC2456F26340311001AFC4B /* String+Date.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Date.swift"; sourceTree = ""; }; + 0EC2457026340311001AFC4B /* DateExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateExtensions.swift; sourceTree = ""; }; + 0EC2457126340311001AFC4B /* PublisherExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PublisherExtensions.swift; sourceTree = ""; }; + 0EC2457326340311001AFC4B /* HollowDateFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HollowDateFormatter.swift; sourceTree = ""; }; + 0EC2457426340311001AFC4B /* AvatarGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AvatarGenerator.swift; sourceTree = ""; }; + 0EC2457526340311001AFC4B /* OpenURLHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenURLHelper.swift; sourceTree = ""; }; + 0EC2457626340311001AFC4B /* LineSwitchManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LineSwitchManager.swift; sourceTree = ""; }; + 0EC2457726340311001AFC4B /* DeviceModelUtilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceModelUtilities.swift; sourceTree = ""; }; + 0EC2457826340311001AFC4B /* PostCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostCache.swift; sourceTree = ""; }; + 0EC246112634040D001AFC4B /* ImageCompressStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageCompressStore.swift; sourceTree = ""; }; + 0EC246122634040D001AFC4B /* HollowInputStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HollowInputStore.swift; sourceTree = ""; }; + 0EC246132634040D001AFC4B /* PostListRequestStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostListRequestStore.swift; sourceTree = ""; }; + 0EC246142634040D001AFC4B /* HollowDetailStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HollowDetailStore.swift; sourceTree = ""; }; + 0EC246162634040D001AFC4B /* AccountInfoStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountInfoStore.swift; sourceTree = ""; }; + 0EC246172634040D001AFC4B /* DeviceListStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceListStore.swift; sourceTree = ""; }; + 0EC246182634040D001AFC4B /* MessageStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageStore.swift; sourceTree = ""; }; + 0EC2461A2634040D001AFC4B /* WelcomeStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WelcomeStore.swift; sourceTree = ""; }; + 0EC2461B2634040D001AFC4B /* LoginStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginStore.swift; sourceTree = ""; }; + 0EC2463E26340770001AFC4B /* AppModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppModel.swift; sourceTree = ""; }; + 0EC24648263407D3001AFC4B /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = ""; }; + 0EC2464C263407E4001AFC4B /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = ""; }; + 0EC24650263408CC001AFC4B /* AppModelEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppModelEnvironment.swift; sourceTree = ""; }; + 0EC2465526340B71001AFC4B /* View+showAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+showAlert.swift"; sourceTree = ""; }; + 0EC2465B2634116A001AFC4B /* AppModelBehaviour.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppModelBehaviour.swift; sourceTree = ""; }; 0EC2AE35260F761A0099B500 /* View+conditionalSizeCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+conditionalSizeCategory.swift"; sourceTree = ""; }; 0EC4B67725D644CA0065BB82 /* CustomTextEditor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomTextEditor.swift; sourceTree = ""; }; 0EC4B67E25D670070065BB82 /* ImagePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagePicker.swift; sourceTree = ""; }; + 0ED087BE263480C9003C2E11 /* LoginSubViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginSubViews.swift; sourceTree = ""; }; + 0ED087D026354556003C2E11 /* PostListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostListView.swift; sourceTree = ""; }; + 0ED1A15F26346C37001453A7 /* ReCAPTCHAWebView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReCAPTCHAWebView.swift; sourceTree = ""; }; + 0ED1A16626346CB1001453A7 /* ReCAPTCHAPageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReCAPTCHAPageView.swift; sourceTree = ""; }; 0ED82EFF26103FB600A799C0 /* View+defaultPadding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+defaultPadding.swift"; sourceTree = ""; }; 0EDA262625D22C8600B67292 /* DeviceListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceListView.swift; sourceTree = ""; }; - 0EDA262A25D22CBF00B67292 /* DeviceListStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceListStore.swift; sourceTree = ""; }; 0EDA263025D2465100B67292 /* LoadingIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingIndicator.swift; sourceTree = ""; }; 0EE60EFC25FCCCD700D97E7A /* CheckmarkButtonImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckmarkButtonImage.swift; sourceTree = ""; }; 0EF10F6526330F29004D6538 /* HollowDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HollowDetailViewController.swift; sourceTree = ""; }; 0EFA3DDD25D3A03D0003C76B /* LoadingLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingLabel.swift; sourceTree = ""; }; 0EFB54D925E8CF4B001BC98A /* HollowInputComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HollowInputComponents.swift; sourceTree = ""; }; 0EFB54E425E8D9FD001BC98A /* View+modalPresent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+modalPresent.swift"; sourceTree = ""; }; - 0EFB54EB25E90B55001BC98A /* ConnectivityStatus+isConnected.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConnectivityStatus+isConnected.swift"; sourceTree = ""; }; - 0EFB54F325E926A1001BC98A /* PostListRequestGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostListRequestGroup.swift; sourceTree = ""; }; 0EFB54F925E92CB0001BC98A /* SearchSubViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSubViews.swift; sourceTree = ""; }; 0EFB550425E93E24001BC98A /* PostListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostListView.swift; sourceTree = ""; }; 0EFE13F3262876FE007B0045 /* View+imageSaver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+imageSaver.swift"; sourceTree = ""; }; @@ -430,26 +560,31 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 0EC244BF263400C5001AFC4B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0EC245132634029D001AFC4B /* Kingfisher in Frameworks */, + 0EC245152634029D001AFC4B /* Cache in Frameworks */, + 0EC2450F2634029D001AFC4B /* AppCenterAnalytics in Frameworks */, + 0EC245112634029D001AFC4B /* AppCenterCrashes in Frameworks */, + 0EC2450D2634029D001AFC4B /* Alamofire in Frameworks */, + 0EC2450B2634029D001AFC4B /* Defaults in Frameworks */, + 0EC245092634029D001AFC4B /* WaterfallGrid in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 0E340F6F25CFEBFE002F0F0C /* App */ = { - isa = PBXGroup; - children = ( - 0E415A7325CD769B00351672 /* HollowApp.swift */, - 0E1DD7DB25DA4BE5000D27BA /* AppModel.swift */, - 0E1DD7DE25DA4E5C000D27BA /* AppModelEnvironment.swift */, - 0E1DD7D825DA4B91000D27BA /* AppModelBehaviour.swift */, - 0E415B9A25CD93C000351672 /* AppDelegate.swift */, - ); - path = App; - sourceTree = ""; - }; 0E415A6725CD769B00351672 = { isa = PBXGroup; children = ( + 0EC244DA26340156001AFC4B /* Shared */, 0E415A7225CD769B00351672 /* Hollow */, + 0EC244C3263400C6001AFC4B /* HollowMac */, 0E415A7125CD769B00351672 /* Products */, + 0EC245072634029D001AFC4B /* Frameworks */, ); sourceTree = ""; }; @@ -457,6 +592,7 @@ isa = PBXGroup; children = ( 0E415A7025CD769B00351672 /* Hollow.app */, + 0EC244C2263400C5001AFC4B /* HollowMac.app */, ); name = Products; sourceTree = ""; @@ -464,9 +600,8 @@ 0E415A7225CD769B00351672 /* Hollow */ = { isa = PBXGroup; children = ( - 0E340F6F25CFEBFE002F0F0C /* App */, - 0E906CB825DCEAC80034053C /* Model */, - 0E415B3225CD772200351672 /* ViewModel */, + 0EC245002634022C001AFC4B /* App */, + 0EC2451A26340301001AFC4B /* Model */, 0E415AE825CD772200351672 /* View */, 0E6EF32925FB6962001293FE /* Resources */, 0E4284BC25CE2A96008F134A /* Hollow.entitlements */, @@ -482,7 +617,6 @@ isa = PBXGroup; children = ( 0EFE1408262AF7E1007B0045 /* README.md */, - 0E415AE925CD772200351672 /* ViewConstants.swift */, 0EFE1401262AF6C4007B0045 /* Hierarchy */, 0E415B0125CD772200351672 /* Components */, 0E415AEA25CD772200351672 /* Modifiers */, @@ -525,7 +659,6 @@ 0E415AED25CD772200351672 /* CustomTabView.swift */, 0E415AEF25CD772200351672 /* CustomScrollView.swift */, 0EC4B67725D644CA0065BB82 /* CustomTextEditor.swift */, - 0E415AEE25CD772200351672 /* ReCAPTCHAWebView.swift */, 0E415AF025CD772200351672 /* Blur.swift */, 0E09B7A925D4D73100DC5615 /* ImageViewer.swift */, 0EC4B67E25D670070065BB82 /* ImagePicker.swift */, @@ -538,10 +671,10 @@ 0E415AF125CD772200351672 /* Customization */ = { isa = PBXGroup; children = ( + 0E415AF925CD772200351672 /* CustomColors.swift */, 0E28BC4125F5E29D00A97820 /* CustomColorScheme.swift */, 0E415AF525CD772200351672 /* CustomAnimations.swift */, 0E415AF725CD772200351672 /* LinearGradientExtensions.swift */, - 0E415AF925CD772200351672 /* CustomColors.swift */, 0E415AFC25CD772200351672 /* CustomGradient.swift */, ); path = Customization; @@ -656,44 +789,12 @@ path = Login; sourceTree = ""; }; - 0E415B3225CD772200351672 /* ViewModel */ = { - isa = PBXGroup; - children = ( - 0E415B3325CD772200351672 /* Hollow */, - 0E415B3F25CD772200351672 /* Login */, - 0EDA262925D22C9800B67292 /* Account */, - ); - path = ViewModel; - sourceTree = ""; - }; - 0E415B3325CD772200351672 /* Hollow */ = { - isa = PBXGroup; - children = ( - 0E294C0C25E2570D00A9875A /* HollowDetailStore.swift */, - 0E09B7BA25D52F4F00DC5615 /* HollowInputStore.swift */, - 0E906D4F25DD18050034053C /* ImageCompressStore.swift */, - 0E415B3C25CD772200351672 /* PostListRequestStore.swift */, - ); - path = Hollow; - sourceTree = ""; - }; - 0E415B3F25CD772200351672 /* Login */ = { - isa = PBXGroup; - children = ( - 0E415B4025CD772200351672 /* LoginStore.swift */, - 0E415B4125CD772200351672 /* WelcomeStore.swift */, - ); - path = Login; - sourceTree = ""; - }; - 0E4786D725E3E11B0004F8FA /* Other */ = { + 0E4E4BBC26345ED400B32F7F /* Modifiers */ = { isa = PBXGroup; children = ( - 0E4786D825E3E1870004F8FA /* FetchImageRequest.swift */, - 0EFB54F325E926A1001BC98A /* PostListRequestGroup.swift */, - 0E294E9625F1F448001E4C08 /* ReportRequestGroup.swift */, + 0E4E4BC42634619A00B32F7F /* View+roundedCorner.swift */, ); - path = Other; + path = Modifiers; sourceTree = ""; }; 0E6EF32925FB6962001293FE /* Resources */ = { @@ -714,7 +815,6 @@ 0EB4F9A125F37A0E00BDAFF1 /* Text+highlight.swift */, 0EAA1B4A25E6562B00904296 /* View+flash.swift */, 0E85DFBD25DAE578003D134B /* View+hideKeyboard.swift */, - 0E415AF625CD772200351672 /* ViewLayouts.swift */, 0E415AF425CD772200351672 /* Color+uiColor.swift */, 0E415AEB25CD772200351672 /* ErrorAlert.swift */, 0E36F53F25D0E02F001AE852 /* GetSize.swift */, @@ -725,184 +825,384 @@ path = Utilities; sourceTree = ""; }; - 0E906CB825DCEAC80034053C /* Model */ = { + 0EA6E98D260DDA0500A151D7 /* iPad */ = { + isa = PBXGroup; + children = ( + 0EA6E98A260DD9FE00A151D7 /* MainView_iPad.swift */, + 0E3E6957260E1312005E8DA2 /* MainSubViews_iPad.swift */, + 0E3E694A260DF2C8005E8DA2 /* HollowDetailView_iPad.swift */, + ); + path = iPad; + sourceTree = ""; + }; + 0EC244C3263400C6001AFC4B /* HollowMac */ = { + isa = PBXGroup; + children = ( + 0EC244EE263401E7001AFC4B /* App */, + 0EC24637263405B0001AFC4B /* View */, + 0EC244C6263400C6001AFC4B /* ContentView.swift */, + 0EC244C8263400C7001AFC4B /* Assets.xcassets */, + 0EC244CD263400C7001AFC4B /* Info.plist */, + 0EC244CE263400C7001AFC4B /* HollowMac.entitlements */, + ); + path = HollowMac; + sourceTree = ""; + }; + 0EC244DA26340156001AFC4B /* Shared */ = { + isa = PBXGroup; + children = ( + 0EC244DD26340172001AFC4B /* CrossPlatform */, + 0EC244F126340211001AFC4B /* App */, + 0EC2452926340311001AFC4B /* Model */, + 0EC2460E2634040D001AFC4B /* ViewModel */, + 0EC246052634032B001AFC4B /* View */, + ); + path = Shared; + sourceTree = ""; + }; + 0EC244DD26340172001AFC4B /* CrossPlatform */ = { + isa = PBXGroup; + children = ( + 0EC244DE26340172001AFC4B /* CrossPlatformTypes.swift */, + 0EC244DF26340172001AFC4B /* CrossPlatformImage.swift */, + ); + path = CrossPlatform; + sourceTree = ""; + }; + 0EC244EE263401E7001AFC4B /* App */ = { + isa = PBXGroup; + children = ( + 0EC244C4263400C6001AFC4B /* HollowMacApp.swift */, + 0EC244EA263401E4001AFC4B /* AppDelegate.swift */, + 0EC2463E26340770001AFC4B /* AppModel.swift */, + 0EC24650263408CC001AFC4B /* AppModelEnvironment.swift */, + 0EC2465B2634116A001AFC4B /* AppModelBehaviour.swift */, + ); + path = App; + sourceTree = ""; + }; + 0EC244F126340211001AFC4B /* App */ = { + isa = PBXGroup; + children = ( + 0EC244F526340211001AFC4B /* AppDelegateShared.swift */, + ); + path = App; + sourceTree = ""; + }; + 0EC245002634022C001AFC4B /* App */ = { + isa = PBXGroup; + children = ( + 0EC245012634022C001AFC4B /* HollowApp.swift */, + 0EC244F226340211001AFC4B /* AppModelBehaviour.swift */, + 0EC244F326340211001AFC4B /* AppModelEnvironment.swift */, + 0EC244F426340211001AFC4B /* AppModel.swift */, + 0EC245022634022C001AFC4B /* AppDelegate.swift */, + ); + path = App; + sourceTree = ""; + }; + 0EC245072634029D001AFC4B /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + 0EC2451A26340301001AFC4B /* Model */ = { + isa = PBXGroup; + children = ( + 0EC2451B26340301001AFC4B /* Extensions */, + 0EC2451F26340301001AFC4B /* Utilities */, + ); + path = Model; + sourceTree = ""; + }; + 0EC2451B26340301001AFC4B /* Extensions */ = { + isa = PBXGroup; + children = ( + 0EC2451C26340301001AFC4B /* UIDevice+isPad.swift */, + 0EC2451D26340301001AFC4B /* ConnectivityPublisherExtensions.swift */, + 0EC2451E26340301001AFC4B /* ConnectivityStatus+isConnected.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + 0EC2451F26340301001AFC4B /* Utilities */ = { + isa = PBXGroup; + children = ( + 0EC2452126340301001AFC4B /* ImageSaver.swift */, + ); + path = Utilities; + sourceTree = ""; + }; + 0EC2452926340311001AFC4B /* Model */ = { isa = PBXGroup; children = ( - 0E906CF725DCEADD0034053C /* Constants.swift */, - 0E906CB925DCEADC0034053C /* Net */, - 0E906CEB25DCEADC0034053C /* Extensions */, - 0E906CDE25DCEADC0034053C /* Types */, - 0E906CF825DCEADD0034053C /* Utilities */, + 0EC2452A26340311001AFC4B /* Constants.swift */, + 0EC2452B26340311001AFC4B /* Types */, + 0EC2453A26340311001AFC4B /* Net */, + 0EC2456326340311001AFC4B /* Extensions */, + 0EC2457226340311001AFC4B /* Utilities */, ); path = Model; sourceTree = ""; }; - 0E906CB925DCEADC0034053C /* Net */ = { + 0EC2452B26340311001AFC4B /* Types */ = { isa = PBXGroup; children = ( - 0E906CD325DCEADC0034053C /* README.md */, - 0E906CD825DCEADC0034053C /* Request.swift */, - 0E906CDD25DCEADC0034053C /* RequestError.swift */, - 0E906CC525DCEADC0034053C /* DefaultRequest.swift */, - 0E4786C725E3A93E0004F8FA /* RequestPublisher.swift */, - 0EAA1B4625E6422E00904296 /* Request+publisher.swift */, - 0E906CC625DCEADC0034053C /* Config */, - 0E906CCA25DCEADC0034053C /* Security */, - 0E906CBA25DCEADC0034053C /* Contents */, - 0E906CD425DCEADC0034053C /* Edit */, - 0E906CD925DCEADC0034053C /* Send */, - 0E4786D725E3E11B0004F8FA /* Other */, + 0EC2452C26340311001AFC4B /* HollowConfig.swift */, + 0EC2452D26340311001AFC4B /* Comment.swift */, + 0EC2452E26340311001AFC4B /* ImageMetadata.swift */, + 0EC2452F26340311001AFC4B /* SystemMessage.swift */, + 0EC2453026340311001AFC4B /* PushNotificationType.swift */, + 0EC2453126340311001AFC4B /* Post.swift */, + 0EC2453226340311001AFC4B /* Vote.swift */, + 0EC2453326340311001AFC4B /* DeviceInformationType.swift */, + 0EC2453426340311001AFC4B /* Data */, + 0EC2453926340311001AFC4B /* HollowType.swift */, + ); + path = Types; + sourceTree = ""; + }; + 0EC2453426340311001AFC4B /* Data */ = { + isa = PBXGroup; + children = ( + 0EC2453526340311001AFC4B /* VoteData.swift */, + 0EC2453626340311001AFC4B /* HollowImage.swift */, + 0EC2453726340311001AFC4B /* CommentData.swift */, + 0EC2453826340311001AFC4B /* PostData.swift */, + ); + path = Data; + sourceTree = ""; + }; + 0EC2453A26340311001AFC4B /* Net */ = { + isa = PBXGroup; + children = ( + 0EC2453B26340311001AFC4B /* Contents */, + 0EC2454426340311001AFC4B /* DefaultRequest.swift */, + 0EC2454526340311001AFC4B /* Config */, + 0EC2454926340311001AFC4B /* Security */, + 0EC2455226340311001AFC4B /* Other */, + 0EC2455626340311001AFC4B /* README.md */, + 0EC2455726340311001AFC4B /* Edit */, + 0EC2455B26340311001AFC4B /* Request.swift */, + 0EC2455C26340311001AFC4B /* Send */, + 0EC2456026340311001AFC4B /* Request+publisher.swift */, + 0EC2456126340311001AFC4B /* RequestPublisher.swift */, + 0EC2456226340311001AFC4B /* RequestError.swift */, ); path = Net; sourceTree = ""; }; - 0E906CBA25DCEADC0034053C /* Contents */ = { + 0EC2453B26340311001AFC4B /* Contents */ = { isa = PBXGroup; children = ( - 0E906CBB25DCEADC0034053C /* PostDetailRequest.swift */, - 0E906CBD25DCEADC0034053C /* SearchHistoryRequest.swift */, - 0E906CBE25DCEADC0034053C /* SearchRequest.swift */, - 0E906CBF25DCEADC0034053C /* PostListRequest.swift */, - 0E906CC025DCEADC0034053C /* SystemMessageRequest.swift */, - 0E906CC125DCEADC0034053C /* AttentionListRequest.swift */, - 0E906CC225DCEADC0034053C /* RandomListRequest.swift */, - 0E906CC425DCEADC0034053C /* AttentionListSearchRequest.swift */, + 0EC2453C26340311001AFC4B /* PostDetailRequest.swift */, + 0EC2453D26340311001AFC4B /* SearchHistoryRequest.swift */, + 0EC2453E26340311001AFC4B /* SearchRequest.swift */, + 0EC2453F26340311001AFC4B /* PostListRequest.swift */, + 0EC2454026340311001AFC4B /* SystemMessageRequest.swift */, + 0EC2454126340311001AFC4B /* AttentionListRequest.swift */, + 0EC2454226340311001AFC4B /* RandomListRequest.swift */, + 0EC2454326340311001AFC4B /* AttentionListSearchRequest.swift */, ); path = Contents; sourceTree = ""; }; - 0E906CC625DCEADC0034053C /* Config */ = { + 0EC2454526340311001AFC4B /* Config */ = { isa = PBXGroup; children = ( - 0E906CC725DCEADC0034053C /* GetConfigRequest.swift */, - 0E906CC825DCEADC0034053C /* GetPushRequest.swift */, - 0E906CC925DCEADC0034053C /* SetPushRequest.swift */, + 0EC2454626340311001AFC4B /* GetConfigRequest.swift */, + 0EC2454726340311001AFC4B /* GetPushRequest.swift */, + 0EC2454826340311001AFC4B /* SetPushRequest.swift */, ); path = Config; sourceTree = ""; }; - 0E906CCA25DCEADC0034053C /* Security */ = { + 0EC2454926340311001AFC4B /* Security */ = { isa = PBXGroup; children = ( - 0E906CCB25DCEADC0034053C /* LoginRequest.swift */, - 0E906CCC25DCEADC0034053C /* LogoutRequest.swift */, - 0E906CCD25DCEADC0034053C /* EmailCheckRequest.swift */, - 0E906CCE25DCEADC0034053C /* DeviceListRequest.swift */, - 0E906CCF25DCEADC0034053C /* UpdateDeviceTokenRequest.swift */, - 0E906CD025DCEADC0034053C /* DeviceTerminationRequest.swift */, - 0E906CD125DCEADC0034053C /* AccountCreationRequest.swift */, - 0E906CD225DCEADC0034053C /* ChangePasswordRequest.swift */, + 0EC2454A26340311001AFC4B /* LoginRequest.swift */, + 0EC2454B26340311001AFC4B /* LogoutRequest.swift */, + 0EC2454C26340311001AFC4B /* EmailCheckRequest.swift */, + 0EC2454D26340311001AFC4B /* DeviceListRequest.swift */, + 0EC2454E26340311001AFC4B /* UpdateDeviceTokenRequest.swift */, + 0EC2454F26340311001AFC4B /* DeviceTerminationRequest.swift */, + 0EC2455026340311001AFC4B /* AccountCreationRequest.swift */, + 0EC2455126340311001AFC4B /* ChangePasswordRequest.swift */, ); path = Security; sourceTree = ""; }; - 0E906CD425DCEADC0034053C /* Edit */ = { + 0EC2455226340311001AFC4B /* Other */ = { isa = PBXGroup; children = ( - 0E906CD525DCEADC0034053C /* ReportPostRequest.swift */, - 0E906CD625DCEADC0034053C /* ReportCommentRequest.swift */, - 0E906CD725DCEADC0034053C /* EditAttentionRequest.swift */, + 0EC2455326340311001AFC4B /* PostListRequestGroup.swift */, + 0EC2455426340311001AFC4B /* FetchImageRequest.swift */, + 0EC2455526340311001AFC4B /* ReportRequestGroup.swift */, + ); + path = Other; + sourceTree = ""; + }; + 0EC2455726340311001AFC4B /* Edit */ = { + isa = PBXGroup; + children = ( + 0EC2455826340311001AFC4B /* ReportPostRequest.swift */, + 0EC2455926340311001AFC4B /* ReportCommentRequest.swift */, + 0EC2455A26340311001AFC4B /* EditAttentionRequest.swift */, ); path = Edit; sourceTree = ""; }; - 0E906CD925DCEADC0034053C /* Send */ = { + 0EC2455C26340311001AFC4B /* Send */ = { isa = PBXGroup; children = ( - 0E906CDA25DCEADC0034053C /* SendPostRequest.swift */, - 0E906CDB25DCEADC0034053C /* SendCommentRequest.swift */, - 0E906CDC25DCEADC0034053C /* SendVoteRequest.swift */, + 0EC2455D26340311001AFC4B /* SendPostRequest.swift */, + 0EC2455E26340311001AFC4B /* SendCommentRequest.swift */, + 0EC2455F26340311001AFC4B /* SendVoteRequest.swift */, ); path = Send; sourceTree = ""; }; - 0E906CDE25DCEADC0034053C /* Types */ = { + 0EC2456326340311001AFC4B /* Extensions */ = { isa = PBXGroup; children = ( - 0E906CE525DCEADC0034053C /* Data */, - 0E906CDF25DCEADC0034053C /* Comment.swift */, - 0E906CE025DCEADC0034053C /* SystemMessage.swift */, - 0E906CE125DCEADC0034053C /* PushNotificationType.swift */, - 0E906CE225DCEADC0034053C /* Post.swift */, - 00EFC07025DFC71500536B52 /* Vote.swift */, - 00EFC07325DFC73800536B52 /* ImageMetadata.swift */, - 0E906CE325DCEADC0034053C /* DeviceInformationType.swift */, - 0E906CE425DCEADC0034053C /* HollowType.swift */, - 0E8CFBB7260B2E6F0050FDD2 /* HollowConfig.swift */, + 0EC2456426340311001AFC4B /* String+SHA256.swift */, + 0EC2456526340311001AFC4B /* Data+hexEncodedString.swift */, + 0EC2456626340311001AFC4B /* DefaultsKeys.swift */, + 0EC2456726340311001AFC4B /* Int+bool.swift */, + 0EC2456826340311001AFC4B /* String+removeLineBreak.swift */, + 0EC2456926340311001AFC4B /* Int+string.swift */, + 0EC2456A26340311001AFC4B /* Array+removeDuplicates.swift */, + 0EC2456B26340311001AFC4B /* String+regex.swift */, + 0EC2456C26340311001AFC4B /* Double+int.swift */, + 0EC2456D26340311001AFC4B /* Array+sortedRange.swift */, + 0EC2456E26340311001AFC4B /* Bool+int.swift */, + 0EC2456F26340311001AFC4B /* String+Date.swift */, + 0EC2457026340311001AFC4B /* DateExtensions.swift */, + 0EC2457126340311001AFC4B /* PublisherExtensions.swift */, ); - path = Types; + path = Extensions; sourceTree = ""; }; - 0E906CE525DCEADC0034053C /* Data */ = { + 0EC2457226340311001AFC4B /* Utilities */ = { isa = PBXGroup; children = ( - 0E906CE625DCEADC0034053C /* VoteData.swift */, - 0E906CE725DCEADC0034053C /* HollowImage.swift */, - 0E906CE925DCEADC0034053C /* CommentData.swift */, - 0E906CEA25DCEADC0034053C /* PostData.swift */, + 0EC2452026340301001AFC4B /* ImageCompressor.swift */, + 0EC2457326340311001AFC4B /* HollowDateFormatter.swift */, + 0EC2457426340311001AFC4B /* AvatarGenerator.swift */, + 0EC2457526340311001AFC4B /* OpenURLHelper.swift */, + 0EC2457626340311001AFC4B /* LineSwitchManager.swift */, + 0EC2457726340311001AFC4B /* DeviceModelUtilities.swift */, + 0EC2457826340311001AFC4B /* PostCache.swift */, ); - path = Data; + path = Utilities; sourceTree = ""; }; - 0E906CEB25DCEADC0034053C /* Extensions */ = { + 0EC246052634032B001AFC4B /* View */ = { isa = PBXGroup; children = ( - 0E906CEC25DCEADC0034053C /* String+SHA256.swift */, - 0E906CED25DCEADC0034053C /* Data+hexEncodedString.swift */, - 0E906CEE25DCEADC0034053C /* String+regex.swift */, - 0E906CF025DCEADC0034053C /* DefaultsKeys.swift */, - 0E906CF125DCEADC0034053C /* Int+bool.swift */, - 0E906CF225DCEADC0034053C /* String+removeLineBreak.swift */, - 0E906CF325DCEADC0034053C /* Int+string.swift */, - 0E906CF425DCEADC0034053C /* Bool+int.swift */, - 0E906CF525DCEADC0034053C /* String+Date.swift */, - 0EAA1B3A25E511BA00904296 /* PublisherExtensions.swift */, - 0EFB54EB25E90B55001BC98A /* ConnectivityStatus+isConnected.swift */, - 0E58810925E9E61B006F6A94 /* ConnectivityPublisherExtensions.swift */, - 0EA4298A25EB6F4D00B74D5D /* Double+int.swift */, - 0EA4298D25EBBB8B00B74D5D /* DateExtensions.swift */, - 0EB4F9A525F3837C00BDAFF1 /* Array+sortedRange.swift */, - 0E527CD125F3D84800D91706 /* Array+removeDuplicates.swift */, - 0E3E6954260E08EC005E8DA2 /* UIDevice+isPad.swift */, + 0ED1A15F26346C37001453A7 /* ReCAPTCHAWebView.swift */, + 0E415AE925CD772200351672 /* ViewConstants.swift */, + 0ED1A16626346CB1001453A7 /* ReCAPTCHAPageView.swift */, + 0E415AF625CD772200351672 /* ViewLayouts.swift */, ); - path = Extensions; + path = View; sourceTree = ""; }; - 0E906CF825DCEADD0034053C /* Utilities */ = { + 0EC2460E2634040D001AFC4B /* ViewModel */ = { isa = PBXGroup; children = ( - 0E906CF925DCEADD0034053C /* ImageCompressor.swift */, - 0E906CFA25DCEADD0034053C /* ImageSaver.swift */, - 00B014EC25E1205A004E416E /* PostCache.swift */, - 00AA103425E74212006B3D6D /* LineSwitchManager.swift */, - 0E527CD525F4825700D91706 /* OpenURLHelper.swift */, - 0E3AC3FF25F7778400A4278A /* AvatarGenerator.swift */, - 0EA3C16125FEFCA600865B4D /* DeviceModelUtilities.swift */, - 0E3E695A260E166D005E8DA2 /* HollowDateFormatter.swift */, + 0EC246102634040D001AFC4B /* Hollow */, + 0EC246152634040D001AFC4B /* Account */, + 0EC246192634040D001AFC4B /* Login */, ); - path = Utilities; + path = ViewModel; sourceTree = ""; }; - 0EA6E98D260DDA0500A151D7 /* iPad */ = { + 0EC246102634040D001AFC4B /* Hollow */ = { isa = PBXGroup; children = ( - 0EA6E98A260DD9FE00A151D7 /* MainView_iPad.swift */, - 0E3E6957260E1312005E8DA2 /* MainSubViews_iPad.swift */, - 0E3E694A260DF2C8005E8DA2 /* HollowDetailView_iPad.swift */, + 0EC246112634040D001AFC4B /* ImageCompressStore.swift */, + 0EC246122634040D001AFC4B /* HollowInputStore.swift */, + 0EC246132634040D001AFC4B /* PostListRequestStore.swift */, + 0EC246142634040D001AFC4B /* HollowDetailStore.swift */, ); - path = iPad; + path = Hollow; sourceTree = ""; }; - 0EDA262925D22C9800B67292 /* Account */ = { + 0EC246152634040D001AFC4B /* Account */ = { isa = PBXGroup; children = ( - 0EA2D50B25EF8125003D539E /* MessageStore.swift */, - 0EDA262A25D22CBF00B67292 /* DeviceListStore.swift */, - 0E28BC4925F60EB400A97820 /* AccountInfoStore.swift */, + 0EC246162634040D001AFC4B /* AccountInfoStore.swift */, + 0EC246172634040D001AFC4B /* DeviceListStore.swift */, + 0EC246182634040D001AFC4B /* MessageStore.swift */, ); path = Account; sourceTree = ""; }; + 0EC246192634040D001AFC4B /* Login */ = { + isa = PBXGroup; + children = ( + 0EC2461A2634040D001AFC4B /* WelcomeStore.swift */, + 0EC2461B2634040D001AFC4B /* LoginStore.swift */, + ); + path = Login; + sourceTree = ""; + }; + 0EC24637263405B0001AFC4B /* View */ = { + isa = PBXGroup; + children = ( + 0ED087C2263485E1003C2E11 /* Utilities */, + 0E4E4BBC26345ED400B32F7F /* Modifiers */, + 0EC24638263405B4001AFC4B /* Hierarchy */, + 0EC2465426340B5F001AFC4B /* Integration */, + ); + path = View; + sourceTree = ""; + }; + 0EC24638263405B4001AFC4B /* Hierarchy */ = { + isa = PBXGroup; + children = ( + 0ED087CF263544F5003C2E11 /* Main */, + 0EC24639263405D3001AFC4B /* Login */, + ); + path = Hierarchy; + sourceTree = ""; + }; + 0EC24639263405D3001AFC4B /* Login */ = { + isa = PBXGroup; + children = ( + 0EC24648263407D3001AFC4B /* WelcomeView.swift */, + 0EC2464C263407E4001AFC4B /* LoginView.swift */, + 0ED087BE263480C9003C2E11 /* LoginSubViews.swift */, + ); + path = Login; + sourceTree = ""; + }; + 0EC2465426340B5F001AFC4B /* Integration */ = { + isa = PBXGroup; + children = ( + ); + path = Integration; + sourceTree = ""; + }; + 0ED087C2263485E1003C2E11 /* Utilities */ = { + isa = PBXGroup; + children = ( + 0EC2465526340B71001AFC4B /* View+showAlert.swift */, + ); + path = Utilities; + sourceTree = ""; + }; + 0ED087CF263544F5003C2E11 /* Main */ = { + isa = PBXGroup; + children = ( + 0ED087D026354556003C2E11 /* PostListView.swift */, + ); + path = Main; + sourceTree = ""; + }; 0EFE13FE262AF2D6007B0045 /* Presentation */ = { isa = PBXGroup; children = ( @@ -958,6 +1258,32 @@ productReference = 0E415A7025CD769B00351672 /* Hollow.app */; productType = "com.apple.product-type.application"; }; + 0EC244C1263400C5001AFC4B /* HollowMac */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0EC244D1263400C7001AFC4B /* Build configuration list for PBXNativeTarget "HollowMac" */; + buildPhases = ( + 0EC244BE263400C5001AFC4B /* Sources */, + 0EC244BF263400C5001AFC4B /* Frameworks */, + 0EC244C0263400C5001AFC4B /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = HollowMac; + packageProductDependencies = ( + 0EC245082634029D001AFC4B /* WaterfallGrid */, + 0EC2450A2634029D001AFC4B /* Defaults */, + 0EC2450C2634029D001AFC4B /* Alamofire */, + 0EC2450E2634029D001AFC4B /* AppCenterAnalytics */, + 0EC245102634029D001AFC4B /* AppCenterCrashes */, + 0EC245122634029D001AFC4B /* Kingfisher */, + 0EC245142634029D001AFC4B /* Cache */, + ); + productName = HollowMac; + productReference = 0EC244C2263400C5001AFC4B /* HollowMac.app */; + productType = "com.apple.product-type.application"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -971,6 +1297,9 @@ 0E415A6F25CD769B00351672 = { CreatedOnToolsVersion = 12.4; }; + 0EC244C1263400C5001AFC4B = { + CreatedOnToolsVersion = 12.4; + }; }; }; buildConfigurationList = 0E415A6B25CD769B00351672 /* Build configuration list for PBXProject "Hollow" */; @@ -998,6 +1327,7 @@ projectRoot = ""; targets = ( 0E415A6F25CD769B00351672 /* Hollow */, + 0EC244C1263400C5001AFC4B /* HollowMac */, ); }; /* End PBXProject section */ @@ -1009,11 +1339,21 @@ files = ( 0E6EF32B25FB6C05001293FE /* dependencies.json in Resources */, 0EAA1B5825E7694400904296 /* InfoPlist.strings in Resources */, + 0EC245C326340312001AFC4B /* README.md in Resources */, 0EAA1B4F25E6971500904296 /* Localizable.strings in Resources */, 0E415A7825CD769F00351672 /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; + 0EC244C0263400C5001AFC4B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0EC245C426340312001AFC4B /* README.md in Resources */, + 0EC244C9263400C7001AFC4B /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -1022,187 +1362,294 @@ buildActionMask = 2147483647; files = ( 0EC2AE36260F761A0099B500 /* View+conditionalSizeCategory.swift in Sources */, - 0E906D1025DCEADD0034053C /* ChangePasswordRequest.swift in Sources */, + 0EC246282634040D001AFC4B /* DeviceListStore.swift in Sources */, + 0EC2458B26340311001AFC4B /* VoteData.swift in Sources */, + 0EC245A926340311001AFC4B /* GetPushRequest.swift in Sources */, + 0EC245F126340312001AFC4B /* DateExtensions.swift in Sources */, 0EFE1405262AF768007B0045 /* View+makeDivider.swift in Sources */, 0EAC359025E8CCA7000E9F36 /* HollowDetailSubViews.swift in Sources */, 0E415B6C25CD772200351672 /* HollowHeaderView.swift in Sources */, - 0E906D1F25DCEADD0034053C /* HollowType.swift in Sources */, + 0EC244FC26340211001AFC4B /* AppDelegateShared.swift in Sources */, + 0EC245A726340311001AFC4B /* GetConfigRequest.swift in Sources */, 0EFA3DDE25D3A03D0003C76B /* LoadingLabel.swift in Sources */, + 0EC246242634040D001AFC4B /* HollowDetailStore.swift in Sources */, + 0EC245CD26340312001AFC4B /* SendPostRequest.swift in Sources */, 0E415B7825CD772200351672 /* MessageView.swift in Sources */, - 0E906D2025DCEADD0034053C /* VoteData.swift in Sources */, - 0E906D1E25DCEADD0034053C /* DeviceInformationType.swift in Sources */, + 0EC245BF26340312001AFC4B /* FetchImageRequest.swift in Sources */, + 0EC245E926340312001AFC4B /* Double+int.swift in Sources */, 0E8BCFE725E0A4720056D809 /* IntegrationUtilities.swift in Sources */, + 0EC245CF26340312001AFC4B /* SendCommentRequest.swift in Sources */, 0E415B5A25CD772200351672 /* MyButton.swift in Sources */, - 0E906D2325DCEADD0034053C /* CommentData.swift in Sources */, - 0E415B4525CD772200351672 /* ReCAPTCHAWebView.swift in Sources */, - 0E906CFF25DCEADD0034053C /* PostListRequest.swift in Sources */, + 0EC2458526340311001AFC4B /* Post.swift in Sources */, + 0EC245042634022C001AFC4B /* AppDelegate.swift in Sources */, + 0EC245E526340312001AFC4B /* Array+removeDuplicates.swift in Sources */, 0E415B6625CD772200351672 /* HollowVoteContentView.swift in Sources */, + 0EC245C526340312001AFC4B /* ReportPostRequest.swift in Sources */, 0E40F74825D7C997003A2342 /* HollowInputSubViews.swift in Sources */, 0E415B5725CD772200351672 /* Spinner.swift in Sources */, + 0EC245C726340312001AFC4B /* ReportCommentRequest.swift in Sources */, + 0EC245E326340312001AFC4B /* Int+string.swift in Sources */, 0E415B4A25CD772200351672 /* Color+uiColor.swift in Sources */, - 0E415B8325CD772200351672 /* LoginStore.swift in Sources */, + 0EC2452226340301001AFC4B /* UIDevice+isPad.swift in Sources */, + 0EC245DD26340312001AFC4B /* DefaultsKeys.swift in Sources */, 0EDA263125D2465100B67292 /* LoadingIndicator.swift in Sources */, + 0EC245B726340311001AFC4B /* DeviceTerminationRequest.swift in Sources */, + 0EC2457926340311001AFC4B /* Constants.swift in Sources */, 0E85DFBE25DAE578003D134B /* View+hideKeyboard.swift in Sources */, - 00EFC07425DFC73800536B52 /* ImageMetadata.swift in Sources */, 0EFB54DA25E8CF4B001BC98A /* HollowInputComponents.swift in Sources */, - 0E906D0825DCEADD0034053C /* SetPushRequest.swift in Sources */, + 0EC2452426340301001AFC4B /* ConnectivityStatus+isConnected.swift in Sources */, + 0EC245D526340312001AFC4B /* RequestPublisher.swift in Sources */, 0E415B7025CD772200351672 /* SettingsSubViews.swift in Sources */, 0E415B5425CD772200351672 /* ImageButtonModifier.swift in Sources */, - 0E527CD225F3D84800D91706 /* Array+removeDuplicates.swift in Sources */, - 0E906D2425DCEADD0034053C /* PostData.swift in Sources */, - 0E906D5025DD18050034053C /* ImageCompressStore.swift in Sources */, 0E6721DD25DE326400A84311 /* View+conditionalPadding.swift in Sources */, 0EFE13F4262876FE007B0045 /* View+imageSaver.swift in Sources */, + 0EC2458126340311001AFC4B /* SystemMessage.swift in Sources */, 0E3E6960260E3676005E8DA2 /* SplitView.swift in Sources */, 0E6721D625DE2DDB00A84311 /* GetSafeAreaInsets.swift in Sources */, - 0EAA1B4725E6422E00904296 /* Request+publisher.swift in Sources */, - 0EA2D50C25EF8125003D539E /* MessageStore.swift in Sources */, + 0EC245F526340312001AFC4B /* HollowDateFormatter.swift in Sources */, 0EA2D50F25EF9F41003D539E /* SearchBar.swift in Sources */, - 0E906D1325DCEADD0034053C /* ReportCommentRequest.swift in Sources */, - 0E906D1625DCEADD0034053C /* SendPostRequest.swift in Sources */, 0E415B4F25CD772200351672 /* CustomColors.swift in Sources */, + 0EC245FB26340312001AFC4B /* LineSwitchManager.swift in Sources */, 0EC4B67825D644CA0065BB82 /* CustomTextEditor.swift in Sources */, - 0E1DD7DC25DA4BE5000D27BA /* AppModel.swift in Sources */, - 0E906D1C25DCEADD0034053C /* PushNotificationType.swift in Sources */, - 0E415A7425CD769B00351672 /* HollowApp.swift in Sources */, 0EA2D51225F0A53F003D539E /* View+swipeToDismiss.swift in Sources */, - 0E906D3125DCEADD0034053C /* ImageCompressor.swift in Sources */, - 00B014ED25E1205A004E416E /* PostCache.swift in Sources */, 0E6EF31825F9C3D2001293FE /* FloatButton.swift in Sources */, + 0EC2459F26340311001AFC4B /* AttentionListRequest.swift in Sources */, 0E294E9C25F2405C001E4C08 /* View+roundedCorner.swift in Sources */, 0E415B6325CD772200351672 /* HollowCommentContentView.swift in Sources */, + 0EC2458326340311001AFC4B /* PushNotificationType.swift in Sources */, 0E6BE3B4260ED1EC00B529F7 /* View+noNavigationItems.swift in Sources */, - 0E906CFE25DCEADD0034053C /* SearchRequest.swift in Sources */, - 0E906D2D25DCEADD0034053C /* Bool+int.swift in Sources */, - 0E4786D925E3E1870004F8FA /* FetchImageRequest.swift in Sources */, - 0EFB54F425E926A1001BC98A /* PostListRequestGroup.swift in Sources */, 0E3DA2A825FF379000ECEFAC /* ProviderInfoView.swift in Sources */, 0E415B5E25CD772200351672 /* HollowCommentInputView.swift in Sources */, 0E95A1E6262C7F9300079050 /* Toast.swift in Sources */, - 0E906D0725DCEADD0034053C /* GetPushRequest.swift in Sources */, + 0EC245BD26340312001AFC4B /* PostListRequestGroup.swift in Sources */, 0E415B7325CD772200351672 /* SearchView.swift in Sources */, - 0E906D1925DCEADD0034053C /* RequestError.swift in Sources */, - 0E1DD7D925DA4B91000D27BA /* AppModelBehaviour.swift in Sources */, - 0E906D0125DCEADD0034053C /* AttentionListRequest.swift in Sources */, + 0EC2457D26340311001AFC4B /* Comment.swift in Sources */, + 0EC245DB26340312001AFC4B /* Data+hexEncodedString.swift in Sources */, + 0EC245CB26340312001AFC4B /* Request.swift in Sources */, + 0EC245FF26340312001AFC4B /* PostCache.swift in Sources */, 0E1DD7D625D983BE000D27BA /* Avatar.swift in Sources */, - 0E28BC4A25F60EB400A97820 /* AccountInfoStore.swift in Sources */, - 0E8CFBB8260B2E700050FDD2 /* HollowConfig.swift in Sources */, + 0EC245D326340312001AFC4B /* Request+publisher.swift in Sources */, + 0EC2458D26340311001AFC4B /* HollowImage.swift in Sources */, + 0EC2462E2634040D001AFC4B /* LoginStore.swift in Sources */, + 0EC245A526340311001AFC4B /* DefaultRequest.swift in Sources */, + 0ED1A16726346CB1001453A7 /* ReCAPTCHAPageView.swift in Sources */, 0EAC358B25E8BE92000E9F36 /* View+revertColorScheme.swift in Sources */, - 0EFB54EC25E90B55001BC98A /* ConnectivityStatus+isConnected.swift in Sources */, - 0E906D1B25DCEADD0034053C /* SystemMessage.swift in Sources */, - 0E906D2B25DCEADD0034053C /* String+removeLineBreak.swift in Sources */, 0E415B6725CD772200351672 /* HollowImageView.swift in Sources */, 0E36F54625D0F0FB001AE852 /* GetFrame.swift in Sources */, - 00AA103525E74212006B3D6D /* LineSwitchManager.swift in Sources */, + 0EC245EB26340312001AFC4B /* Array+sortedRange.swift in Sources */, 0E671AD5260F6E8C00590736 /* View+onClickGesture.swift in Sources */, - 0E906D0C25DCEADD0034053C /* DeviceListRequest.swift in Sources */, 0E3E694B260DF2C8005E8DA2 /* HollowDetailView_iPad.swift in Sources */, 0EC4B67F25D670070065BB82 /* ImagePicker.swift in Sources */, - 0E906D1725DCEADD0034053C /* SendCommentRequest.swift in Sources */, - 0E294E9725F1F448001E4C08 /* ReportRequestGroup.swift in Sources */, - 0E906CFD25DCEADD0034053C /* SearchHistoryRequest.swift in Sources */, - 0E906D0625DCEADD0034053C /* GetConfigRequest.swift in Sources */, - 0E906D3025DCEADD0034053C /* Constants.swift in Sources */, + 0EC2459526340311001AFC4B /* PostDetailRequest.swift in Sources */, 0E415B7425CD772200351672 /* TimelineView.swift in Sources */, - 0E294C0D25E2570D00A9875A /* HollowDetailStore.swift in Sources */, - 0E527CD625F4825700D91706 /* OpenURLHelper.swift in Sources */, + 0ED1A16026346C37001453A7 /* ReCAPTCHAWebView.swift in Sources */, + 0EC245C126340312001AFC4B /* ReportRequestGroup.swift in Sources */, + 0EC245FD26340312001AFC4B /* DeviceModelUtilities.swift in Sources */, + 0EC244F826340211001AFC4B /* AppModelEnvironment.swift in Sources */, + 0EC245A326340311001AFC4B /* AttentionListSearchRequest.swift in Sources */, + 0EC2452526340301001AFC4B /* ImageCompressor.swift in Sources */, 0E415B6425CD772200351672 /* HollowCiteContentView.swift in Sources */, 0EFB550525E93E24001BC98A /* PostListView.swift in Sources */, - 0EB4F9A625F3837C00BDAFF1 /* Array+sortedRange.swift in Sources */, + 0EC245D126340312001AFC4B /* SendVoteRequest.swift in Sources */, + 0EC245AB26340311001AFC4B /* SetPushRequest.swift in Sources */, + 0EC2462C2634040D001AFC4B /* WelcomeStore.swift in Sources */, 0E415B6B25CD772200351672 /* HollowButtonStyle.swift in Sources */, + 0EC2457F26340311001AFC4B /* ImageMetadata.swift in Sources */, 0EA6E98B260DD9FE00A151D7 /* MainView_iPad.swift in Sources */, - 0E1DD7DF25DA4E5C000D27BA /* AppModelEnvironment.swift in Sources */, 0E415B6525CD772200351672 /* HollowContentView.swift in Sources */, - 0E906D0A25DCEADD0034053C /* LogoutRequest.swift in Sources */, + 0EC245AF26340311001AFC4B /* LogoutRequest.swift in Sources */, + 0EC2462A2634040D001AFC4B /* MessageStore.swift in Sources */, 0E415B4D25CD772200351672 /* LinearGradientExtensions.swift in Sources */, + 0EC2459726340311001AFC4B /* SearchHistoryRequest.swift in Sources */, 0E415B4725CD772200351672 /* Blur.swift in Sources */, - 0E906D2725DCEADD0034053C /* String+regex.swift in Sources */, 0EAA1B4B25E6562B00904296 /* View+flash.swift in Sources */, - 0E906D1525DCEADD0034053C /* Request.swift in Sources */, - 0E906D0525DCEADD0034053C /* DefaultRequest.swift in Sources */, - 0E906D1225DCEADD0034053C /* ReportPostRequest.swift in Sources */, - 0E906D0D25DCEADD0034053C /* UpdateDeviceTokenRequest.swift in Sources */, 0E3E6958260E1312005E8DA2 /* MainSubViews_iPad.swift in Sources */, - 0EA4298E25EBBB8B00B74D5D /* DateExtensions.swift in Sources */, - 0E58810A25E9E61B006F6A94 /* ConnectivityPublisherExtensions.swift in Sources */, 0EE60EFD25FCCCD700D97E7A /* CheckmarkButtonImage.swift in Sources */, + 0EC2459D26340311001AFC4B /* SystemMessageRequest.swift in Sources */, 0E415B6F25CD772200351672 /* AccountInfoView.swift in Sources */, - 0E3AC40025F7778400A4278A /* AvatarGenerator.swift in Sources */, - 0E3E6955260E08EC005E8DA2 /* UIDevice+isPad.swift in Sources */, - 0E906D1425DCEADD0034053C /* EditAttentionRequest.swift in Sources */, - 0E906D0025DCEADD0034053C /* SystemMessageRequest.swift in Sources */, + 0EC245032634022C001AFC4B /* HollowApp.swift in Sources */, + 0EC2459B26340311001AFC4B /* PostListRequest.swift in Sources */, + 0EC245AD26340311001AFC4B /* LoginRequest.swift in Sources */, 0E415B7625CD772200351672 /* WanderView.swift in Sources */, - 0E906D3225DCEADD0034053C /* ImageSaver.swift in Sources */, - 0E906D1825DCEADD0034053C /* SendVoteRequest.swift in Sources */, - 0EA3C16225FEFCA600865B4D /* DeviceModelUtilities.swift in Sources */, + 0EC246222634040D001AFC4B /* PostListRequestStore.swift in Sources */, + 0EC244FA26340211001AFC4B /* AppModel.swift in Sources */, 0E415B7925CD772200351672 /* LoginView.swift in Sources */, - 0E906D2E25DCEADD0034053C /* String+Date.swift in Sources */, - 0E906CFB25DCEADD0034053C /* PostDetailRequest.swift in Sources */, - 0EAA1B3B25E511BA00904296 /* PublisherExtensions.swift in Sources */, + 0EC244E026340172001AFC4B /* CrossPlatformTypes.swift in Sources */, + 0EC245ED26340312001AFC4B /* Bool+int.swift in Sources */, 0EA2D51D25F11EA9003D539E /* ReportMenuContent.swift in Sources */, + 0EC2458926340311001AFC4B /* DeviceInformationType.swift in Sources */, + 0EC245B526340311001AFC4B /* UpdateDeviceTokenRequest.swift in Sources */, 0E415B5225CD772200351672 /* CustomGradient.swift in Sources */, - 0EA4298B25EB6F4D00B74D5D /* Double+int.swift in Sources */, 0E415B6125CD772200351672 /* HollowInputView.swift in Sources */, 0EB4F9A225F37A0E00BDAFF1 /* Text+highlight.swift in Sources */, - 0E4786C825E3A93E0004F8FA /* RequestPublisher.swift in Sources */, + 0EC245BB26340312001AFC4B /* ChangePasswordRequest.swift in Sources */, 0E415B6E25CD772200351672 /* AboutView.swift in Sources */, 0E415B7525CD772200351672 /* SettingsView.swift in Sources */, - 0E906D2A25DCEADD0034053C /* Int+bool.swift in Sources */, + 0EC245B126340311001AFC4B /* EmailCheckRequest.swift in Sources */, 0E415B7A25CD772200351672 /* LoginSubViews.swift in Sources */, + 0EC246202634040D001AFC4B /* HollowInputStore.swift in Sources */, 0EFB54E525E8D9FD001BC98A /* View+modalPresent.swift in Sources */, - 0E906D0425DCEADD0034053C /* AttentionListSearchRequest.swift in Sources */, - 00EFC07125DFC71500536B52 /* Vote.swift in Sources */, - 0E906D2C25DCEADD0034053C /* Int+string.swift in Sources */, 0E8BCFE325E095A90056D809 /* StyledAlert.swift in Sources */, 0EA2D51A25F0FA05003D539E /* View+defaultBlurBackground.swift in Sources */, - 0E906D0F25DCEADD0034053C /* AccountCreationRequest.swift in Sources */, 0E415B4325CD772200351672 /* ErrorAlert.swift in Sources */, - 0E415B8025CD772200351672 /* PostListRequestStore.swift in Sources */, - 0E906D0E25DCEADD0034053C /* DeviceTerminationRequest.swift in Sources */, 0E415B7725CD772200351672 /* MainView.swift in Sources */, - 0E906D0225DCEADD0034053C /* RandomListRequest.swift in Sources */, + 0EC245A126340311001AFC4B /* RandomListRequest.swift in Sources */, 0E36F54025D0E02F001AE852 /* GetSize.swift in Sources */, + 0EC245F926340312001AFC4B /* OpenURLHelper.swift in Sources */, 0E2C1EE425D7A04C00A296E9 /* MyButtonDefaultStyle.swift in Sources */, - 0E906D0B25DCEADD0034053C /* EmailCheckRequest.swift in Sources */, + 0EC245D926340312001AFC4B /* String+SHA256.swift in Sources */, + 0EC245B926340311001AFC4B /* AccountCreationRequest.swift in Sources */, 0E28BC4525F5F8A000A97820 /* View+defaultListStyle.swift in Sources */, 0E415B4425CD772200351672 /* CustomTabView.swift in Sources */, - 0E906D2125DCEADD0034053C /* HollowImage.swift in Sources */, 0EF10F6626330F29004D6538 /* HollowDetailViewController.swift in Sources */, 0E58810F25EA0F47006F6A94 /* View+presentPopover.swift in Sources */, - 0E906D2625DCEADD0034053C /* Data+hexEncodedString.swift in Sources */, - 0E3E695B260E166D005E8DA2 /* HollowDateFormatter.swift in Sources */, - 0E415B9B25CD93C000351672 /* AppDelegate.swift in Sources */, 0EDA262725D22C8600B67292 /* DeviceListView.swift in Sources */, 0E386D8E2613101B000568B6 /* View+refreshNavigationItem.swift in Sources */, + 0EC2458F26340311001AFC4B /* CommentData.swift in Sources */, + 0EC245F326340312001AFC4B /* PublisherExtensions.swift in Sources */, + 0EC2461E2634040D001AFC4B /* ImageCompressStore.swift in Sources */, + 0EC2459126340311001AFC4B /* PostData.swift in Sources */, 0E415B6825CD772200351672 /* HollowTextView.swift in Sources */, 0E6EF31B25F9DFDE001293FE /* View+dynamicFont.swift in Sources */, 0E186C39262C60A1001A5855 /* View+showToast.swift in Sources */, + 0EC245E126340312001AFC4B /* String+removeLineBreak.swift in Sources */, 0E415B4625CD772200351672 /* CustomScrollView.swift in Sources */, - 0E906D0925DCEADD0034053C /* LoginRequest.swift in Sources */, 0E28BC4225F5E29D00A97820 /* CustomColorScheme.swift in Sources */, - 0E906D1A25DCEADD0034053C /* Comment.swift in Sources */, 0EB45E3E261209A300409227 /* View+presentStyledAlert.swift in Sources */, 0E415B4C25CD772200351672 /* ViewLayouts.swift in Sources */, 0E3E694E260DFCE8005E8DA2 /* Text+dateText.swift in Sources */, + 0EC246262634040D001AFC4B /* AccountInfoStore.swift in Sources */, 0E09B7B125D4E7E800DC5615 /* BarImageButton.swift in Sources */, + 0EC2452626340301001AFC4B /* ImageSaver.swift in Sources */, + 0EC244E226340172001AFC4B /* CrossPlatformImage.swift in Sources */, 0E415B5825CD772200351672 /* MyTextField.swift in Sources */, 0E415B5925CD772200351672 /* ExpandedButton.swift in Sources */, 0ED82F0026103FB600A799C0 /* View+defaultPadding.swift in Sources */, - 0E906D2925DCEADD0034053C /* DefaultsKeys.swift in Sources */, + 0EC244F626340211001AFC4B /* AppModelBehaviour.swift in Sources */, 0E415B4225CD772200351672 /* ViewConstants.swift in Sources */, + 0EC245D726340312001AFC4B /* RequestError.swift in Sources */, 0E415B5125CD772200351672 /* View+topBar.swift in Sources */, + 0EC2459326340311001AFC4B /* HollowType.swift in Sources */, + 0EC2452326340301001AFC4B /* ConnectivityPublisherExtensions.swift in Sources */, + 0EC245F726340312001AFC4B /* AvatarGenerator.swift in Sources */, 0E415B6D25CD772200351672 /* HollowDetailView.swift in Sources */, + 0EC245EF26340312001AFC4B /* String+Date.swift in Sources */, + 0EC2458726340311001AFC4B /* Vote.swift in Sources */, 0E415B7B25CD772200351672 /* WelcomeView.swift in Sources */, + 0EC245E726340312001AFC4B /* String+regex.swift in Sources */, 0E415B4B25CD772200351672 /* CustomAnimations.swift in Sources */, - 0E415B8425CD772200351672 /* WelcomeStore.swift in Sources */, + 0EC2457B26340311001AFC4B /* HollowConfig.swift in Sources */, + 0EC245DF26340312001AFC4B /* Int+bool.swift in Sources */, 0EFB54FA25E92CB0001BC98A /* SearchSubViews.swift in Sources */, - 0EDA262B25D22CBF00B67292 /* DeviceListStore.swift in Sources */, + 0EC245C926340312001AFC4B /* EditAttentionRequest.swift in Sources */, 0E09B7AA25D4D73100DC5615 /* ImageViewer.swift in Sources */, - 0E906D2525DCEADD0034053C /* String+SHA256.swift in Sources */, - 0E906D1D25DCEADD0034053C /* Post.swift in Sources */, - 0E09B7BB25D52F4F00DC5615 /* HollowInputStore.swift in Sources */, 0EA3C15E25FEFBAA00865B4D /* View+singleLineText.swift in Sources */, + 0EC2459926340311001AFC4B /* SearchRequest.swift in Sources */, 0E85DFC525DB71F2003D134B /* IndicatorOverlay.swift in Sources */, + 0EC245B326340311001AFC4B /* DeviceListRequest.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0EC244BE263400C5001AFC4B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0EC245D826340312001AFC4B /* RequestError.swift in Sources */, + 0EC24649263407D3001AFC4B /* WelcomeView.swift in Sources */, + 0EC245D226340312001AFC4B /* SendVoteRequest.swift in Sources */, + 0EC2459E26340311001AFC4B /* SystemMessageRequest.swift in Sources */, + 0EC244FD26340211001AFC4B /* AppDelegateShared.swift in Sources */, + 0EC245BC26340312001AFC4B /* ChangePasswordRequest.swift in Sources */, + 0EC244C7263400C6001AFC4B /* ContentView.swift in Sources */, + 0EC2462B2634040D001AFC4B /* MessageStore.swift in Sources */, + 0EC245A026340311001AFC4B /* AttentionListRequest.swift in Sources */, + 0EC245FC26340312001AFC4B /* LineSwitchManager.swift in Sources */, + 0ED087D126354556003C2E11 /* PostListView.swift in Sources */, + 0EC2461F2634040D001AFC4B /* ImageCompressStore.swift in Sources */, + 0EC245A226340311001AFC4B /* RandomListRequest.swift in Sources */, + 0EC244EB263401E4001AFC4B /* AppDelegate.swift in Sources */, + 0EC2457C26340311001AFC4B /* HollowConfig.swift in Sources */, + 0EC246252634040D001AFC4B /* HollowDetailStore.swift in Sources */, + 0EC2465C2634116A001AFC4B /* AppModelBehaviour.swift in Sources */, + 0EC2459626340311001AFC4B /* PostDetailRequest.swift in Sources */, + 0EC245DC26340312001AFC4B /* Data+hexEncodedString.swift in Sources */, + 0EC244E326340172001AFC4B /* CrossPlatformImage.swift in Sources */, + 0EC2459426340311001AFC4B /* HollowType.swift in Sources */, + 0EC245EC26340312001AFC4B /* Array+sortedRange.swift in Sources */, + 0EC2460926340358001AFC4B /* ViewConstants.swift in Sources */, + 0EC246212634040D001AFC4B /* HollowInputStore.swift in Sources */, + 0EC245AA26340311001AFC4B /* GetPushRequest.swift in Sources */, + 0EC245FE26340312001AFC4B /* DeviceModelUtilities.swift in Sources */, + 0EC245D426340312001AFC4B /* Request+publisher.swift in Sources */, + 0EC2458C26340311001AFC4B /* VoteData.swift in Sources */, + 0EC2458426340311001AFC4B /* PushNotificationType.swift in Sources */, + 0EC2457A26340311001AFC4B /* Constants.swift in Sources */, + 0ED1A16126346C37001453A7 /* ReCAPTCHAWebView.swift in Sources */, + 0EC245F626340312001AFC4B /* HollowDateFormatter.swift in Sources */, + 0EC245F826340312001AFC4B /* AvatarGenerator.swift in Sources */, + 0EC2464D263407E4001AFC4B /* LoginView.swift in Sources */, + 0EC245E226340312001AFC4B /* String+removeLineBreak.swift in Sources */, + 0EC2462D2634040D001AFC4B /* WelcomeStore.swift in Sources */, + 0EC2459226340311001AFC4B /* PostData.swift in Sources */, + 0EC245F226340312001AFC4B /* DateExtensions.swift in Sources */, + 0EC24651263408CC001AFC4B /* AppModelEnvironment.swift in Sources */, + 0EC245F426340312001AFC4B /* PublisherExtensions.swift in Sources */, + 0EC245E426340312001AFC4B /* Int+string.swift in Sources */, + 0EC245D026340312001AFC4B /* SendCommentRequest.swift in Sources */, + 0EC2457E26340311001AFC4B /* Comment.swift in Sources */, + 0EC245E026340312001AFC4B /* Int+bool.swift in Sources */, + 0EC245C626340312001AFC4B /* ReportPostRequest.swift in Sources */, + 0EC245F026340312001AFC4B /* String+Date.swift in Sources */, + 0EC245E626340312001AFC4B /* Array+removeDuplicates.swift in Sources */, + 0ED1A16B26346D28001453A7 /* ViewLayouts.swift in Sources */, + 0EC245DA26340312001AFC4B /* String+SHA256.swift in Sources */, + 0EC245CE26340312001AFC4B /* SendPostRequest.swift in Sources */, + 0EC245B026340311001AFC4B /* LogoutRequest.swift in Sources */, + 0EC2458826340311001AFC4B /* Vote.swift in Sources */, + 0EC245B826340311001AFC4B /* DeviceTerminationRequest.swift in Sources */, + 0EC246272634040D001AFC4B /* AccountInfoStore.swift in Sources */, + 0EC2458626340311001AFC4B /* Post.swift in Sources */, + 0EC2459A26340311001AFC4B /* SearchRequest.swift in Sources */, + 0EC245FA26340312001AFC4B /* OpenURLHelper.swift in Sources */, + 0EC245AC26340311001AFC4B /* SetPushRequest.swift in Sources */, + 0EC2465626340B71001AFC4B /* View+showAlert.swift in Sources */, + 0EC246232634040D001AFC4B /* PostListRequestStore.swift in Sources */, + 0EC2459826340311001AFC4B /* SearchHistoryRequest.swift in Sources */, + 0E4E4BC52634619A00B32F7F /* View+roundedCorner.swift in Sources */, + 0EC245B226340311001AFC4B /* EmailCheckRequest.swift in Sources */, + 0EC245C826340312001AFC4B /* ReportCommentRequest.swift in Sources */, + 0EC244E126340172001AFC4B /* CrossPlatformTypes.swift in Sources */, + 0EC245E826340312001AFC4B /* String+regex.swift in Sources */, + 0EC245D626340312001AFC4B /* RequestPublisher.swift in Sources */, + 0EC245A826340311001AFC4B /* GetConfigRequest.swift in Sources */, + 0EC2459C26340311001AFC4B /* PostListRequest.swift in Sources */, + 0EC245AE26340311001AFC4B /* LoginRequest.swift in Sources */, + 0EC2458026340311001AFC4B /* ImageMetadata.swift in Sources */, + 0EC245C026340312001AFC4B /* FetchImageRequest.swift in Sources */, + 0EC245BA26340312001AFC4B /* AccountCreationRequest.swift in Sources */, + 0ED1A16826346CB1001453A7 /* ReCAPTCHAPageView.swift in Sources */, + 0EC245CC26340312001AFC4B /* Request.swift in Sources */, + 0EC245DE26340312001AFC4B /* DefaultsKeys.swift in Sources */, + 0EC245A426340311001AFC4B /* AttentionListSearchRequest.swift in Sources */, + 0EC2459026340311001AFC4B /* CommentData.swift in Sources */, + 0ED087BF263480C9003C2E11 /* LoginSubViews.swift in Sources */, + 0EC245EE26340312001AFC4B /* Bool+int.swift in Sources */, + 0EC2463426340481001AFC4B /* ImageCompressor.swift in Sources */, + 0EC2458E26340311001AFC4B /* HollowImage.swift in Sources */, + 0EC2462F2634040D001AFC4B /* LoginStore.swift in Sources */, + 0EC2458226340311001AFC4B /* SystemMessage.swift in Sources */, + 0EC245C226340312001AFC4B /* ReportRequestGroup.swift in Sources */, + 0EC245EA26340312001AFC4B /* Double+int.swift in Sources */, + 0EC245B426340311001AFC4B /* DeviceListRequest.swift in Sources */, + 0EC245A626340311001AFC4B /* DefaultRequest.swift in Sources */, + 0EC246292634040D001AFC4B /* DeviceListStore.swift in Sources */, + 0EC2463F26340770001AFC4B /* AppModel.swift in Sources */, + 0EC2458A26340311001AFC4B /* DeviceInformationType.swift in Sources */, + 0EC245BE26340312001AFC4B /* PostListRequestGroup.swift in Sources */, + 0EC2460026340312001AFC4B /* PostCache.swift in Sources */, + 0EC245CA26340312001AFC4B /* EditAttentionRequest.swift in Sources */, + 0EC245B626340311001AFC4B /* UpdateDeviceTokenRequest.swift in Sources */, + 0EC244C5263400C6001AFC4B /* HollowMacApp.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1368,7 +1815,7 @@ MARKETING_VERSION = 3.0.6; PRODUCT_BUNDLE_IDENTIFIER = treehollow.Hollow; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTS_MACCATALYST = YES; + SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -1394,12 +1841,62 @@ MARKETING_VERSION = 3.0.6; PRODUCT_BUNDLE_IDENTIFIER = treehollow.Hollow; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTS_MACCATALYST = YES; + SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; + 0EC244CF263400C7001AFC4B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = HollowMac/HollowMac.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_ASSET_PATHS = ""; + DEVELOPMENT_TEAM = C5UH93T368; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = HollowMac/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 11.0; + PRODUCT_BUNDLE_IDENTIFIER = treehollow.HollowMac; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 0EC244D0263400C7001AFC4B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = HollowMac/HollowMac.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_ASSET_PATHS = ""; + DEVELOPMENT_TEAM = C5UH93T368; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = HollowMac/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 11.0; + PRODUCT_BUNDLE_IDENTIFIER = treehollow.HollowMac; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -1421,6 +1918,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 0EC244D1263400C7001AFC4B /* Build configuration list for PBXNativeTarget "HollowMac" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0EC244CF263400C7001AFC4B /* Debug */, + 0EC244D0263400C7001AFC4B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ @@ -1536,6 +2042,41 @@ package = 0E671AC7260F394500590736 /* XCRemoteSwiftPackageReference "Cache" */; productName = Cache; }; + 0EC245082634029D001AFC4B /* WaterfallGrid */ = { + isa = XCSwiftPackageProductDependency; + package = 0E28BC1425F4ED1700A97820 /* XCRemoteSwiftPackageReference "WaterfallGrid" */; + productName = WaterfallGrid; + }; + 0EC2450A2634029D001AFC4B /* Defaults */ = { + isa = XCSwiftPackageProductDependency; + package = 0E28BC1725F4ED2D00A97820 /* XCRemoteSwiftPackageReference "Defaults" */; + productName = Defaults; + }; + 0EC2450C2634029D001AFC4B /* Alamofire */ = { + isa = XCSwiftPackageProductDependency; + package = 0E28BC1A25F4ED8200A97820 /* XCRemoteSwiftPackageReference "Alamofire" */; + productName = Alamofire; + }; + 0EC2450E2634029D001AFC4B /* AppCenterAnalytics */ = { + isa = XCSwiftPackageProductDependency; + package = 0E28BC1E25F4EE7B00A97820 /* XCRemoteSwiftPackageReference "appcenter-sdk-apple" */; + productName = AppCenterAnalytics; + }; + 0EC245102634029D001AFC4B /* AppCenterCrashes */ = { + isa = XCSwiftPackageProductDependency; + package = 0E28BC1E25F4EE7B00A97820 /* XCRemoteSwiftPackageReference "appcenter-sdk-apple" */; + productName = AppCenterCrashes; + }; + 0EC245122634029D001AFC4B /* Kingfisher */ = { + isa = XCSwiftPackageProductDependency; + package = 0E28BC2B25F4EF4D00A97820 /* XCRemoteSwiftPackageReference "Kingfisher" */; + productName = Kingfisher; + }; + 0EC245142634029D001AFC4B /* Cache */ = { + isa = XCSwiftPackageProductDependency; + package = 0E671AC7260F394500590736 /* XCRemoteSwiftPackageReference "Cache" */; + productName = Cache; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 0E415A6825CD769B00351672 /* Project object */; diff --git a/Source/Hollow/App/AppDelegate.swift b/Source/Hollow/App/AppDelegate.swift index 179797e8..52baae4a 100644 --- a/Source/Hollow/App/AppDelegate.swift +++ b/Source/Hollow/App/AppDelegate.swift @@ -6,144 +6,22 @@ // Copyright © 2021 treehollow. All rights reserved. // -import SwiftUI +import UIKit import Defaults -import AppCenter -import AppCenterAnalytics -import AppCenterCrashes -import Connectivity class AppDelegate: UIResponder, UIApplicationDelegate { // MARK: - UIApplicationDelegate func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { - #if !DEBUG - // Start AppCenter services - AppCenter.start( - withAppSecret: "aae3c20c-75f5-4840-96f3-541cd7e6dd88", - services: [Analytics.self, Crashes.self] - ) - #endif - Defaults[.customColorSet] = Defaults[.tempCustomColorSet] Defaults[.applyCustomColorSet] = Defaults[.customColorSet] != nil - - // Setup remote notifications - setupRemoteNotifications(application) - - // Fetch the lastest config - fetchConfig() - + setupApplication(application) return true } func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { - // Temporarily save the token in defaults. We are not using this - // default except registering or logging in for the first time. - Defaults[.deviceToken] = deviceToken - - // Try to send the token to the server, if we have a user token. - if let accessToken = Defaults[.accessToken] { - sendDeviceToken(deviceToken, withAccessToken: accessToken) - } + didRegisterForRemoteNotifications(with: deviceToken) } func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { - print("Fail to register remote notification with error: \(error.localizedDescription)") - } - - #if targetEnvironment(macCatalyst) - override func buildMenu(with builder: UIMenuBuilder) { - builder.remove(menu: .file) - builder.remove(menu: .edit) - builder.remove(menu: .format) - builder.remove(menu: .help) - builder.remove(menu: .services) - builder.remove(menu: .toolbar) - } - #endif -} - -// MARK: - Tree Hollow Configuration & Setup -extension AppDelegate { - func setupRemoteNotifications(_ application: UIApplication) { - // Request notification access - let center = UNUserNotificationCenter.current() - center.delegate = self - let authorizationOptions = Constants.Application.requestedNotificationOptions - center.requestAuthorization(options: authorizationOptions) { granted, error in - guard granted else { return } - // Register for APN - DispatchQueue.main.async { - application.registerForRemoteNotifications() - } - } - } - - private func sendDeviceToken(_ deviceToken: Data, withAccessToken accessToken: String) { - guard let config = Defaults[.hollowConfig] else { return } - let configuration = UpdateDeviceTokenRequestConfiguration(deviceToken: deviceToken, token: accessToken, apiRoot: config.apiRootUrls) - let request = UpdateDeviceTokenRequest(configuration: configuration) - - request.performRequest(completion: { result, error in - if let error = error { - print(error) - // TODO: Handle error - } - }) - } - - private func fetchConfig() { - guard let hollowType = Defaults[.hollowType] else { return } - var configURL: String? { - switch hollowType { - case .thu: return Constants.HollowConfig.thuConfigURL - case .pku: return Constants.HollowConfig.pkuConfigURL - case .other: - return Defaults[.customConfigURL] - } - } - - guard let urlString = configURL else { return } - let request = GetConfigRequest(configuration: GetConfigRequestConfiguration(hollowType: hollowType, customAPIRoot: urlString)!) - - request.performRequest(completion: { result, error in - if let _ = error { - // TODO: Handle error - return - } - - if let result = result { - // Update the config and test connectivity - Defaults[.hollowConfig] = result - LineSwitchManager.testAll() - } - }) - } -} - -// MARK: - User Notifications -extension AppDelegate: UNUserNotificationCenterDelegate { - func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { - let content = response.notification.request.content - - if let type = content.userInfo["type"] as? Int, type == 1 { - // System message for type 1 - let messageView = MessageView(presented: .constant(true), page: .message, selfDismiss: true) - IntegrationUtilities.presentView(content: { messageView }) - } else if let postId = content.userInfo["pid"] as? Int { - let commentId = content.userInfo["cid"] as? Int - IntegrationUtilities.openTemplateDetailView(postId: postId, jumpToComment: commentId) - } - - completionHandler() - } - - func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { - - if notification.request.content.userInfo["delete"] == nil { - completionHandler([.banner, .sound, .list]) - } else { - completionHandler([]) - } } } diff --git a/Source/Hollow/App/AppModel.swift b/Source/Hollow/App/AppModel.swift index 5256ea5a..e66c9ee1 100644 --- a/Source/Hollow/App/AppModel.swift +++ b/Source/Hollow/App/AppModel.swift @@ -8,7 +8,11 @@ import Combine import Defaults + +#if canImport(Rechability) import Connectivity +#endif + import SwiftUI class AppModel: ObservableObject { @@ -16,6 +20,7 @@ class AppModel: ObservableObject { @Published var isInMainView = Defaults[.accessToken] != nil && Defaults[.hollowConfig] != nil + #if canImport(Rechability) var connectedToNetwork = Connectivity().status.isConnected init() { @@ -27,4 +32,5 @@ class AppModel: ObservableObject { }) .store(in: &cancellables) } + #endif } diff --git a/Source/Hollow/App/AppModelBehaviour.swift b/Source/Hollow/App/AppModelBehaviour.swift index e91dd905..e91ffb87 100644 --- a/Source/Hollow/App/AppModelBehaviour.swift +++ b/Source/Hollow/App/AppModelBehaviour.swift @@ -8,6 +8,7 @@ import SwiftUI import Defaults +import UserNotifications /// Modifier for views that take control of the behaviour of the app /// using the model injected in the environment. diff --git a/Source/Hollow/App/AppModelEnvironment.swift b/Source/Hollow/App/AppModelEnvironment.swift index 9993ff45..b8fc0066 100644 --- a/Source/Hollow/App/AppModelEnvironment.swift +++ b/Source/Hollow/App/AppModelEnvironment.swift @@ -28,7 +28,11 @@ extension AppModelEnvironment { case .tokenExpiredError: Defaults[.accessToken] = nil appModelState.shouldShowMainView = false + // FIXME: Show Alert in macOS + #if !os(macOS) ToastManager.shared.show(configuration: .error(title: nil, body: NSLocalizedString("WELCOMVIEW_TOKEN_EXPIRED_LABEL", comment: ""))) + #endif + return true default: return false } diff --git a/Source/Hollow/App/HollowApp.swift b/Source/Hollow/App/HollowApp.swift index 468f970f..10d28707 100644 --- a/Source/Hollow/App/HollowApp.swift +++ b/Source/Hollow/App/HollowApp.swift @@ -29,8 +29,6 @@ struct HollowApp: App { WelcomeView() } } - // Set larger size category for macOS - .conditionalSizeCategory() // Inject the app model into the environment .environmentObject(appModel) // Set the color scheme when appear diff --git a/Source/Hollow/Model/Extensions/UIDevice+isPad.swift b/Source/Hollow/Model/Extensions/UIDevice+isPad.swift index 781dfc28..dc32abdf 100644 --- a/Source/Hollow/Model/Extensions/UIDevice+isPad.swift +++ b/Source/Hollow/Model/Extensions/UIDevice+isPad.swift @@ -10,9 +10,5 @@ import UIKit extension UIDevice { static let isPad = UIDevice.current.userInterfaceIdiom == .pad - #if targetEnvironment(macCatalyst) - static let isMac = true - #else static let isMac = false - #endif } diff --git a/Source/Hollow/View/Components/LoadingLabel.swift b/Source/Hollow/View/Components/LoadingLabel.swift index 24639545..8e7a60c8 100644 --- a/Source/Hollow/View/Components/LoadingLabel.swift +++ b/Source/Hollow/View/Components/LoadingLabel.swift @@ -2,7 +2,7 @@ // LoadingLabel.swift // Hollow // -// Created by 梁业升 on 2021/2/10. +// Created by liang2kl on 2021/2/10. // Copyright © 2021 treehollow. All rights reserved. // diff --git a/Source/Hollow/View/Components/SearchBar.swift b/Source/Hollow/View/Components/SearchBar.swift index fb4b760a..b8f6456d 100644 --- a/Source/Hollow/View/Components/SearchBar.swift +++ b/Source/Hollow/View/Components/SearchBar.swift @@ -2,7 +2,7 @@ // SearchBar.swift // Hollow // -// Created by 梁业升 on 2021/3/3. +// Created by liang2kl on 2021/3/3. // Copyright © 2021 treehollow. All rights reserved. // diff --git a/Source/Hollow/View/Hierarchy/Hollow/Content/HollowVoteContentView.swift b/Source/Hollow/View/Hierarchy/Hollow/Content/HollowVoteContentView.swift index 45659d24..82daff06 100644 --- a/Source/Hollow/View/Hierarchy/Hollow/Content/HollowVoteContentView.swift +++ b/Source/Hollow/View/Hierarchy/Hollow/Content/HollowVoteContentView.swift @@ -70,9 +70,10 @@ struct HollowVoteContentView: View { Spacer() // Not displaying the vote result if not voted if voted { - let percentage = Int(Double(100 * voteData.voteCount) / Double(totalCount)) + let percentage = Double(100 * voteData.voteCount) / Double(totalCount) + let percentageString = String(format: "%.1f", percentage) let text = showProportion ? - percentage.string + (voteData.voteCount == 0 ? "" : "%") : + percentageString + (voteData.voteCount == 0 ? "" : "%") : voteData.voteCount.string Text(text).bold() diff --git a/Source/Hollow/View/Hierarchy/Login/LoginSubViews.swift b/Source/Hollow/View/Hierarchy/Login/LoginSubViews.swift index db0bc670..f1f92835 100644 --- a/Source/Hollow/View/Hierarchy/Login/LoginSubViews.swift +++ b/Source/Hollow/View/Hierarchy/Login/LoginSubViews.swift @@ -11,43 +11,7 @@ import Defaults // Sub views of `LoginView`, we put them here to // improve code hightlight and completion performance. -extension LoginView { - struct ReCAPTCHAPageView: View { - @Binding var presented: Bool - let successHandler: (String) -> Void - @State private var pageLoadingFinish = false - - var body: some View { - VStack { - Button(action: { - withAnimation { - presented = false - } - }) { - Image(systemName: "xmark") - .modifier(ImageButtonModifier()) - .padding(.bottom) - } - .leading() - ReCAPTCHAWebView(onFinishLoading: { - withAnimation { - pageLoadingFinish = true - } - }, successHandler: successHandler) - .onAppear { - pageLoadingFinish = false - } - } - .padding() - .overlay(Group { - if !pageLoadingFinish { - Spinner(color: .buttonGradient1, desiredWidth: 30) - } - }) - - } - } - +extension LoginView { struct RegisterTextFields: View { @EnvironmentObject var viewModel: LoginStore @Environment(\.openURL) var openURL diff --git a/Source/HollowMac/App/AppDelegate.swift b/Source/HollowMac/App/AppDelegate.swift new file mode 100644 index 00000000..3a9cfbf2 --- /dev/null +++ b/Source/HollowMac/App/AppDelegate.swift @@ -0,0 +1,23 @@ +// +// AppDelegate.swift +// HollowMac +// +// Created by liang2kl on 2021/4/24. +// Copyright © 2021 treehollow. All rights reserved. +// + +import AppKit + +class AppDelegate: NSObject, NSApplicationDelegate { + func applicationDidFinishLaunching(_ notification: Notification) { + setupApplication(NSApplication.shared) + } + + func application(_ application: NSApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { + didRegisterForRemoteNotifications(with: deviceToken) + } + + func application(_ application: NSApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { + + } +} diff --git a/Source/HollowMac/App/AppModel.swift b/Source/HollowMac/App/AppModel.swift new file mode 100644 index 00000000..5cd10d9c --- /dev/null +++ b/Source/HollowMac/App/AppModel.swift @@ -0,0 +1,14 @@ +// +// AppModel.swift +// HollowMac +// +// Created by liang2kl on 2021/4/24. +// Copyright © 2021 treehollow. All rights reserved. +// + +import Combine +import Defaults + +class AppModel: ObservableObject { + @Published var isInMainView = Defaults[.accessToken] != nil +} diff --git a/Source/HollowMac/App/AppModelBehaviour.swift b/Source/HollowMac/App/AppModelBehaviour.swift new file mode 100644 index 00000000..3363b0bf --- /dev/null +++ b/Source/HollowMac/App/AppModelBehaviour.swift @@ -0,0 +1,56 @@ +// +// AppModelBehaviour.swift +// HollowMac +// +// Created by liang2kl on 2021/4/24. +// Copyright © 2021 treehollow. All rights reserved. +// + +import SwiftUI +import Defaults +import UserNotifications + +/// Modifier for views that take control of the behaviour of the app +/// using the model injected in the environment. +/// +/// Supply `appModelState` (typically in the view model) to access **indirect** +/// control of the environment object in the view model. +struct AppModelBehaviour: ViewModifier { + // Fetch the app model in the environment inside + // this modifier to get rid of doing so in every view. + @EnvironmentObject var appModel: AppModel + + var state: AppModelState + + func body(content: Content) -> some View { + content + // Use `onChange` to update the app model corresponding + // to the internally stored state. Be careful that `onChange` + // will be called when the state variable is initialized. So + // remember to provide a correct initial value. + + // Handle entering main view + .onChange(of: state.shouldShowMainView) { show in + withAnimation { appModel.isInMainView = show } + if !show { restore() } + } + +// .onChange(of: state.isLoggingIn) { loggingIn in +// withAnimation { appModel.isLoggingIn = loggingIn } +// print(appModel.isLoggingIn) +// } + + } + + private func restore() { + restoreDefaults() + UNUserNotificationCenter.current().removeAllDeliveredNotifications() + } + + private func restoreDefaults() { + Defaults.Keys.customConfigURL.reset() + Defaults.Keys.accessToken.reset() + Defaults.Keys.hiddenAnnouncement.reset() + Defaults.Keys.deviceListCache.reset() + } +} diff --git a/Source/HollowMac/App/AppModelEnvironment.swift b/Source/HollowMac/App/AppModelEnvironment.swift new file mode 100644 index 00000000..e3fbdba0 --- /dev/null +++ b/Source/HollowMac/App/AppModelEnvironment.swift @@ -0,0 +1,46 @@ +// +// AppModelEnvironment.swift +// HollowMac +// +// Created by liang2kl on 2021/4/24. +// Copyright © 2021 treehollow. All rights reserved. +// + +import SwiftUI +import Defaults + +protocol AppModelEnvironment: ObservableObject { + var appModelState: AppModelState { get set } +} + +extension AppModelEnvironment { + /// Default implementation to handle token expire error. + /// + /// - parameter error: The request error + /// - returns: `true` if the error is handled and should return, `false` otherwise. + func handleTokenExpireError(_ error: DefaultRequestError) -> Bool { + switch error { + case .tokenExpiredError: + Defaults[.accessToken] = nil + appModelState.shouldShowMainView = false + // TODO: Show Alert + + return true + default: return false + } + } + + /// Error handler for default requests. + func defaultErrorHandler(errorMessage: inout (title: String, message: String)?, error: DefaultRequestError) { + if handleTokenExpireError(error) { return } + if error.loadingCompleted() { return } + errorMessage = (title: "", message: error.description) + } +} + +/// State definition reflecting the view model. +struct AppModelState { + var errorMessage: (title: String, message: String)? + + var shouldShowMainView = Defaults[.accessToken] != nil +} diff --git a/Source/HollowMac/App/HollowMacApp.swift b/Source/HollowMac/App/HollowMacApp.swift new file mode 100644 index 00000000..3306282e --- /dev/null +++ b/Source/HollowMac/App/HollowMacApp.swift @@ -0,0 +1,35 @@ +// +// HollowMacApp.swift +// HollowMac +// +// Created by liang2kl on 2021/4/24. +// Copyright © 2021 treehollow. All rights reserved. +// + +import SwiftUI +import Defaults + +@main +struct HollowMacApp: App { + @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate + @StateObject var appModel = AppModel() + + init() { + Defaults[.accessToken] = nil + } + + var body: some Scene { + WindowGroup { + if appModel.isInMainView { + + } else { + WelcomeView() +// .disabled(appModel.isLoggingIn) + .environmentObject(appModel) + } + + } + .windowStyle(HiddenTitleBarWindowStyle()) + .windowToolbarStyle(ExpandedWindowToolbarStyle()) + } +} diff --git a/Source/HollowMac/Assets.xcassets/AccentColor.colorset/Contents.json b/Source/HollowMac/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..eb878970 --- /dev/null +++ b/Source/HollowMac/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Source/HollowMac/Assets.xcassets/AppIcon.appiconset/Contents.json b/Source/HollowMac/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..3f00db43 --- /dev/null +++ b/Source/HollowMac/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,58 @@ +{ + "images" : [ + { + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Source/HollowMac/Assets.xcassets/Contents.json b/Source/HollowMac/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Source/HollowMac/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Source/HollowMac/ContentView.swift b/Source/HollowMac/ContentView.swift new file mode 100644 index 00000000..bf6a80ff --- /dev/null +++ b/Source/HollowMac/ContentView.swift @@ -0,0 +1,22 @@ +// +// ContentView.swift +// HollowMac +// +// Created by liang2kl on 2021/4/24. +// Copyright © 2021 treehollow. All rights reserved. +// + +import SwiftUI + +struct ContentView: View { + var body: some View { + Text("Hello, world!") + .padding() + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView() + } +} diff --git a/Source/HollowMac/HollowMac.entitlements b/Source/HollowMac/HollowMac.entitlements new file mode 100644 index 00000000..625af03d --- /dev/null +++ b/Source/HollowMac/HollowMac.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.files.user-selected.read-only + + com.apple.security.network.client + + + diff --git a/Source/HollowMac/Info.plist b/Source/HollowMac/Info.plist new file mode 100644 index 00000000..97087891 --- /dev/null +++ b/Source/HollowMac/Info.plist @@ -0,0 +1,39 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleURLTypes + + + CFBundleTypeRole + Viewer + CFBundleURLName + com.github.treehollow + CFBundleURLSchemes + + treehollow + + + + CFBundleVersion + 1 + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + Copyright © 2021 treehollow. All rights reserved. + + diff --git a/Source/HollowMac/View/Hierarchy/Login/LoginSubViews.swift b/Source/HollowMac/View/Hierarchy/Login/LoginSubViews.swift new file mode 100644 index 00000000..aa435cb7 --- /dev/null +++ b/Source/HollowMac/View/Hierarchy/Login/LoginSubViews.swift @@ -0,0 +1,79 @@ +// +// LoginSubViews.swift +// HollowMac +// +// Created by liang2kl on 2021/4/25. +// Copyright © 2021 treehollow. All rights reserved. +// + +import SwiftUI + +extension LoginView { + struct RegisterTextFields: View { + @EnvironmentObject var store: LoginStore + // The password is valid only if its length is no less than 8 and it contains no blank spaces. + private var passwordValid: Bool { + store.originalPassword.count >= 8 && + !store.originalPassword.contains(" ") + } + + private var confirmedPasswordValid: Bool { + store.confirmedPassword == store.originalPassword + } + + private let passwordRequirements: String = + NSLocalizedString("LOGINVIEW_PASSWORD_TEXTFIELD_REQUIREMENT_FOOTER", comment: "") + + var body: some View { + GroupBox(label: Text("Verification Code"), content: { + TextField("123456", text: $store.emailVerificationCode) + }) + + GroupBox(label: Text("Password"), content: { + HStack { + SecureField(passwordRequirements, text: $store.originalPassword) + + Group { + if store.originalPassword != "" && !passwordValid { + Image(systemName: "xmark") + .foregroundColor(.red) + } else if store.originalPassword != "" { + Image(systemName: "checkmark") + .foregroundColor(.green) + } + } + + } + + }) + + GroupBox(label: Text("Confirmed Password"), content: { + HStack { + SecureField("", text: $store.confirmedPassword) + + if passwordValid { + if store.confirmedPassword != "" && !confirmedPasswordValid { + Image(systemName: "xmark") + .foregroundColor(.red) + } else if store.confirmedPassword != "" { + Image(systemName: "checkmark") + .foregroundColor(.green) + } + } + + } + + }) + } + } + + struct LoginTextFields: View { + @EnvironmentObject var store: LoginStore + + var body: some View { + GroupBox(label: Text("Password"), content: { + SecureField("", text: $store.loginPassword) + }) + } + } +} diff --git a/Source/HollowMac/View/Hierarchy/Login/LoginView.swift b/Source/HollowMac/View/Hierarchy/Login/LoginView.swift new file mode 100644 index 00000000..bb6bd5c4 --- /dev/null +++ b/Source/HollowMac/View/Hierarchy/Login/LoginView.swift @@ -0,0 +1,128 @@ +// +// LoginView.swift +// HollowMac +// +// Created by liang2kl on 2021/4/24. +// Copyright © 2021 treehollow. All rights reserved. +// + +import SwiftUI +import Defaults + +struct LoginView: View { + @ObservedObject var store = LoginStore() + @Environment(\.presentationMode) var presentationMode + + // Determine when we should check user's email. + private var shouldCheckEmail: Bool { store.emailCheckType == nil || store.emailCheckType == .reCAPTCHANeeded } + + // Determine when the user should register with verification code and password. + private var shouldRegister: Bool { store.emailCheckType == .newUser } + + // Determine when the user should enter the password to login. + private var shouldLogin: Bool { store.emailCheckType == .oldUser } + + private var buttonText: String { + if store.isLoading { return NSLocalizedString("LOGINVIEW_BUTTON_LOADING", comment: "") + "..." } + if shouldCheckEmail { return NSLocalizedString("LOGINVIEW_BUTTON_CONTINUE", comment: "") } + if shouldRegister { return NSLocalizedString("LOGINVIEW_BUTTON_REGISTER", comment: "") } + if shouldLogin { return NSLocalizedString("LOGINVIEW_BUTTON_LOGIN", comment: "") } + return "" + } + + // Determine when to disable the interaction with the text fields + private var disableInteraction: Bool { store.isLoading } + + // Determine when to disable the button + private var disableButton: Bool { + if store.isLoading { return true } + if shouldCheckEmail { return store.email == "" } + if shouldRegister { return + store.emailVerificationCode == "" || + store.confirmedPassword != store.originalPassword || + // Invalid original password + store.originalPassword.count < 8 || + store.originalPassword.contains(" ") + } + if shouldLogin { return store.loginPassword == "" } + return true + } + + + var body: some View { + VStack(spacing: 20) { + if let title = Defaults[.hollowConfig]?.name { + Text(title) + .font(.headline) + } + GroupBox(label: Text("Email"), content: { + HStack { + TextField("", text: $store.email) + Menu(content: { + ForEach(Defaults[.hollowConfig]?.emailSuffixes ?? [], id: \.self) { suffix in + Button("\(suffix)") { + store.emailSuffix = suffix + } + } + }, label: { + Text("@\(store.emailSuffix)") + }) + .frame(maxWidth: 200) +// .menuStyle(BorderlessButtonMenuStyle()) + } + .sheet(isPresented: $store.showsRecaptcha) { + ReCAPTCHAPageView( + presented: $store.showsRecaptcha, + successHandler: { token in + withAnimation { + store.showsRecaptcha = false + store.reCAPTCHAToken = token + // Check again with the token + store.checkEmail() + } + } + ) + .frame(minWidth: 400, minHeight: 600) + } + }) + + if shouldRegister { + Divider() + RegisterTextFields().environmentObject(store) + } + + if shouldLogin { + LoginTextFields().environmentObject(store) + } + + Spacer() + } + .textFieldStyle(PlainTextFieldStyle()) + .padding() + .frame(minHeight: shouldRegister ? 400 : nil) + .frame(minHeight: shouldLogin ? 300 : nil) + .frame(minWidth: 200) + .disabled(disableInteraction) + .toolbar { + Button("Close", action: { presentationMode.wrappedValue.dismiss() }) + .keyboardShortcut(.cancelAction) + + Button(buttonText) { + if shouldCheckEmail { store.checkEmail() } + if shouldRegister { store.register() } + if shouldLogin { store.login() } + } + .keyboardShortcut(.defaultAction) + .disabled(disableButton) + } + .errorAlert($store.errorMessage) + + .modifier(AppModelBehaviour(state: store.appModelState)) + } +} + +struct LoginView_Previews: PreviewProvider { + static var previews: some View { + LoginView() + } +} diff --git a/Source/HollowMac/View/Hierarchy/Login/WelcomeView.swift b/Source/HollowMac/View/Hierarchy/Login/WelcomeView.swift new file mode 100644 index 00000000..2487d301 --- /dev/null +++ b/Source/HollowMac/View/Hierarchy/Login/WelcomeView.swift @@ -0,0 +1,72 @@ +// +// WelcomeView.swift +// HollowMac +// +// Created by liang2kl on 2021/4/24. +// Copyright © 2021 treehollow. All rights reserved. +// + +import SwiftUI +import Defaults + +struct WelcomeView: View { + @ObservedObject var store = WelcomeStore() + + var body: some View { + VStack(spacing: 20) { + Image(nsImage: NSApp.applicationIconImage) + .padding(50) + Text(Constants.Application.appLocalizedName) + .bold() + .font(.title2) + .padding(.bottom, 20) + Spacer() + Button(action: { + Defaults[.hollowType] = .thu + Defaults[.hollowConfig] = nil + store.requestConfig(hollowType: .thu) + }) { + Text("T大树洞").frame(minWidth: 80) + } + + Button(action: { + Defaults[.hollowType] = .pku + Defaults[.hollowConfig] = nil + store.requestConfig(hollowType: .pku) + }) { + Text("未名树洞").frame(minWidth: 80) + } + + Button(action: { + Defaults[.customConfigURL] = nil + Defaults[.hollowType] = .other + Defaults[.hollowConfig] = nil + showInputAlert( + title: NSLocalizedString("WELCOMEVIEW_INPUT_CUSTOM_URL_ALERT_TITLE", comment: ""), + message: NSLocalizedString("WELCOMEVIEW_INPUT_CUSTOM_URL_ALERT_MSG", comment: ""), + placeholder: "https://example.com/config.txt", + onDismiss: { url in if let url = url { + store.requestConfig(hollowType: .other, customConfigURL: url) + }} + ) + }) { + Text("其他").frame(minWidth: 80) + } + } + .padding(.bottom) + .disabled(store.isLoadingConfig) + .frame(minWidth: 300, maxWidth: 600) + .sheet(isPresented: $store.showLogin) { + LoginView() + } + .navigationTitle("Welcome") + .errorAlert($store.errorMessage) + + } +} + +struct WelcomeView_Previews: PreviewProvider { + static var previews: some View { + WelcomeView() + } +} diff --git a/Source/HollowMac/View/Hierarchy/Main/PostListView.swift b/Source/HollowMac/View/Hierarchy/Main/PostListView.swift new file mode 100644 index 00000000..08e055fc --- /dev/null +++ b/Source/HollowMac/View/Hierarchy/Main/PostListView.swift @@ -0,0 +1,25 @@ +// +// PostListView.swift +// HollowMac +// +// Created by 梁业升 on 2021/4/25. +// Copyright © 2021 treehollow. All rights reserved. +// + +import SwiftUI + +struct PostListView: View { + @ObservedObject var store: PostListRequestStore + var body: some View { + Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + } +} + +struct PostListView_Previews: PreviewProvider { + static let posts: [PostDataWrapper] = [ + .init(post: .init(attention: true, deleted: false, likeNumber: 102, permissions: [.delete], postId: 300123, replyNumber: 17, timestamp: Date(), tag: "HollowMac", text: "HollowMac[49374:1454326] [connection] nw_read_request_report [C2] Receive failed with error \"Connection reset by peer\"", hollowImage: .init(placeholder: (1000, 400), image: nil, imageURL: ""), vote: .init(votedOption: "", voteData: [.init(title: "Option 1", voteCount: -1), .init(title: "Option 2", voteCount: -1)]), comments: [], loadingError: nil, citedPostId: nil, hasURL: false, hasCitedNumbers: false, hash: 102123, colorIndex: 0), citedPost: nil) + ] + static var previews: some View { + PostListView(store: .init(type: .postList)) + } +} diff --git a/Source/HollowMac/View/Modifiers/View+roundedCorner.swift b/Source/HollowMac/View/Modifiers/View+roundedCorner.swift new file mode 100644 index 00000000..04dbc02c --- /dev/null +++ b/Source/HollowMac/View/Modifiers/View+roundedCorner.swift @@ -0,0 +1,15 @@ +// +// View+roundedCorner.swift +// Hollow +// +// Created by liang2kl on 2021/3/5. +// Copyright © 2021 treehollow. All rights reserved. +// + +import SwiftUI + +extension View { + func roundedCorner(_ radius: CGFloat) -> some View { + self.clipShape(RoundedRectangle(cornerRadius: radius, style: .continuous)) + } +} diff --git a/Source/HollowMac/View/Utilities/View+showAlert.swift b/Source/HollowMac/View/Utilities/View+showAlert.swift new file mode 100644 index 00000000..55b22abe --- /dev/null +++ b/Source/HollowMac/View/Utilities/View+showAlert.swift @@ -0,0 +1,86 @@ +// +// View+showAlert.swift +// HollowMac +// +// Created by liang2kl on 2021/4/24. +// Copyright © 2021 treehollow. All rights reserved. +// + +import SwiftUI + +extension View { + func errorAlert(_ errorMessage: Binding<(title: String, message: String)?>) -> some View { + return self + .modifier(ErrorAlert(errorMessage: errorMessage)) + } + + func showInputAlert(title: String?, message: String?, placeholder: String?, onDismiss: @escaping (String?) -> Void) { + let alert = NSAlert() + if let title = title { + alert.messageText = title + } + if let message = message { + alert.informativeText = message + } + let textField = NSTextField(frame: NSRect(x: 0, y: 0, width: 200, height: 24)) + textField.placeholderString = placeholder + alert.accessoryView = textField + alert.addButton(withTitle: "INPUT_ALERT_BUTTON_CONFIRM") + alert.addButton(withTitle: "INPUT_ALERT_BUTTON_CANCEL") + let result = alert.runModal() + switch result { + case .alertFirstButtonReturn: onDismiss(textField.stringValue) + default: onDismiss(nil) + } + } +} + +struct AlertButtonConfiguration { + var title: String + var action: () -> Void = {} + + static func cancel(completion: @escaping () -> Void) -> AlertButtonConfiguration { + AlertButtonConfiguration(title: "Cancel/", action: completion) + } + + static func ok(completion: @escaping () -> Void) -> AlertButtonConfiguration { + AlertButtonConfiguration(title: "OK/", action: completion) + } +} + +fileprivate func showAlert(title: String?, message: String?, buttons: [AlertButtonConfiguration], onCancel: (() -> Void)? = nil) { + let alert = NSAlert() + if let title = title { + alert.messageText = title + } + if let message = message { + alert.informativeText = message + } + + for button in buttons.prefix(3) { + alert.addButton(withTitle: button.title) + } + + let result = alert.runModal() + switch result { + case .alertFirstButtonReturn: buttons[0].action() + case .alertSecondButtonReturn: buttons[1].action() + case .alertThirdButtonReturn: buttons[2].action() + default: onCancel?() + } +} + + +fileprivate struct ErrorAlert: ViewModifier { + @Binding var errorMessage: (title: String, message: String)? + + func body(content: Content) -> some View { + content + .onChange(of: errorMessage?.1) { message in + guard let errorMessage = errorMessage else { return } + let title = errorMessage.title == "" ? errorMessage.message : errorMessage.title + let message = errorMessage.title == "" ? "" : errorMessage.message + showAlert(title: title, message: message, buttons: [.ok(completion: { self.errorMessage = nil })]) + } + } +} diff --git a/Source/Shared/App/AppDelegateShared.swift b/Source/Shared/App/AppDelegateShared.swift new file mode 100644 index 00000000..e04ec5de --- /dev/null +++ b/Source/Shared/App/AppDelegateShared.swift @@ -0,0 +1,156 @@ +// +// AppDelegateShared.swift +// HollowMac +// +// Created by liang2kl on 2021/4/24. +// Copyright © 2021 treehollow. All rights reserved. +// + +import SwiftUI +import Defaults +import UserNotifications +import AppCenter +import AppCenterAnalytics +import AppCenterCrashes + +#if os(macOS) +typealias HAppDelegate = AppDelegate +typealias HApplication = NSApplication +#else +typealias HAppDelegate = AppDelegate +typealias HApplication = UIApplication +#endif + +extension HAppDelegate { + func setupApplication(_ application: HApplication) { + #if !DEBUG + // Start AppCenter services + AppCenter.start( + withAppSecret: "aae3c20c-75f5-4840-96f3-541cd7e6dd88", + services: [Analytics.self, Crashes.self] + ) + #endif + + // Setup remote notifications + setupRemoteNotifications(application) + + // Fetch the lastest config + fetchConfig() + } + + func didRegisterForRemoteNotifications(with deviceToken: Data) { + // Temporarily save the token in defaults. We are not using this + // default except registering or logging in for the first time. + Defaults[.deviceToken] = deviceToken + + // Try to send the token to the server, if we have a user token. + if let accessToken = Defaults[.accessToken] { + sendDeviceToken(deviceToken, withAccessToken: accessToken) + } + } + + private func sendDeviceToken(_ deviceToken: Data, withAccessToken accessToken: String) { + guard let config = Defaults[.hollowConfig] else { return } + let configuration = UpdateDeviceTokenRequestConfiguration(deviceToken: deviceToken, token: accessToken, apiRoot: config.apiRootUrls) + let request = UpdateDeviceTokenRequest(configuration: configuration) + + request.performRequest(completion: { result, error in + if let error = error { + print(error) + // TODO: Handle error + } + }) + } + + private func fetchConfig() { + guard let hollowType = Defaults[.hollowType] else { return } + var configURL: String? { + switch hollowType { + case .thu: return Constants.HollowConfig.thuConfigURL + case .pku: return Constants.HollowConfig.pkuConfigURL + case .other: + return Defaults[.customConfigURL] + } + } + + guard let urlString = configURL else { return } + let request = GetConfigRequest(configuration: GetConfigRequestConfiguration(hollowType: hollowType, customAPIRoot: urlString)!) + + request.performRequest(completion: { result, error in + if let _ = error { + // TODO: Handle error + return + } + + if let result = result { + // Update the config and test connectivity + Defaults[.hollowConfig] = result + LineSwitchManager.testAll() + } + }) + } + + func setupRemoteNotifications(_ application: HApplication) { + // Request notification access + let center = UNUserNotificationCenter.current() + center.delegate = self + let authorizationOptions = Constants.Application.requestedNotificationOptions + center.requestAuthorization(options: authorizationOptions) { granted, error in + guard granted else { return } + // Register for APN + DispatchQueue.main.async { + application.registerForRemoteNotifications() + } + } + } +} + +extension HAppDelegate: UNUserNotificationCenterDelegate { + func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { + let content = response.notification.request.content + + if let type = content.userInfo["type"] as? Int, type == 1 { + // System message for type 1 + presentMessageView() + } else if let postId = content.userInfo["pid"] as? Int { + let commentId = content.userInfo["cid"] as? Int + presentDetail(postId: postId, commentId: commentId) + } + + completionHandler() + } + + func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { + + if notification.request.content.userInfo["delete"] == nil { + completionHandler([.banner, .sound, .list]) + } else { + completionHandler([]) + } + } + + +} + +#if os(macOS) +extension AppDelegate { + // TODO: Implementation + private func presentDetail(postId: Int, commentId: Int?) { + } + + private func presentMessageView() { + } +} +#else +extension AppDelegate { + private func presentDetail(postId: Int, commentId: Int?) { + IntegrationUtilities.openTemplateDetailView(postId: postId, jumpToComment: commentId) + } + + private func presentMessageView() { + let messageView = MessageView(presented: .constant(true), page: .message, selfDismiss: true) + IntegrationUtilities.presentView(content: { messageView }) + } +} + +#endif diff --git a/Source/Shared/CrossPlatform/CrossPlatformImage.swift b/Source/Shared/CrossPlatform/CrossPlatformImage.swift new file mode 100644 index 00000000..1b82b83d --- /dev/null +++ b/Source/Shared/CrossPlatform/CrossPlatformImage.swift @@ -0,0 +1,37 @@ +// +// CrossPlatformImage.swift +// Hollow +// +// Created by liang2kl on 2021/4/24. +// Copyright © 2021 treehollow. All rights reserved. +// + +import SwiftUI + +#if os(macOS) +private extension NSBitmapImageRep { + var png: Data? { representation(using: .png, properties: [:]) } +} +private extension Data { + var bitmap: NSBitmapImageRep? { NSBitmapImageRep(data: self) } +} +extension NSImage { + func pngData() -> Data? { + return tiffRepresentation?.bitmap?.png + } + + func jpegData(compressionQuality: CGFloat) -> Data? { + let cgImage = self.cgImage(forProposedRect: nil, context: nil, hints: nil)! + let bitmapRep = NSBitmapImageRep(cgImage: cgImage) + let jpegData = bitmapRep.representation(using: NSBitmapImageRep.FileType.jpeg, properties: [.compressionFactor : compressionQuality]) + return jpegData + } + +} + +extension Image { + init(uiImage: NSImage) { + self.init(nsImage: uiImage) + } +} +#endif diff --git a/Source/Shared/CrossPlatform/CrossPlatformTypes.swift b/Source/Shared/CrossPlatform/CrossPlatformTypes.swift new file mode 100644 index 00000000..78e565f3 --- /dev/null +++ b/Source/Shared/CrossPlatform/CrossPlatformTypes.swift @@ -0,0 +1,18 @@ +// +// CrossPlatformTypes.swift +// Hollow +// +// Created by liang2kl on 2021/4/23. +// Copyright © 2021 treehollow. All rights reserved. +// + +import SwiftUI +#if os(macOS) +typealias HImage = NSImage +typealias HView = NSView +typealias HColor = NSColor +#else +typealias HImage = UIImage +typealias HColor = UIColor +typealias HView = UIView +#endif diff --git a/Source/Hollow/Model/Constants.swift b/Source/Shared/Model/Constants.swift similarity index 76% rename from Source/Hollow/Model/Constants.swift rename to Source/Shared/Model/Constants.swift index e980ab31..db9c1d2c 100644 --- a/Source/Hollow/Model/Constants.swift +++ b/Source/Shared/Model/Constants.swift @@ -7,7 +7,11 @@ // import Foundation +import UserNotifications + +#if !os(macOS) import UIKit +#endif /// Shared constants. /// @@ -16,9 +20,6 @@ struct Constants { struct Application { static let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "" static let buildVersion = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "" - static let deviceInfo = DeviceModelUtilities.modelIdentifier + - " (\(UIDevice.isMac ? "Mac Catalyst" : UIDevice.current.systemName) " + - UIDevice.current.systemVersion + ")" static let appLocalizedName = Bundle.main.localizedInfoDictionary?["CFBundleName"] as? String ?? Bundle.main.infoDictionary?["CFBundleName"] as? String ?? "" @@ -35,3 +36,14 @@ struct Constants { static let apiTestPath = "generate_204" } } + +extension Constants.Application { + #if !os(macOS) + static let deviceInfo = DeviceModelUtilities.modelIdentifier + + " (\(UIDevice.isMac ? "Mac Catalyst" : UIDevice.current.systemName) " + + UIDevice.current.systemVersion + ")" + #else + // FIXME + static let deviceInfo = "" + #endif +} diff --git a/Source/Hollow/Model/Extensions/Array+removeDuplicates.swift b/Source/Shared/Model/Extensions/Array+removeDuplicates.swift similarity index 100% rename from Source/Hollow/Model/Extensions/Array+removeDuplicates.swift rename to Source/Shared/Model/Extensions/Array+removeDuplicates.swift diff --git a/Source/Hollow/Model/Extensions/Array+sortedRange.swift b/Source/Shared/Model/Extensions/Array+sortedRange.swift similarity index 100% rename from Source/Hollow/Model/Extensions/Array+sortedRange.swift rename to Source/Shared/Model/Extensions/Array+sortedRange.swift diff --git a/Source/Hollow/Model/Extensions/Bool+int.swift b/Source/Shared/Model/Extensions/Bool+int.swift similarity index 100% rename from Source/Hollow/Model/Extensions/Bool+int.swift rename to Source/Shared/Model/Extensions/Bool+int.swift diff --git a/Source/Hollow/Model/Extensions/Data+hexEncodedString.swift b/Source/Shared/Model/Extensions/Data+hexEncodedString.swift similarity index 100% rename from Source/Hollow/Model/Extensions/Data+hexEncodedString.swift rename to Source/Shared/Model/Extensions/Data+hexEncodedString.swift diff --git a/Source/Hollow/Model/Extensions/DateExtensions.swift b/Source/Shared/Model/Extensions/DateExtensions.swift similarity index 90% rename from Source/Hollow/Model/Extensions/DateExtensions.swift rename to Source/Shared/Model/Extensions/DateExtensions.swift index ded2dbc3..62fc17b2 100644 --- a/Source/Hollow/Model/Extensions/DateExtensions.swift +++ b/Source/Shared/Model/Extensions/DateExtensions.swift @@ -2,7 +2,7 @@ // DateExtensions.swift // Hollow // -// Created by 梁业升 on 2021/2/28. +// Created by liang2kl on 2021/2/28. // Copyright © 2021 treehollow. All rights reserved. // diff --git a/Source/Hollow/Model/Extensions/DefaultsKeys.swift b/Source/Shared/Model/Extensions/DefaultsKeys.swift similarity index 98% rename from Source/Hollow/Model/Extensions/DefaultsKeys.swift rename to Source/Shared/Model/Extensions/DefaultsKeys.swift index 050dfeb8..caaa69d9 100644 --- a/Source/Hollow/Model/Extensions/DefaultsKeys.swift +++ b/Source/Shared/Model/Extensions/DefaultsKeys.swift @@ -52,6 +52,11 @@ extension Defaults.Keys { // MARK: - User Settings // Defaults for user settings +#if os(macOS) +extension Defaults.Keys { + +} +#else extension Defaults.Keys { static let foldPredefinedTags = Key("user.settings.fold.predifined.tags", default: true) /// Additional tags to be blocked @@ -69,3 +74,4 @@ extension Defaults.Keys { static let enableEfficientMode = Key("user.settings.enable.efficient.mode", default: false) } +#endif diff --git a/Source/Hollow/Model/Extensions/Double+int.swift b/Source/Shared/Model/Extensions/Double+int.swift similarity index 100% rename from Source/Hollow/Model/Extensions/Double+int.swift rename to Source/Shared/Model/Extensions/Double+int.swift diff --git a/Source/Hollow/Model/Extensions/Int+bool.swift b/Source/Shared/Model/Extensions/Int+bool.swift similarity index 100% rename from Source/Hollow/Model/Extensions/Int+bool.swift rename to Source/Shared/Model/Extensions/Int+bool.swift diff --git a/Source/Hollow/Model/Extensions/Int+string.swift b/Source/Shared/Model/Extensions/Int+string.swift similarity index 100% rename from Source/Hollow/Model/Extensions/Int+string.swift rename to Source/Shared/Model/Extensions/Int+string.swift diff --git a/Source/Hollow/Model/Extensions/PublisherExtensions.swift b/Source/Shared/Model/Extensions/PublisherExtensions.swift similarity index 99% rename from Source/Hollow/Model/Extensions/PublisherExtensions.swift rename to Source/Shared/Model/Extensions/PublisherExtensions.swift index 4c59e7ea..105ec276 100644 --- a/Source/Hollow/Model/Extensions/PublisherExtensions.swift +++ b/Source/Shared/Model/Extensions/PublisherExtensions.swift @@ -7,7 +7,7 @@ // import Combine -import UIKit +import SwiftUI extension Publisher { /// Wrap `.sink` on main thread. diff --git a/Source/Hollow/Model/Extensions/String+Date.swift b/Source/Shared/Model/Extensions/String+Date.swift similarity index 100% rename from Source/Hollow/Model/Extensions/String+Date.swift rename to Source/Shared/Model/Extensions/String+Date.swift diff --git a/Source/Hollow/Model/Extensions/String+SHA256.swift b/Source/Shared/Model/Extensions/String+SHA256.swift similarity index 100% rename from Source/Hollow/Model/Extensions/String+SHA256.swift rename to Source/Shared/Model/Extensions/String+SHA256.swift diff --git a/Source/Hollow/Model/Extensions/String+regex.swift b/Source/Shared/Model/Extensions/String+regex.swift similarity index 100% rename from Source/Hollow/Model/Extensions/String+regex.swift rename to Source/Shared/Model/Extensions/String+regex.swift diff --git a/Source/Hollow/Model/Extensions/String+removeLineBreak.swift b/Source/Shared/Model/Extensions/String+removeLineBreak.swift similarity index 90% rename from Source/Hollow/Model/Extensions/String+removeLineBreak.swift rename to Source/Shared/Model/Extensions/String+removeLineBreak.swift index 8b808498..8f8cc25b 100644 --- a/Source/Hollow/Model/Extensions/String+removeLineBreak.swift +++ b/Source/Shared/Model/Extensions/String+removeLineBreak.swift @@ -2,7 +2,7 @@ // String+removeLineBreak.swift // Hollow // -// Created by 梁业升 on 2021/2/8. +// Created by liang2kl on 2021/2/8. // Copyright © 2021 treehollow. All rights reserved. // diff --git a/Source/Hollow/Model/Net/Config/GetConfigRequest.swift b/Source/Shared/Model/Net/Config/GetConfigRequest.swift similarity index 100% rename from Source/Hollow/Model/Net/Config/GetConfigRequest.swift rename to Source/Shared/Model/Net/Config/GetConfigRequest.swift diff --git a/Source/Hollow/Model/Net/Config/GetPushRequest.swift b/Source/Shared/Model/Net/Config/GetPushRequest.swift similarity index 100% rename from Source/Hollow/Model/Net/Config/GetPushRequest.swift rename to Source/Shared/Model/Net/Config/GetPushRequest.swift diff --git a/Source/Hollow/Model/Net/Config/SetPushRequest.swift b/Source/Shared/Model/Net/Config/SetPushRequest.swift similarity index 100% rename from Source/Hollow/Model/Net/Config/SetPushRequest.swift rename to Source/Shared/Model/Net/Config/SetPushRequest.swift diff --git a/Source/Hollow/Model/Net/Contents/AttentionListRequest.swift b/Source/Shared/Model/Net/Contents/AttentionListRequest.swift similarity index 100% rename from Source/Hollow/Model/Net/Contents/AttentionListRequest.swift rename to Source/Shared/Model/Net/Contents/AttentionListRequest.swift diff --git a/Source/Hollow/Model/Net/Contents/AttentionListSearchRequest.swift b/Source/Shared/Model/Net/Contents/AttentionListSearchRequest.swift similarity index 100% rename from Source/Hollow/Model/Net/Contents/AttentionListSearchRequest.swift rename to Source/Shared/Model/Net/Contents/AttentionListSearchRequest.swift diff --git a/Source/Hollow/Model/Net/Contents/PostDetailRequest.swift b/Source/Shared/Model/Net/Contents/PostDetailRequest.swift similarity index 100% rename from Source/Hollow/Model/Net/Contents/PostDetailRequest.swift rename to Source/Shared/Model/Net/Contents/PostDetailRequest.swift diff --git a/Source/Hollow/Model/Net/Contents/PostListRequest.swift b/Source/Shared/Model/Net/Contents/PostListRequest.swift similarity index 99% rename from Source/Hollow/Model/Net/Contents/PostListRequest.swift rename to Source/Shared/Model/Net/Contents/PostListRequest.swift index 0b3331cd..6fb3bac0 100644 --- a/Source/Hollow/Model/Net/Contents/PostListRequest.swift +++ b/Source/Shared/Model/Net/Contents/PostListRequest.swift @@ -7,7 +7,6 @@ import Foundation import Alamofire -import UIKit /// Configuration for PostListRequest struct PostListRequestConfiguration { diff --git a/Source/Hollow/Model/Net/Contents/RandomListRequest.swift b/Source/Shared/Model/Net/Contents/RandomListRequest.swift similarity index 100% rename from Source/Hollow/Model/Net/Contents/RandomListRequest.swift rename to Source/Shared/Model/Net/Contents/RandomListRequest.swift diff --git a/Source/Hollow/Model/Net/Contents/SearchHistoryRequest.swift b/Source/Shared/Model/Net/Contents/SearchHistoryRequest.swift similarity index 100% rename from Source/Hollow/Model/Net/Contents/SearchHistoryRequest.swift rename to Source/Shared/Model/Net/Contents/SearchHistoryRequest.swift diff --git a/Source/Hollow/Model/Net/Contents/SearchRequest.swift b/Source/Shared/Model/Net/Contents/SearchRequest.swift similarity index 100% rename from Source/Hollow/Model/Net/Contents/SearchRequest.swift rename to Source/Shared/Model/Net/Contents/SearchRequest.swift diff --git a/Source/Hollow/Model/Net/Contents/SystemMessageRequest.swift b/Source/Shared/Model/Net/Contents/SystemMessageRequest.swift similarity index 100% rename from Source/Hollow/Model/Net/Contents/SystemMessageRequest.swift rename to Source/Shared/Model/Net/Contents/SystemMessageRequest.swift diff --git a/Source/Hollow/Model/Net/DefaultRequest.swift b/Source/Shared/Model/Net/DefaultRequest.swift similarity index 100% rename from Source/Hollow/Model/Net/DefaultRequest.swift rename to Source/Shared/Model/Net/DefaultRequest.swift diff --git a/Source/Hollow/Model/Net/Edit/EditAttentionRequest.swift b/Source/Shared/Model/Net/Edit/EditAttentionRequest.swift similarity index 100% rename from Source/Hollow/Model/Net/Edit/EditAttentionRequest.swift rename to Source/Shared/Model/Net/Edit/EditAttentionRequest.swift diff --git a/Source/Hollow/Model/Net/Edit/ReportCommentRequest.swift b/Source/Shared/Model/Net/Edit/ReportCommentRequest.swift similarity index 100% rename from Source/Hollow/Model/Net/Edit/ReportCommentRequest.swift rename to Source/Shared/Model/Net/Edit/ReportCommentRequest.swift diff --git a/Source/Hollow/Model/Net/Edit/ReportPostRequest.swift b/Source/Shared/Model/Net/Edit/ReportPostRequest.swift similarity index 100% rename from Source/Hollow/Model/Net/Edit/ReportPostRequest.swift rename to Source/Shared/Model/Net/Edit/ReportPostRequest.swift diff --git a/Source/Hollow/Model/Net/Other/FetchImageRequest.swift b/Source/Shared/Model/Net/Other/FetchImageRequest.swift similarity index 97% rename from Source/Hollow/Model/Net/Other/FetchImageRequest.swift rename to Source/Shared/Model/Net/Other/FetchImageRequest.swift index 7490cebe..0c16bf19 100644 --- a/Source/Hollow/Model/Net/Other/FetchImageRequest.swift +++ b/Source/Shared/Model/Net/Other/FetchImageRequest.swift @@ -6,8 +6,8 @@ // Copyright © 2021 treehollow. All rights reserved. // -import UIKit import Kingfisher +import Foundation struct FetchImageRequestConfiguration { var urlBase: [String] @@ -37,7 +37,7 @@ enum FetchImageRequestError: RequestError { struct FetchImageRequest: Request { typealias Configuration = FetchImageRequestConfiguration - typealias Result = UIImage + typealias Result = HImage typealias ResultData = Result typealias Error = FetchImageRequestError diff --git a/Source/Hollow/Model/Net/Other/PostListRequestGroup.swift b/Source/Shared/Model/Net/Other/PostListRequestGroup.swift similarity index 100% rename from Source/Hollow/Model/Net/Other/PostListRequestGroup.swift rename to Source/Shared/Model/Net/Other/PostListRequestGroup.swift diff --git a/Source/Hollow/Model/Net/Other/ReportRequestGroup.swift b/Source/Shared/Model/Net/Other/ReportRequestGroup.swift similarity index 100% rename from Source/Hollow/Model/Net/Other/ReportRequestGroup.swift rename to Source/Shared/Model/Net/Other/ReportRequestGroup.swift diff --git a/Source/Hollow/Model/Net/README.md b/Source/Shared/Model/Net/README.md similarity index 100% rename from Source/Hollow/Model/Net/README.md rename to Source/Shared/Model/Net/README.md diff --git a/Source/Hollow/Model/Net/Request+publisher.swift b/Source/Shared/Model/Net/Request+publisher.swift similarity index 100% rename from Source/Hollow/Model/Net/Request+publisher.swift rename to Source/Shared/Model/Net/Request+publisher.swift diff --git a/Source/Hollow/Model/Net/Request.swift b/Source/Shared/Model/Net/Request.swift similarity index 100% rename from Source/Hollow/Model/Net/Request.swift rename to Source/Shared/Model/Net/Request.swift diff --git a/Source/Hollow/Model/Net/RequestError.swift b/Source/Shared/Model/Net/RequestError.swift similarity index 100% rename from Source/Hollow/Model/Net/RequestError.swift rename to Source/Shared/Model/Net/RequestError.swift diff --git a/Source/Hollow/Model/Net/RequestPublisher.swift b/Source/Shared/Model/Net/RequestPublisher.swift similarity index 100% rename from Source/Hollow/Model/Net/RequestPublisher.swift rename to Source/Shared/Model/Net/RequestPublisher.swift diff --git a/Source/Hollow/Model/Net/Security/AccountCreationRequest.swift b/Source/Shared/Model/Net/Security/AccountCreationRequest.swift similarity index 99% rename from Source/Hollow/Model/Net/Security/AccountCreationRequest.swift rename to Source/Shared/Model/Net/Security/AccountCreationRequest.swift index a5875fe9..b4db40be 100644 --- a/Source/Hollow/Model/Net/Security/AccountCreationRequest.swift +++ b/Source/Shared/Model/Net/Security/AccountCreationRequest.swift @@ -6,7 +6,6 @@ // import Alamofire -import UIKit import Foundation /// Configuraions for creating an account. diff --git a/Source/Hollow/Model/Net/Security/ChangePasswordRequest.swift b/Source/Shared/Model/Net/Security/ChangePasswordRequest.swift similarity index 100% rename from Source/Hollow/Model/Net/Security/ChangePasswordRequest.swift rename to Source/Shared/Model/Net/Security/ChangePasswordRequest.swift diff --git a/Source/Hollow/Model/Net/Security/DeviceListRequest.swift b/Source/Shared/Model/Net/Security/DeviceListRequest.swift similarity index 100% rename from Source/Hollow/Model/Net/Security/DeviceListRequest.swift rename to Source/Shared/Model/Net/Security/DeviceListRequest.swift diff --git a/Source/Hollow/Model/Net/Security/DeviceTerminationRequest.swift b/Source/Shared/Model/Net/Security/DeviceTerminationRequest.swift similarity index 100% rename from Source/Hollow/Model/Net/Security/DeviceTerminationRequest.swift rename to Source/Shared/Model/Net/Security/DeviceTerminationRequest.swift diff --git a/Source/Hollow/Model/Net/Security/EmailCheckRequest.swift b/Source/Shared/Model/Net/Security/EmailCheckRequest.swift similarity index 100% rename from Source/Hollow/Model/Net/Security/EmailCheckRequest.swift rename to Source/Shared/Model/Net/Security/EmailCheckRequest.swift diff --git a/Source/Hollow/Model/Net/Security/LoginRequest.swift b/Source/Shared/Model/Net/Security/LoginRequest.swift similarity index 99% rename from Source/Hollow/Model/Net/Security/LoginRequest.swift rename to Source/Shared/Model/Net/Security/LoginRequest.swift index b1e4855e..e99856a2 100644 --- a/Source/Hollow/Model/Net/Security/LoginRequest.swift +++ b/Source/Shared/Model/Net/Security/LoginRequest.swift @@ -7,7 +7,7 @@ // import Alamofire -import UIKit +import Foundation struct LoginRequestConfiguration { var email: String diff --git a/Source/Hollow/Model/Net/Security/LogoutRequest.swift b/Source/Shared/Model/Net/Security/LogoutRequest.swift similarity index 100% rename from Source/Hollow/Model/Net/Security/LogoutRequest.swift rename to Source/Shared/Model/Net/Security/LogoutRequest.swift diff --git a/Source/Hollow/Model/Net/Security/UpdateDeviceTokenRequest.swift b/Source/Shared/Model/Net/Security/UpdateDeviceTokenRequest.swift similarity index 100% rename from Source/Hollow/Model/Net/Security/UpdateDeviceTokenRequest.swift rename to Source/Shared/Model/Net/Security/UpdateDeviceTokenRequest.swift diff --git a/Source/Hollow/Model/Net/Send/SendCommentRequest.swift b/Source/Shared/Model/Net/Send/SendCommentRequest.swift similarity index 100% rename from Source/Hollow/Model/Net/Send/SendCommentRequest.swift rename to Source/Shared/Model/Net/Send/SendCommentRequest.swift diff --git a/Source/Hollow/Model/Net/Send/SendPostRequest.swift b/Source/Shared/Model/Net/Send/SendPostRequest.swift similarity index 100% rename from Source/Hollow/Model/Net/Send/SendPostRequest.swift rename to Source/Shared/Model/Net/Send/SendPostRequest.swift diff --git a/Source/Hollow/Model/Net/Send/SendVoteRequest.swift b/Source/Shared/Model/Net/Send/SendVoteRequest.swift similarity index 100% rename from Source/Hollow/Model/Net/Send/SendVoteRequest.swift rename to Source/Shared/Model/Net/Send/SendVoteRequest.swift diff --git a/Source/Hollow/Model/Types/Comment.swift b/Source/Shared/Model/Types/Comment.swift similarity index 100% rename from Source/Hollow/Model/Types/Comment.swift rename to Source/Shared/Model/Types/Comment.swift diff --git a/Source/Hollow/Model/Types/Data/CommentData.swift b/Source/Shared/Model/Types/Data/CommentData.swift similarity index 98% rename from Source/Hollow/Model/Types/Data/CommentData.swift rename to Source/Shared/Model/Types/Data/CommentData.swift index c7704867..e72e5ea6 100644 --- a/Source/Hollow/Model/Types/Data/CommentData.swift +++ b/Source/Shared/Model/Types/Data/CommentData.swift @@ -6,7 +6,7 @@ // Copyright © 2021 treehollow. All rights reserved. // -import UIKit +import Foundation /// Data wrapper representing a single comment. struct CommentData: Identifiable, Codable { diff --git a/Source/Hollow/Model/Types/Data/HollowImage.swift b/Source/Shared/Model/Types/Data/HollowImage.swift similarity index 93% rename from Source/Hollow/Model/Types/Data/HollowImage.swift rename to Source/Shared/Model/Types/Data/HollowImage.swift index f1432f7e..ee3e6b1c 100644 --- a/Source/Hollow/Model/Types/Data/HollowImage.swift +++ b/Source/Shared/Model/Types/Data/HollowImage.swift @@ -6,8 +6,7 @@ // Copyright © 2021 treehollow. All rights reserved. // -import Foundation -import UIKit +import SwiftUI import Cache /// Image wrapper for displaying in a hollow with support for placeholder. @@ -20,7 +19,14 @@ struct HollowImage: Codable{ /// Width and hight information to display a placeholder. var placeholder: ImagePlaceHolder /// The image to display. - var image: UIImage? { + /// store image here + private var imageWrapper: ImageWrapper? + /// image url + var imageURL: String + + var loadingError: String? + + var image: HImage? { get { imageWrapper?.image } set { if let image = newValue { @@ -28,16 +34,10 @@ struct HollowImage: Codable{ } } } - /// store image here - private var imageWrapper: ImageWrapper? - /// image url - var imageURL: String - - var loadingError: String? /// - parameter placeholder: Placeholder metadata. /// - parameter image: The image to display, set it to `nil` when the image is still loading. - init(placeholder: (width: CGFloat, height: CGFloat), image: UIImage?, imageURL: String) { + init(placeholder: (width: CGFloat, height: CGFloat), image: HImage?, imageURL: String) { self.placeholder = ImagePlaceHolder( width: placeholder.width, height: placeholder.height @@ -45,5 +45,4 @@ struct HollowImage: Codable{ self.imageURL = imageURL self.image = image } - } diff --git a/Source/Hollow/Model/Types/Data/PostData.swift b/Source/Shared/Model/Types/Data/PostData.swift similarity index 99% rename from Source/Hollow/Model/Types/Data/PostData.swift rename to Source/Shared/Model/Types/Data/PostData.swift index 645b7e6b..5363d5c0 100644 --- a/Source/Hollow/Model/Types/Data/PostData.swift +++ b/Source/Shared/Model/Types/Data/PostData.swift @@ -6,7 +6,7 @@ // Copyright © 2021 treehollow. All rights reserved. // -import UIKit +import Foundation /// Data wrapper representing a single post. struct PostData: Identifiable, Codable { diff --git a/Source/Hollow/Model/Types/Data/VoteData.swift b/Source/Shared/Model/Types/Data/VoteData.swift similarity index 100% rename from Source/Hollow/Model/Types/Data/VoteData.swift rename to Source/Shared/Model/Types/Data/VoteData.swift diff --git a/Source/Hollow/Model/Types/DeviceInformationType.swift b/Source/Shared/Model/Types/DeviceInformationType.swift similarity index 100% rename from Source/Hollow/Model/Types/DeviceInformationType.swift rename to Source/Shared/Model/Types/DeviceInformationType.swift diff --git a/Source/Hollow/Model/Types/HollowConfig.swift b/Source/Shared/Model/Types/HollowConfig.swift similarity index 100% rename from Source/Hollow/Model/Types/HollowConfig.swift rename to Source/Shared/Model/Types/HollowConfig.swift diff --git a/Source/Hollow/Model/Types/HollowType.swift b/Source/Shared/Model/Types/HollowType.swift similarity index 100% rename from Source/Hollow/Model/Types/HollowType.swift rename to Source/Shared/Model/Types/HollowType.swift diff --git a/Source/Hollow/Model/Types/ImageMetadata.swift b/Source/Shared/Model/Types/ImageMetadata.swift similarity index 88% rename from Source/Hollow/Model/Types/ImageMetadata.swift rename to Source/Shared/Model/Types/ImageMetadata.swift index 1eb3e394..43d9bdd4 100644 --- a/Source/Hollow/Model/Types/ImageMetadata.swift +++ b/Source/Shared/Model/Types/ImageMetadata.swift @@ -6,8 +6,7 @@ // Copyright © 2021 treehollow. All rights reserved. // -import Foundation -import UIKit +import SwiftUI /// ImageMetadata struct ImageMetadata: Codable { diff --git a/Source/Hollow/Model/Types/Post.swift b/Source/Shared/Model/Types/Post.swift similarity index 100% rename from Source/Hollow/Model/Types/Post.swift rename to Source/Shared/Model/Types/Post.swift diff --git a/Source/Hollow/Model/Types/PushNotificationType.swift b/Source/Shared/Model/Types/PushNotificationType.swift similarity index 100% rename from Source/Hollow/Model/Types/PushNotificationType.swift rename to Source/Shared/Model/Types/PushNotificationType.swift diff --git a/Source/Hollow/Model/Types/SystemMessage.swift b/Source/Shared/Model/Types/SystemMessage.swift similarity index 100% rename from Source/Hollow/Model/Types/SystemMessage.swift rename to Source/Shared/Model/Types/SystemMessage.swift diff --git a/Source/Hollow/Model/Types/Vote.swift b/Source/Shared/Model/Types/Vote.swift similarity index 100% rename from Source/Hollow/Model/Types/Vote.swift rename to Source/Shared/Model/Types/Vote.swift diff --git a/Source/Hollow/Model/Utilities/AvatarGenerator.swift b/Source/Shared/Model/Utilities/AvatarGenerator.swift similarity index 100% rename from Source/Hollow/Model/Utilities/AvatarGenerator.swift rename to Source/Shared/Model/Utilities/AvatarGenerator.swift diff --git a/Source/Hollow/Model/Utilities/DeviceModelUtilities.swift b/Source/Shared/Model/Utilities/DeviceModelUtilities.swift similarity index 75% rename from Source/Hollow/Model/Utilities/DeviceModelUtilities.swift rename to Source/Shared/Model/Utilities/DeviceModelUtilities.swift index 6506af71..4d222af0 100644 --- a/Source/Hollow/Model/Utilities/DeviceModelUtilities.swift +++ b/Source/Shared/Model/Utilities/DeviceModelUtilities.swift @@ -10,11 +10,6 @@ import Foundation struct DeviceModelUtilities { static var modelIdentifier: String { - #if targetEnvironment(simulator) - return ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "" - #elseif targetEnvironment(macCatalyst) - return "Mac" - #else var systemInfo = utsname() uname(&systemInfo) let machineMirror = Mirror(reflecting: systemInfo.machine) @@ -23,6 +18,5 @@ struct DeviceModelUtilities { return identifier + String(UnicodeScalar(UInt8(value))) } return identifier - #endif } } diff --git a/Source/Hollow/Model/Utilities/HollowDateFormatter.swift b/Source/Shared/Model/Utilities/HollowDateFormatter.swift similarity index 100% rename from Source/Hollow/Model/Utilities/HollowDateFormatter.swift rename to Source/Shared/Model/Utilities/HollowDateFormatter.swift diff --git a/Source/Hollow/Model/Utilities/ImageCompressor.swift b/Source/Shared/Model/Utilities/ImageCompressor.swift similarity index 85% rename from Source/Hollow/Model/Utilities/ImageCompressor.swift rename to Source/Shared/Model/Utilities/ImageCompressor.swift index 81f9286e..12c16126 100644 --- a/Source/Hollow/Model/Utilities/ImageCompressor.swift +++ b/Source/Shared/Model/Utilities/ImageCompressor.swift @@ -6,12 +6,12 @@ // Copyright © 2021 treehollow. All rights reserved. // -import UIKit +import SwiftUI struct ImageCompressor { static let resizingQuality: CGFloat = 0.7 var dataCountThreshold: Int - var image: UIImage + var image: HImage func compress() -> (jpegData: Data, base64String: String, description: String)? { let formatter = ByteCountFormatter() formatter.allowedUnits = [.useKB] @@ -77,7 +77,7 @@ struct ImageCompressor { return nil } - func compress(_ image: UIImage, quality: CGFloat) -> Data? { + func compress(_ image: HImage, quality: CGFloat) -> Data? { if let jpegData = image.jpegData(compressionQuality: quality) { if jpegData.count <= dataCountThreshold { print("[ImageCompressor] Succeed to compress with quality \(quality), the size is \(jpegData.count)") @@ -91,6 +91,7 @@ struct ImageCompressor { } } +#if !os(macOS) private extension UIImage { func resizeTo(scaleFactor: CGFloat) -> UIImage { let destinationSize = CGSize(width: self.size.width * scaleFactor, height: self.size.height * scaleFactor) @@ -106,6 +107,19 @@ private extension UIImage { } } +#else +private extension NSImage { + func resizeTo(scaleFactor: CGFloat) -> NSImage { + let destSize = CGSize(width: self.size.width * scaleFactor, height: self.size.height * scaleFactor) + let newImage = NSImage(size: destSize) + newImage.lockFocus() + self.draw(in: NSMakeRect(0, 0, destSize.width, destSize.height), from: NSMakeRect(0, 0, self.size.width, self.size.height), operation: .sourceOver, fraction: CGFloat(1)) + newImage.unlockFocus() + newImage.size = destSize + return NSImage(data: newImage.tiffRepresentation!)! + } +} +#endif private extension ByteCountFormatter { func sizeDescription(forData data: Data) -> String? { diff --git a/Source/Hollow/Model/Utilities/LineSwitchManager.swift b/Source/Shared/Model/Utilities/LineSwitchManager.swift similarity index 100% rename from Source/Hollow/Model/Utilities/LineSwitchManager.swift rename to Source/Shared/Model/Utilities/LineSwitchManager.swift diff --git a/Source/Hollow/Model/Utilities/OpenURLHelper.swift b/Source/Shared/Model/Utilities/OpenURLHelper.swift similarity index 92% rename from Source/Hollow/Model/Utilities/OpenURLHelper.swift rename to Source/Shared/Model/Utilities/OpenURLHelper.swift index 8f199f59..cda60562 100644 --- a/Source/Hollow/Model/Utilities/OpenURLHelper.swift +++ b/Source/Shared/Model/Utilities/OpenURLHelper.swift @@ -29,6 +29,7 @@ struct OpenURLHelper { var openURL: OpenURLAction + #if !os(macOS) func tryOpen(_ url: URL, method: OpenMethod) throws { if UIApplication.shared.canOpenURL(url) { open(url, method: method) @@ -43,15 +44,18 @@ struct OpenURLHelper { } private func open(_ url: URL, method: OpenMethod) { - #if targetEnvironment(macCatalyst) - openURL(url) - #else switch method { case .inApp: IntegrationUtilities.presentSafariVC(url: url) case .universal: openURL(url) } - #endif } + + #else + func tryOpen(_ url: URL, method: OpenMethod) throws { + openURL(url) + } + #endif + } diff --git a/Source/Hollow/Model/Utilities/PostCache.swift b/Source/Shared/Model/Utilities/PostCache.swift similarity index 100% rename from Source/Hollow/Model/Utilities/PostCache.swift rename to Source/Shared/Model/Utilities/PostCache.swift diff --git a/Source/Shared/View/ReCAPTCHAPageView.swift b/Source/Shared/View/ReCAPTCHAPageView.swift new file mode 100644 index 00000000..6a1e72be --- /dev/null +++ b/Source/Shared/View/ReCAPTCHAPageView.swift @@ -0,0 +1,58 @@ +// +// ReCAPTCHAPageView.swift +// Hollow +// +// Created by liang2kl on 2021/4/24. +// Copyright © 2021 treehollow. All rights reserved. +// + +import SwiftUI + +struct ReCAPTCHAPageView: View { + @Binding var presented: Bool + let successHandler: (String) -> Void + @State private var pageLoadingFinish = false + + var body: some View { + VStack { + Button(action: { + withAnimation { + presented = false + } + }) { + #if !os(macOS) + Image(systemName: "xmark") + .foregroundColor(.hollowContentText) + .dynamicFont(size: 20, weight: .medium) + .padding(.bottom) + #else + Text("Cancel") + #endif + + } + .keyboardShortcut(.cancelAction) + + .leading() + ReCAPTCHAWebView(onFinishLoading: { + withAnimation { + pageLoadingFinish = true + } + }, successHandler: successHandler) + .onAppear { + pageLoadingFinish = false + } + } + .padding() + .overlay(Group { + if !pageLoadingFinish { + #if !os(macOS) + Spinner(color: .buttonGradient1, desiredWidth: 30) + #else + ProgressView("Loading") + .progressViewStyle(CircularProgressViewStyle()) + #endif + } + }) + + } +} diff --git a/Source/Hollow/View/Integration/ReCAPTCHAWebView.swift b/Source/Shared/View/ReCAPTCHAWebView.swift similarity index 76% rename from Source/Hollow/View/Integration/ReCAPTCHAWebView.swift rename to Source/Shared/View/ReCAPTCHAWebView.swift index c5a76c85..a903e23b 100644 --- a/Source/Hollow/View/Integration/ReCAPTCHAWebView.swift +++ b/Source/Shared/View/ReCAPTCHAWebView.swift @@ -10,13 +10,39 @@ import SwiftUI import WebKit import Defaults -struct ReCAPTCHAWebView: UIViewRepresentable { +#if os(macOS) +fileprivate typealias HViewRepresentable = NSViewRepresentable +#else +fileprivate typealias HViewRepresentable = UIViewRepresentable +#endif + +struct ReCAPTCHAWebView: HViewRepresentable { + #if os(macOS) + typealias NSViewType = WKWebView + #else typealias UIViewType = WKWebView + #endif var onFinishLoading: () -> Void // TODO: Handler including parameter representing error. var successHandler: (String) -> Void + #if os(macOS) + func makeNSView(context: Context) -> WKWebView { + return setupWebView(context: context) + } + + func updateNSView(_ nsView: WKWebView, context: Context) { + } + #else func makeUIView(context: Context) -> WKWebView { + return setupWebView(context: context) + } + + func updateUIView(_ uiView: WKWebView, context: Context) { + } + #endif + + func setupWebView(context: Context) -> WKWebView { let preferences = WKWebpagePreferences() preferences.allowsContentJavaScript = true let configuration = WKWebViewConfiguration() @@ -29,10 +55,6 @@ struct ReCAPTCHAWebView: UIViewRepresentable { return webView } - func updateUIView(_ uiView: WKWebView, context: Context) { - - } - func makeCoordinator() -> Coordinator { return Coordinator(self) } diff --git a/Source/Hollow/View/ViewConstants.swift b/Source/Shared/View/ViewConstants.swift similarity index 100% rename from Source/Hollow/View/ViewConstants.swift rename to Source/Shared/View/ViewConstants.swift diff --git a/Source/Hollow/View/Utilities/ViewLayouts.swift b/Source/Shared/View/ViewLayouts.swift similarity index 100% rename from Source/Hollow/View/Utilities/ViewLayouts.swift rename to Source/Shared/View/ViewLayouts.swift diff --git a/Source/Hollow/ViewModel/Account/AccountInfoStore.swift b/Source/Shared/ViewModel/Account/AccountInfoStore.swift similarity index 96% rename from Source/Hollow/ViewModel/Account/AccountInfoStore.swift rename to Source/Shared/ViewModel/Account/AccountInfoStore.swift index 4490efb6..4b05bf3f 100644 --- a/Source/Hollow/ViewModel/Account/AccountInfoStore.swift +++ b/Source/Shared/ViewModel/Account/AccountInfoStore.swift @@ -50,7 +50,11 @@ class AccountInfoStore: ObservableObject, AppModelEnvironment { self.defaultErrorHandler(errorMessage: &self.errorMessage, error: error) }, receiveValue: { _ in withAnimation { self.isLoading = false } + // FIXME: macOS + #if !os(macOS) ToastManager.shared.show(configuration: .success(title: nil, body: NSLocalizedString("CHANGE_PASSWORD_SUCCESS_ALERT_TITLE", comment: ""))) + #endif + self.appModelState.shouldShowMainView = false Defaults[.accessToken] = nil }) diff --git a/Source/Hollow/ViewModel/Account/DeviceListStore.swift b/Source/Shared/ViewModel/Account/DeviceListStore.swift similarity index 100% rename from Source/Hollow/ViewModel/Account/DeviceListStore.swift rename to Source/Shared/ViewModel/Account/DeviceListStore.swift diff --git a/Source/Hollow/ViewModel/Account/MessageStore.swift b/Source/Shared/ViewModel/Account/MessageStore.swift similarity index 100% rename from Source/Hollow/ViewModel/Account/MessageStore.swift rename to Source/Shared/ViewModel/Account/MessageStore.swift diff --git a/Source/Hollow/ViewModel/Hollow/HollowDetailStore.swift b/Source/Shared/ViewModel/Hollow/HollowDetailStore.swift similarity index 97% rename from Source/Hollow/ViewModel/Hollow/HollowDetailStore.swift rename to Source/Shared/ViewModel/Hollow/HollowDetailStore.swift index 07a1e144..db1c53f9 100644 --- a/Source/Hollow/ViewModel/Hollow/HollowDetailStore.swift +++ b/Source/Shared/ViewModel/Hollow/HollowDetailStore.swift @@ -9,7 +9,10 @@ import Combine import SwiftUI import Defaults + +#if canImport(Rechability) import Connectivity +#endif class HollowDetailStore: ObservableObject, ImageCompressStore, AppModelEnvironment { // MARK: Post Variables @@ -27,10 +30,10 @@ class HollowDetailStore: ObservableObject, ImageCompressStore, AppModelEnvironme self.objectWillChange.send() }} } - @Published var image: UIImage? - @Published var compressedImage: UIImage? + @Published var image: HImage? + @Published var compressedImage: HImage? var compressedImageBase64String: String? - var cancelledImages = [UIImage]() + var cancelledImages = [HImage]() // MARK: Shared Variables @Published var errorMessage: (title: String, message: String)? @@ -56,12 +59,14 @@ class HollowDetailStore: ObservableObject, ImageCompressStore, AppModelEnvironme .receive(on: DispatchQueue.main) .assign(to: \.bindingPostWrapper.wrappedValue, on: self) + #if canImport(Rechability) // When reconnect to the internet, fetch again. ConnectivityPublisher.networkConnectedStatusPublisher .sink(receiveValue: { connected in if connected { self.requestDetail() } }) .store(in: &cancellables) + #endif } func cancelAll() { @@ -133,7 +138,7 @@ class HollowDetailStore: ObservableObject, ImageCompressStore, AppModelEnvironme } } - private func loadImage(for imageURL: String, receiveValue: @escaping (UIImage) -> Void, receiveError: @escaping (FetchImageRequestError) -> Void) { + private func loadImage(for imageURL: String, receiveValue: @escaping (HImage) -> Void, receiveError: @escaping (FetchImageRequestError) -> Void) { guard let config = Defaults[.hollowConfig] else { return } let imageRequest = FetchImageRequest(configuration: .init(urlBase: config.imgBaseUrls, urlString: imageURL)) @@ -197,7 +202,7 @@ class HollowDetailStore: ObservableObject, ImageCompressStore, AppModelEnvironme .store(in: &cancellables) } - private func assignImage(_ image: UIImage, to commentId: Int) { + private func assignImage(_ image: HImage, to commentId: Int) { guard let index = postDataWrapper.post.comments.firstIndex(where: { $0.commentId == commentId }) else { return } withAnimation { self.postDataWrapper.post.comments[index].image?.image = image diff --git a/Source/Hollow/ViewModel/Hollow/HollowInputStore.swift b/Source/Shared/ViewModel/Hollow/HollowInputStore.swift similarity index 93% rename from Source/Hollow/ViewModel/Hollow/HollowInputStore.swift rename to Source/Shared/ViewModel/Hollow/HollowInputStore.swift index 8c66e6ac..6086fa27 100644 --- a/Source/Hollow/ViewModel/Hollow/HollowInputStore.swift +++ b/Source/Shared/ViewModel/Hollow/HollowInputStore.swift @@ -17,8 +17,8 @@ class HollowInputStore: ObservableObject, AppModelEnvironment, ImageCompressStor var refreshHandler: (() -> Void)? @Published var text: String = "" - @Published var image: UIImage? - @Published var compressedImage: UIImage? + @Published var image: HImage? + @Published var compressedImage: HImage? var compressedImageBase64String: String? @Published var availableTags: [String] = Defaults[.hollowConfig]?.sendableTags ?? [] @Published var selectedTag: String? @@ -26,7 +26,7 @@ class HollowInputStore: ObservableObject, AppModelEnvironment, ImageCompressStor @Published var sending = false @Published var errorMessage: (title: String, message: String)? @Published var imageSizeInformation: String? - var cancelledImages = [UIImage]() + var cancelledImages = [HImage]() @Published var appModelState = AppModelState() @@ -57,9 +57,12 @@ class HollowInputStore: ObservableObject, AppModelEnvironment, ImageCompressStor self.refreshHandler?() self.presented.wrappedValue = false if self.selfDismiss { + #if !os(macOS) if let vc = IntegrationUtilities.topViewController() { vc.dismiss(animated: true) } + #endif + // FIXME: macOS } }) .store(in: &cancellables) diff --git a/Source/Hollow/ViewModel/Hollow/ImageCompressStore.swift b/Source/Shared/ViewModel/Hollow/ImageCompressStore.swift similarity index 79% rename from Source/Hollow/ViewModel/Hollow/ImageCompressStore.swift rename to Source/Shared/ViewModel/Hollow/ImageCompressStore.swift index af8f3ec5..789c1407 100644 --- a/Source/Hollow/ViewModel/Hollow/ImageCompressStore.swift +++ b/Source/Shared/ViewModel/Hollow/ImageCompressStore.swift @@ -11,28 +11,28 @@ import SwiftUI /// Protocol for stores which handle image compression. protocol ImageCompressStore: class { - var image: UIImage? { get set } - var compressedImage: UIImage? { get set } + var image: HImage? { get set } + var compressedImage: HImage? { get set } var compressedImageBase64String: String? { get set } var errorMessage: (title: String, message: String)? { get set } var imageSizeInformation: String? { get set } - var cancelledImages: [UIImage] { get set } + var cancelledImages: [HImage] { get set } } extension ImageCompressStore { func handleDrop(providers: [NSItemProvider]) -> Bool { guard providers.count == 1 else { return false } - if let _ = providers.first?.loadObject(ofClass: UIImage.self, completionHandler: { image, error in - guard let image = image else { return } - DispatchQueue.main.async { - withAnimation { - self.cancel() - self.image = image as? UIImage - } - } - }) { - return true - } +// if let _ = providers.first?.loadObject(ofClass: HImage.self, completionHandler: { image, error in +// guard let image = image else { return } +// DispatchQueue.main.async { +// withAnimation { +// self.cancel() +// self.image = image as? HImage +// } +// } +// }) { +// return true +// } return false } @@ -71,7 +71,7 @@ extension ImageCompressStore { } } self.image = nil - self.compressedImage = UIImage(data: result.jpegData) + self.compressedImage = HImage(data: result.jpegData) self.imageSizeInformation = result.description self.compressedImageBase64String = result.base64String }} diff --git a/Source/Hollow/ViewModel/Hollow/PostListRequestStore.swift b/Source/Shared/ViewModel/Hollow/PostListRequestStore.swift similarity index 99% rename from Source/Hollow/ViewModel/Hollow/PostListRequestStore.swift rename to Source/Shared/ViewModel/Hollow/PostListRequestStore.swift index 6384deff..525f2ea9 100644 --- a/Source/Hollow/ViewModel/Hollow/PostListRequestStore.swift +++ b/Source/Shared/ViewModel/Hollow/PostListRequestStore.swift @@ -187,7 +187,7 @@ class PostListRequestStore: ObservableObject, AppModelEnvironment { .store(in: &cancellables) } - private func assignImage(_ image: UIImage, to postId: Int) { + private func assignImage(_ image: HImage, to postId: Int) { // We use the postId rather than the index to // identify which post to assign to. guard let index = posts.firstIndex(where: { $0.post.postId == postId }) else { return } diff --git a/Source/Hollow/ViewModel/Login/LoginStore.swift b/Source/Shared/ViewModel/Login/LoginStore.swift similarity index 98% rename from Source/Hollow/ViewModel/Login/LoginStore.swift rename to Source/Shared/ViewModel/Login/LoginStore.swift index c9a06bc9..fb99ba80 100644 --- a/Source/Hollow/ViewModel/Login/LoginStore.swift +++ b/Source/Shared/ViewModel/Login/LoginStore.swift @@ -77,7 +77,7 @@ class LoginStore: ObservableObject, AppModelEnvironment { guard let config = Defaults[.hollowConfig] else { return } guard let deviceTokenString = Defaults[.deviceToken]?.hexEncodedString() else { errorMessage = (title: "", message: .retriveDeviceTokenFailedMessageLocalized) - UIApplication.shared.registerForRemoteNotifications() + HApplication.shared.registerForRemoteNotifications() return } diff --git a/Source/Hollow/ViewModel/Login/WelcomeStore.swift b/Source/Shared/ViewModel/Login/WelcomeStore.swift similarity index 93% rename from Source/Hollow/ViewModel/Login/WelcomeStore.swift rename to Source/Shared/ViewModel/Login/WelcomeStore.swift index f024aba5..ed4cd634 100644 --- a/Source/Hollow/ViewModel/Login/WelcomeStore.swift +++ b/Source/Shared/ViewModel/Login/WelcomeStore.swift @@ -16,6 +16,9 @@ class WelcomeStore: ObservableObject { @Published var hollowSelection: Int? = nil @Published var isLoadingConfig = false @Published var errorMessage: (title: String, message: String)? = nil + #if os(macOS) + @Published var showLogin = false + #endif var cancellables = Set() @@ -56,6 +59,9 @@ class WelcomeStore: ObservableObject { withAnimation { self.hollowSelection = hollowType.rawValue } + #if os(macOS) + self.showLogin = true + #endif }) .store(in: &cancellables) From f5e8e38951acf418b8712280271e66a78d6eaaf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=81=E4=B8=9A=E5=8D=87?= Date: Mon, 26 Apr 2021 21:13:27 +0800 Subject: [PATCH 2/2] Add experimental features to resolve performance issues --- Source/Hollow.xcodeproj/project.pbxproj | 8 +- Source/Hollow/App/HollowApp.swift | 2 + .../Model/Extensions/UIDevice+isPad.swift | 4 + Source/Hollow/View/Components/Avatar.swift | 50 ++++++++++--- .../Hierarchy/Account/SettingsSubViews.swift | 75 ++++++++++++++++--- .../Hollow/Components/HollowHeaderView.swift | 3 +- .../Content/HollowCommentContentView.swift | 3 +- .../Hollow/Content/HollowImageView.swift | 6 +- .../Hollow/Input/HollowInputComponents.swift | 3 +- .../iPad/HollowDetailView_iPad.swift | 3 +- Source/Hollow/en.lproj/Localizable.strings | 18 ++++- .../Hollow/zh-Hans.lproj/Localizable.strings | 18 ++++- Source/Shared/Model/Constants.swift | 2 +- .../Model/Extensions/DefaultsKeys.swift | 7 +- 14 files changed, 159 insertions(+), 43 deletions(-) diff --git a/Source/Hollow.xcodeproj/project.pbxproj b/Source/Hollow.xcodeproj/project.pbxproj index 05d6d258..a8ad20d1 100644 --- a/Source/Hollow.xcodeproj/project.pbxproj +++ b/Source/Hollow.xcodeproj/project.pbxproj @@ -1803,7 +1803,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Hollow/Hollow.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 41; + CURRENT_PROJECT_VERSION = 42; DEVELOPMENT_TEAM = C5UH93T368; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = Hollow/Info.plist; @@ -1815,7 +1815,7 @@ MARKETING_VERSION = 3.0.6; PRODUCT_BUNDLE_IDENTIFIER = treehollow.Hollow; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTS_MACCATALYST = NO; + SUPPORTS_MACCATALYST = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -1829,7 +1829,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Hollow/Hollow.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 41; + CURRENT_PROJECT_VERSION = 42; DEVELOPMENT_TEAM = C5UH93T368; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = Hollow/Info.plist; @@ -1841,7 +1841,7 @@ MARKETING_VERSION = 3.0.6; PRODUCT_BUNDLE_IDENTIFIER = treehollow.Hollow; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTS_MACCATALYST = NO; + SUPPORTS_MACCATALYST = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; diff --git a/Source/Hollow/App/HollowApp.swift b/Source/Hollow/App/HollowApp.swift index 10d28707..468f970f 100644 --- a/Source/Hollow/App/HollowApp.swift +++ b/Source/Hollow/App/HollowApp.swift @@ -29,6 +29,8 @@ struct HollowApp: App { WelcomeView() } } + // Set larger size category for macOS + .conditionalSizeCategory() // Inject the app model into the environment .environmentObject(appModel) // Set the color scheme when appear diff --git a/Source/Hollow/Model/Extensions/UIDevice+isPad.swift b/Source/Hollow/Model/Extensions/UIDevice+isPad.swift index dc32abdf..781dfc28 100644 --- a/Source/Hollow/Model/Extensions/UIDevice+isPad.swift +++ b/Source/Hollow/Model/Extensions/UIDevice+isPad.swift @@ -10,5 +10,9 @@ import UIKit extension UIDevice { static let isPad = UIDevice.current.userInterfaceIdiom == .pad + #if targetEnvironment(macCatalyst) + static let isMac = true + #else static let isMac = false + #endif } diff --git a/Source/Hollow/View/Components/Avatar.swift b/Source/Hollow/View/Components/Avatar.swift index 5d7b88cf..9a904933 100644 --- a/Source/Hollow/View/Components/Avatar.swift +++ b/Source/Hollow/View/Components/Avatar.swift @@ -7,35 +7,61 @@ // import SwiftUI +import Defaults struct Avatar: View { var colors: [Color] var paddingColor: Color var resolution: Int var padding: CGFloat + var name: String + @Default(.usingSimpleAvatar) var usingSimpleAvatar + @Default(.usingOffscreenRender) var usingOffscreenRender - init(foregroundColor: Color, backgroundColor: Color, resolution: Int, padding: CGFloat, hashValue: Int) { + init(foregroundColor: Color, backgroundColor: Color, resolution: Int, padding: CGFloat, hashValue: Int, name: String) { self.paddingColor = foregroundColor self.colors = AvatarGenerator.colorData(foregroundColor: foregroundColor, backgroundColor: backgroundColor, resolution: resolution, hashValue: hashValue) self.resolution = resolution self.padding = padding + self.name = name } var body: some View { - VStack(spacing: 0) { - ForEach(0.. some View { + if apply { self.drawingGroup() } + else { self } } } diff --git a/Source/Hollow/View/Hierarchy/Account/SettingsSubViews.swift b/Source/Hollow/View/Hierarchy/Account/SettingsSubViews.swift index 7022ffc2..e6c317f5 100644 --- a/Source/Hollow/View/Hierarchy/Account/SettingsSubViews.swift +++ b/Source/Hollow/View/Hierarchy/Account/SettingsSubViews.swift @@ -352,7 +352,9 @@ struct OtherSettingsView: View { OpenURLSettingsView() #endif - EfficientModeSettingsView() + Section(header: Text("SETTINGSVIEW_OTHER_EXP_FEAT_NAV_TITLE").padding(.horizontal)) { + NavigationLink("SETTINGSVIEW_OTHER_EXP_FEAT_NAV_TITLE", destination: ExperimentalFeaturesView()) + } } .defaultListStyle() .navigationBarTitle(NSLocalizedString("SETTINGSVIEW_OTHER_NAV_TITLE", comment: "")) @@ -448,20 +450,69 @@ struct OtherSettingsView: View { } } - private struct EfficientModeSettingsView: View { - @Default(.enableEfficientMode) var enableEfficientMode + private struct ExperimentalFeaturesView: View { + @Default(.reduceImageQuality) var reduceImageQuality + @Default(.usingOffscreenRender) var usingOffscreenRender + @Default(.usingSimpleAvatar) var usingSimpleAvatar + var body: some View { - Section( - header: Text("SETTINGSVIEW_OTHER_PERFORMANCE_SECTION_HEADER").padding(.horizontal), - footer: Text("SETTINGSVIEW_OTHER_PERFORMANCE_SECTION_FOOTER").padding(.horizontal)) { - HStack { - Text("SETTINGSVIEW_OTHER_EFFICIENT_MODE_ENABLE") - Spacer() - Toggle("", isOn: $enableEfficientMode) - .toggleStyle(SwitchToggleStyle(tint: .tint)) - .labelsHidden() + List { + Text("SETTINGSVIEW_OTHER_EXP_FEAT_DESCRIPTION") + .listRowBackground(Color.clear) + + Section( + header: Text("SETTINGSVIEW_OTHER_EXP_REDUCE_IMG_QUALITY_HEADER").padding(.horizontal), + footer: Text("SETTINGSVIEW_OTHER_EXP_REDUCE_IMG_QUALITY_FOOTER").padding(.horizontal)) { + HStack { + Text("SETTINGSVIEW_OTHER_EXP_REDUCE_IMG_QUALITY_LABEL") + Spacer() + + Toggle("", isOn: $reduceImageQuality) + .toggleStyle(SwitchToggleStyle(tint: .tint)) + .labelsHidden() + } } + + Section( + header: Text("SETTINGSVIEW_OTHER_EXP_AVATAR_HEADER").padding(.horizontal), + footer: Text("SETTINGSVIEW_OTHER_EXP_AVATAR_FOOTER").padding(.horizontal)) { + HStack { + Text("SETTINGSVIEW_OTHER_EXP_AVATAR_OFFSCREEN_LABEL") + Spacer() + let isOn = Binding( + get: { usingOffscreenRender }, + set: { on in withAnimation { + if on { usingSimpleAvatar = false } + usingOffscreenRender = on + }} + ) + + Toggle("", isOn: isOn) + .toggleStyle(SwitchToggleStyle(tint: .tint)) + .labelsHidden() + } + + HStack { + Text("SETTINGSVIEW_OTHER_EXP_AVATAR_SIMPLE_LABEL") + Spacer() + + let isOn = Binding( + get: { usingSimpleAvatar }, + set: { on in withAnimation { + if on { usingOffscreenRender = false } + usingSimpleAvatar = on + }} + ) + Toggle("", isOn: isOn) + .toggleStyle(SwitchToggleStyle(tint: .tint)) + .labelsHidden() + } + + } + } + .defaultListStyle() + .navigationTitle(NSLocalizedString("SETTINGSVIEW_OTHER_EXP_FEAT_NAV_TITLE", comment: "")) } } } diff --git a/Source/Hollow/View/Hierarchy/Hollow/Components/HollowHeaderView.swift b/Source/Hollow/View/Hierarchy/Hollow/Components/HollowHeaderView.swift index 00e8375b..b2ba3c9a 100644 --- a/Source/Hollow/View/Hierarchy/Hollow/Components/HollowHeaderView.swift +++ b/Source/Hollow/View/Hierarchy/Hollow/Components/HollowHeaderView.swift @@ -45,7 +45,8 @@ struct _HollowHeaderView: View { backgroundColor: .white, resolution: 6, padding: body37 * 0.1, - hashValue: postData.hash + hashValue: postData.hash, + name: String(postData.postId.string.last ?? " ") ) // Scale the avatar relative to the font scaling. .frame(width: body37, height: body37) diff --git a/Source/Hollow/View/Hierarchy/Hollow/Content/HollowCommentContentView.swift b/Source/Hollow/View/Hierarchy/Hollow/Content/HollowCommentContentView.swift index c00c8da3..2db37d4f 100644 --- a/Source/Hollow/View/Hierarchy/Hollow/Content/HollowCommentContentView.swift +++ b/Source/Hollow/View/Hierarchy/Hollow/Content/HollowCommentContentView.swift @@ -163,7 +163,8 @@ struct HollowCommentContentView: View { backgroundColor: .white, resolution: resolution, padding: avatarWidth * 0.1, - hashValue: hash + hashValue: hash, + name: commentData.name ) .frame(width: avatarWidth) .clipShape(Circle()) diff --git a/Source/Hollow/View/Hierarchy/Hollow/Content/HollowImageView.swift b/Source/Hollow/View/Hierarchy/Hollow/Content/HollowImageView.swift index e2845f20..36009aeb 100644 --- a/Source/Hollow/View/Hierarchy/Hollow/Content/HollowImageView.swift +++ b/Source/Hollow/View/Hierarchy/Hollow/Content/HollowImageView.swift @@ -36,7 +36,7 @@ struct HollowImageView: View { } @ScaledMetric private var reloadButtonSize: CGFloat = 50 - @Default(.enableEfficientMode) var enableEfficientMode + @Default(.reduceImageQuality) var reduceImageQuality var body: some View { Group { @@ -46,7 +46,7 @@ struct HollowImageView: View { if imageAspectRatio >= aspectRatio - 0.001 { Image(uiImage: image) .resizable() - .interpolation(enableEfficientMode ? .none : .low) + .interpolation(reduceImageQuality ? .none : .low) .aspectRatio(contentMode: .fit) .imageSaver(image: image, showSavePhotoAlert: $showSavePhotoAlert, savePhotoError: $savePhotoError) } else { @@ -57,7 +57,7 @@ struct HollowImageView: View { Blur().ignoresSafeArea() Image(uiImage: image) .resizable() - .interpolation(enableEfficientMode ? .none : .low) + .interpolation(reduceImageQuality ? .none : .low) .aspectRatio(contentMode: .fit) .imageSaver(image: image, showSavePhotoAlert: $showSavePhotoAlert, savePhotoError: $savePhotoError) } diff --git a/Source/Hollow/View/Hierarchy/Hollow/Input/HollowInputComponents.swift b/Source/Hollow/View/Hierarchy/Hollow/Input/HollowInputComponents.swift index 912a176d..031f9afe 100644 --- a/Source/Hollow/View/Hierarchy/Hollow/Input/HollowInputComponents.swift +++ b/Source/Hollow/View/Hierarchy/Hollow/Input/HollowInputComponents.swift @@ -33,7 +33,8 @@ struct HollowInputAvatar: View { backgroundColor: .background, resolution: 6, padding: avatarWidth * 0.1, - hashValue: hash + hashValue: hash, + name: "N" ) .frame(width: avatarWidth) diff --git a/Source/Hollow/View/Hierarchy/iPad/HollowDetailView_iPad.swift b/Source/Hollow/View/Hierarchy/iPad/HollowDetailView_iPad.swift index 6a686b00..66e165e9 100644 --- a/Source/Hollow/View/Hierarchy/iPad/HollowDetailView_iPad.swift +++ b/Source/Hollow/View/Hierarchy/iPad/HollowDetailView_iPad.swift @@ -27,7 +27,8 @@ struct HollowDetailView_iPad: View { backgroundColor: .white, resolution: 6, padding: avatarWidth * 0.1, - hashValue: postData.hash + hashValue: postData.hash, + name: String(store.postDataWrapper.post.postId.string.last ?? " ") ) // Scale the avatar relative to the font scaling. .frame(width: avatarWidth, height: avatarWidth) diff --git a/Source/Hollow/en.lproj/Localizable.strings b/Source/Hollow/en.lproj/Localizable.strings index a0f7b4fa..7c634190 100644 --- a/Source/Hollow/en.lproj/Localizable.strings +++ b/Source/Hollow/en.lproj/Localizable.strings @@ -618,8 +618,20 @@ "IMAGEVIEW_SAVE_IMAGE_FAIL_ALERT_NOTE" = "\nPlease check your authorization settings"; -"SETTINGSVIEW_OTHER_PERFORMANCE_SECTION_HEADER" = "Performance"; +"SETTINGSVIEW_OTHER_EXP_FEAT_NAV_TITLE" = "Experimental Features"; -"SETTINGSVIEW_OTHER_PERFORMANCE_SECTION_FOOTER" = "Enabling efficient mode may result in lower rendering quality."; +"SETTINGSVIEW_OTHER_EXP_FEAT_DESCRIPTION" = "Options listed below are some experimental features that are not fully tested, so use them at your own risk. File a report if any error occured."; -"SETTINGSVIEW_OTHER_EFFICIENT_MODE_ENABLE" = "Efficient Mode"; +"SETTINGSVIEW_OTHER_EXP_REDUCE_IMG_QUALITY_HEADER" = "Image Render"; + +"SETTINGSVIEW_OTHER_EXP_REDUCE_IMG_QUALITY_FOOTER" = "Reduce the render quality in photo previews. Detailed image viewing are not affected."; + +"SETTINGSVIEW_OTHER_EXP_REDUCE_IMG_QUALITY_LABEL" = "Reduce Image Render Quality"; + +"SETTINGSVIEW_OTHER_EXP_AVATAR_HEADER" = "Avatar"; + +"SETTINGSVIEW_OTHER_EXP_AVATAR_FOOTER" = "Avatar rendering has some issues on few devices. Turn on the options if you have encountered hitch problems when scrolling. Noted that enabling Off-Screen Render might result in even lower refresh rate."; + +"SETTINGSVIEW_OTHER_EXP_AVATAR_OFFSCREEN_LABEL" = "Enable Off-Screen Render"; + +"SETTINGSVIEW_OTHER_EXP_AVATAR_SIMPLE_LABEL" = "Use Simple Avatars"; diff --git a/Source/Hollow/zh-Hans.lproj/Localizable.strings b/Source/Hollow/zh-Hans.lproj/Localizable.strings index f301f4f5..695f7464 100644 --- a/Source/Hollow/zh-Hans.lproj/Localizable.strings +++ b/Source/Hollow/zh-Hans.lproj/Localizable.strings @@ -619,8 +619,20 @@ "IMAGEVIEW_SAVE_IMAGE_FAIL_ALERT_NOTE" = "\n请检查权限设置"; -"SETTINGSVIEW_OTHER_PERFORMANCE_SECTION_HEADER" = "性能"; +"SETTINGSVIEW_OTHER_EXP_FEAT_NAV_TITLE" = "实验性功能"; -"SETTINGSVIEW_OTHER_PERFORMANCE_SECTION_FOOTER" = "启用高效模式可能会降低渲染质量。"; +"SETTINGSVIEW_OTHER_EXP_FEAT_DESCRIPTION" = "下面是一些未经充分测试的实验性功能。若出现问题,请发送反馈。"; -"SETTINGSVIEW_OTHER_EFFICIENT_MODE_ENABLE" = "高效模式"; +"SETTINGSVIEW_OTHER_EXP_REDUCE_IMG_QUALITY_HEADER" = "图像渲染"; + +"SETTINGSVIEW_OTHER_EXP_REDUCE_IMG_QUALITY_FOOTER" = "降低图片预览的渲染质量。图片查看不受影响。"; + +"SETTINGSVIEW_OTHER_EXP_REDUCE_IMG_QUALITY_LABEL" = "降低图片渲染质量"; + +"SETTINGSVIEW_OTHER_EXP_AVATAR_HEADER" = "头像"; + +"SETTINGSVIEW_OTHER_EXP_AVATAR_FOOTER" = "头像渲染在一些设备上存在问题,若当前使用遇到卡顿等问题,可以尝试打开这些选项。在某些设备上启用Off-Screen Render可能反而会降低刷新率。"; + +"SETTINGSVIEW_OTHER_EXP_AVATAR_OFFSCREEN_LABEL" = "启用Off-Screen Render"; + +"SETTINGSVIEW_OTHER_EXP_AVATAR_SIMPLE_LABEL" = "使用简易头像"; diff --git a/Source/Shared/Model/Constants.swift b/Source/Shared/Model/Constants.swift index db9c1d2c..f82c6d13 100644 --- a/Source/Shared/Model/Constants.swift +++ b/Source/Shared/Model/Constants.swift @@ -28,7 +28,7 @@ struct Constants { struct HollowConfig { static let thuConfigURL = "https://cdn.jsdelivr.net/gh/treehollow/thuhole-config@master/main.txt" - static let pkuConfigURL = "https://cdn.jsdelivr.net/gh/pkuhollow/pkuhollow-config@master/config.txt" + static let pkuConfigURL = "https://cdn.jsdelivr.net/gh/pkuhollow/pkuhollow-config@main/config.txt" } struct Net { diff --git a/Source/Shared/Model/Extensions/DefaultsKeys.swift b/Source/Shared/Model/Extensions/DefaultsKeys.swift index caaa69d9..9473570b 100644 --- a/Source/Shared/Model/Extensions/DefaultsKeys.swift +++ b/Source/Shared/Model/Extensions/DefaultsKeys.swift @@ -71,7 +71,12 @@ extension Defaults.Keys { static let tempCustomColorSet = Key("user.settings.temp.custom.color.set", default: nil) /// The method to handle links. static let openURLMethod = Key("user.settings.open.url.method", default: .inApp) +} - static let enableEfficientMode = Key("user.settings.enable.efficient.mode", default: false) +// MARK: - Experimental Features +extension Defaults.Keys { + static let reduceImageQuality = Key("user.settings.reduce.image.quality", default: false) + static let usingOffscreenRender = Key("user.settings.using.offscreen.render", default: false) + static let usingSimpleAvatar = Key("user.settings.using.simple.avatar", default: false) } #endif