From f58a886cb6f9c322df9232ae37c56e59f3b3d02f Mon Sep 17 00:00:00 2001 From: Bartek Waresiak Date: Mon, 4 Mar 2019 14:53:40 +0100 Subject: [PATCH 01/39] Wireframe for feedback disambiguation screen flow --- DuckDuckGo.xcodeproj/project.pbxproj | 44 +- DuckDuckGo/AppFeedbackViewController.swift | 117 ++++ .../ReportIcon.imageset/Contents.json | 12 + .../ReportIcon.imageset/iconReport.pdf | Bin 0 -> 4201 bytes .../AutoClearSettingsViewController.swift | 4 +- DuckDuckGo/Base.lproj/Settings.storyboard | 6 +- .../CategorizedFeedbackViewController.swift | 128 ++++ DuckDuckGo/DarkTheme.swift | 6 +- DuckDuckGo/DisambiguatedFeedbackModel.swift | 88 +++ DuckDuckGo/Feedback.storyboard | 588 +++++++++++++++++- DuckDuckGo/Feedback.xcassets/Contents.json | 6 + .../happyFace.imageset/Contents.json | 12 + .../happyFace.imageset/happy.pdf | Bin 0 -> 4403 bytes .../sadFace.imageset/Contents.json | 12 + .../sadFace.imageset/sad.pdf | Bin 0 -> 4404 bytes DuckDuckGo/LightTheme.swift | 6 +- DuckDuckGo/MainViewController.swift | 2 +- .../PositiveFeedbackViewController.swift | 76 +++ DuckDuckGo/SettingsViewController.swift | 12 +- ...swift => SiteFeedbackViewController.swift} | 10 +- DuckDuckGo/SubmitFeedbackViewController.swift | 128 ++++ DuckDuckGo/ThemableNavigationController.swift | 27 + DuckDuckGo/Theme.swift | 6 +- 23 files changed, 1237 insertions(+), 53 deletions(-) create mode 100644 DuckDuckGo/AppFeedbackViewController.swift create mode 100644 DuckDuckGo/Assets.xcassets/ReportIcon.imageset/Contents.json create mode 100644 DuckDuckGo/Assets.xcassets/ReportIcon.imageset/iconReport.pdf create mode 100644 DuckDuckGo/CategorizedFeedbackViewController.swift create mode 100644 DuckDuckGo/DisambiguatedFeedbackModel.swift create mode 100644 DuckDuckGo/Feedback.xcassets/Contents.json create mode 100644 DuckDuckGo/Feedback.xcassets/happyFace.imageset/Contents.json create mode 100644 DuckDuckGo/Feedback.xcassets/happyFace.imageset/happy.pdf create mode 100644 DuckDuckGo/Feedback.xcassets/sadFace.imageset/Contents.json create mode 100644 DuckDuckGo/Feedback.xcassets/sadFace.imageset/sad.pdf create mode 100644 DuckDuckGo/PositiveFeedbackViewController.swift rename DuckDuckGo/{FeedbackViewController.swift => SiteFeedbackViewController.swift} (96%) create mode 100644 DuckDuckGo/SubmitFeedbackViewController.swift create mode 100644 DuckDuckGo/ThemableNavigationController.swift diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index db5c1ae67b..1a328a65f3 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -59,7 +59,7 @@ 8390446F20BDCE10006461CD /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8390446E20BDCE10006461CD /* ShareViewController.swift */; }; 8390447220BDCE10006461CD /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8390447020BDCE10006461CD /* MainInterface.storyboard */; }; 8390447620BDCE10006461CD /* ShareExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 8390446C20BDCE10006461CD /* ShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - 839F46B620DD9D31003B8A66 /* FeedbackViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 839F46B520DD9D31003B8A66 /* FeedbackViewController.swift */; }; + 839F46B620DD9D31003B8A66 /* SiteFeedbackViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 839F46B520DD9D31003B8A66 /* SiteFeedbackViewController.swift */; }; 83A23D6320DC330000051F39 /* Feedback.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 83A23D6220DC330000051F39 /* Feedback.storyboard */; }; 83B8BDAF20AB14A70076D6A1 /* APIHeadersTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8BDAD20AB0DA90076D6A1 /* APIHeadersTests.swift */; }; 83BE9BC3215D69C1009844D9 /* AppConfigurationFetch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83BE9BC2215D69C1009844D9 /* AppConfigurationFetch.swift */; }; @@ -257,9 +257,16 @@ 981FED712202519C008488D7 /* BlankSnapshot.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 981FED702202519C008488D7 /* BlankSnapshot.storyboard */; }; 981FED7422046017008488D7 /* AutoClearTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 981FED7322046017008488D7 /* AutoClearTests.swift */; }; 981FED76220464EF008488D7 /* AutoClearSettingsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 981FED75220464EF008488D7 /* AutoClearSettingsModel.swift */; }; + 982E562E222C39F8008D861B /* DisambiguatedFeedbackModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 982E562D222C39F8008D861B /* DisambiguatedFeedbackModel.swift */; }; + 982E5630222C3D5B008D861B /* CategorizedFeedbackViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 982E562F222C3D5B008D861B /* CategorizedFeedbackViewController.swift */; }; + 98314735222D63E00062BDB1 /* ThemableNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98314734222D63E00062BDB1 /* ThemableNavigationController.swift */; }; + 9838059F2228208E00385F1A /* PositiveFeedbackViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9838059E2228208E00385F1A /* PositiveFeedbackViewController.swift */; }; + 984D60B2222A1284003B9E3B /* SubmitFeedbackViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 984D60B1222A1284003B9E3B /* SubmitFeedbackViewController.swift */; }; 9874F9EE2187AFCE00CAF33D /* Themable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9874F9ED2187AFCE00CAF33D /* Themable.swift */; }; + 9888F77B2224980500C46159 /* AppFeedbackViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9888F77A2224980500C46159 /* AppFeedbackViewController.swift */; }; 98B31290218CCB2200E54DE1 /* MockDependencyProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98B3128F218CCB2200E54DE1 /* MockDependencyProvider.swift */; }; 98B31292218CCB8C00E54DE1 /* AppDependencyProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98B31291218CCB8C00E54DE1 /* AppDependencyProvider.swift */; }; + 98DA6B3322243CC3006EA9EB /* Feedback.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 98DA6B3222243CC3006EA9EB /* Feedback.xcassets */; }; 98DA6ECA2181E41F00E65433 /* ThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98DA6EC92181E41F00E65433 /* ThemeManager.swift */; }; 98EA2C3C218B9AAD0023E1DC /* ThemeManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98EA2C3B218B9AAD0023E1DC /* ThemeManagerTests.swift */; }; 98EA2C41218BB5400023E1DC /* SettingsViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98EA2C40218BB5400023E1DC /* SettingsViewControllerTests.swift */; }; @@ -574,7 +581,7 @@ 8390446E20BDCE10006461CD /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = ""; }; 8390447120BDCE10006461CD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = ""; }; 8390447320BDCE10006461CD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 839F46B520DD9D31003B8A66 /* FeedbackViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbackViewController.swift; sourceTree = ""; }; + 839F46B520DD9D31003B8A66 /* SiteFeedbackViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteFeedbackViewController.swift; sourceTree = ""; }; 83A23D6220DC330000051F39 /* Feedback.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Feedback.storyboard; sourceTree = ""; }; 83B8BDAD20AB0DA90076D6A1 /* APIHeadersTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIHeadersTests.swift; sourceTree = ""; }; 83BE9BC2215D69C1009844D9 /* AppConfigurationFetch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConfigurationFetch.swift; sourceTree = ""; }; @@ -775,9 +782,16 @@ 981FED702202519C008488D7 /* BlankSnapshot.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = BlankSnapshot.storyboard; sourceTree = ""; }; 981FED7322046017008488D7 /* AutoClearTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoClearTests.swift; sourceTree = ""; }; 981FED75220464EF008488D7 /* AutoClearSettingsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoClearSettingsModel.swift; sourceTree = ""; }; + 982E562D222C39F8008D861B /* DisambiguatedFeedbackModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisambiguatedFeedbackModel.swift; sourceTree = ""; }; + 982E562F222C3D5B008D861B /* CategorizedFeedbackViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategorizedFeedbackViewController.swift; sourceTree = ""; }; + 98314734222D63E00062BDB1 /* ThemableNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemableNavigationController.swift; sourceTree = ""; }; + 9838059E2228208E00385F1A /* PositiveFeedbackViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositiveFeedbackViewController.swift; sourceTree = ""; }; + 984D60B1222A1284003B9E3B /* SubmitFeedbackViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubmitFeedbackViewController.swift; sourceTree = ""; }; 9874F9ED2187AFCE00CAF33D /* Themable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Themable.swift; sourceTree = ""; }; + 9888F77A2224980500C46159 /* AppFeedbackViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppFeedbackViewController.swift; sourceTree = ""; }; 98B3128F218CCB2200E54DE1 /* MockDependencyProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockDependencyProvider.swift; sourceTree = ""; }; 98B31291218CCB8C00E54DE1 /* AppDependencyProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDependencyProvider.swift; sourceTree = ""; }; + 98DA6B3222243CC3006EA9EB /* Feedback.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Feedback.xcassets; sourceTree = ""; }; 98DA6EC92181E41F00E65433 /* ThemeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeManager.swift; sourceTree = ""; }; 98EA2C3B218B9AAD0023E1DC /* ThemeManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeManagerTests.swift; sourceTree = ""; }; 98EA2C40218BB5400023E1DC /* SettingsViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewControllerTests.swift; sourceTree = ""; }; @@ -1221,6 +1235,7 @@ children = ( 8528AE7A212EF49200D0BD74 /* AppStore */, 83D4A73920E2455B003A7A6D /* API */, + 982E562C222C39E2008D861B /* Model */, 83D4A73820E2454D003A7A6D /* UI */, ); name = Feedback; @@ -1229,9 +1244,14 @@ 83D4A73820E2454D003A7A6D /* UI */ = { isa = PBXGroup; children = ( + 98DA6B3222243CC3006EA9EB /* Feedback.xcassets */, 83A23D6220DC330000051F39 /* Feedback.storyboard */, - 839F46B520DD9D31003B8A66 /* FeedbackViewController.swift */, + 839F46B520DD9D31003B8A66 /* SiteFeedbackViewController.swift */, 83D4A73620E1272F003A7A6D /* FeedbackModel.swift */, + 9888F77A2224980500C46159 /* AppFeedbackViewController.swift */, + 9838059E2228208E00385F1A /* PositiveFeedbackViewController.swift */, + 984D60B1222A1284003B9E3B /* SubmitFeedbackViewController.swift */, + 982E562F222C3D5B008D861B /* CategorizedFeedbackViewController.swift */, ); name = UI; sourceTree = ""; @@ -1627,6 +1647,14 @@ name = AutoClear; sourceTree = ""; }; + 982E562C222C39E2008D861B /* Model */ = { + isa = PBXGroup; + children = ( + 982E562D222C39F8008D861B /* DisambiguatedFeedbackModel.swift */, + ); + name = Model; + sourceTree = ""; + }; 98EA2C3A218B9A880023E1DC /* Themes */ = { isa = PBXGroup; children = ( @@ -2307,6 +2335,7 @@ F1D934021E610DCE00A6F0D6 /* Point.swift */, F143C32B1E4A9A4800CFDE3A /* RoundedRectangleView.swift */, F143C3451E4AA32D00CFDE3A /* SearchBarExtension.swift */, + 98314734222D63E00062BDB1 /* ThemableNavigationController.swift */, F197EA3B1E6885F20029BDC1 /* TextFieldWithInsets.swift */, 83004E832193E14C00DA013C /* UIAlertControllerExtension.swift */, F1B745211E549D550072547E /* UIColorExtension.swift */, @@ -2797,6 +2826,7 @@ 8563A0381F8E54F100F04442 /* Tab.storyboard in Resources */, 85371D242121B9D500920548 /* new_tab.json in Resources */, F1A169F31F3B2A8A00BE3E3B /* FlamesAnimation.xcassets in Resources */, + 98DA6B3322243CC3006EA9EB /* Feedback.xcassets in Resources */, F1E4A4471EE894CD006F2EAE /* Autocomplete.storyboard in Resources */, F14513531F45FAAE00710C46 /* SiteRatingView.xib in Resources */, 85AE66942098BAC10014CF04 /* home-row-instructions.mp4 in Resources */, @@ -3050,6 +3080,7 @@ 85374D3521AC26E300FF5A1E /* BookmarksAndFavoritesDataSource.swift in Sources */, 853C5F6121C277C7001F7A05 /* global.swift in Sources */, F13B4BD31F1822C700814661 /* Tab.swift in Sources */, + 982E562E222C39F8008D861B /* DisambiguatedFeedbackModel.swift in Sources */, F1BE54581E69DE1000FCF649 /* TutorialSettings.swift in Sources */, F1126E251EA749A30016A6C7 /* BlurAnimatedTransitioning.swift in Sources */, 98F3A1D8217B37010011A0D4 /* Theme.swift in Sources */, @@ -3067,6 +3098,7 @@ F103076D1E800F5C0059FEC7 /* EditBookmarkAlert.swift in Sources */, F1BE545E1E69FB9900FCF649 /* OnboardingTutorialPageViewController.swift in Sources */, F1134EC11F40DD9300B73467 /* AppVersionExtension.swift in Sources */, + 982E5630222C3D5B008D861B /* CategorizedFeedbackViewController.swift in Sources */, 85200F9D1FBC5A1C001AF290 /* NetworkLeaderboard.swift in Sources */, F13B4BD51F183B3600814661 /* TabsModelPersistenceExtension.swift in Sources */, F1134EE21F4233A200B73467 /* UIViewToastExtension.swift in Sources */, @@ -3076,6 +3108,7 @@ 85F200042216F5D8006BB258 /* FindInPageView.swift in Sources */, F1617C131E572E0300DEDCAF /* TabSwitcherViewController.swift in Sources */, 83BE9BC3215D69C1009844D9 /* AppConfigurationFetch.swift in Sources */, + 98314735222D63E00062BDB1 /* ThemableNavigationController.swift in Sources */, 85B9CB8921AEBDD5009001F1 /* FavoriteHomeCell.swift in Sources */, 85F2FFCF2211F8E5006BB258 /* TabSwitcherViewController+KeyCommands.swift in Sources */, F17922E01E71BB59006E3D97 /* AutocompleteViewControllerDelegate.swift in Sources */, @@ -3091,7 +3124,8 @@ 853C5F5D21C117A6001F7A05 /* PaddingSpaceHomeViewSectionRenderer.swift in Sources */, 85C11E4C2090888C00BFFEB4 /* HomeRowReminder.swift in Sources */, 85374D3C21AC41E700FF5A1E /* FavoritesHomeViewSectionRenderer.swift in Sources */, - 839F46B620DD9D31003B8A66 /* FeedbackViewController.swift in Sources */, + 839F46B620DD9D31003B8A66 /* SiteFeedbackViewController.swift in Sources */, + 9888F77B2224980500C46159 /* AppFeedbackViewController.swift in Sources */, F14513551F45FBFD00710C46 /* SiteRatingView.swift in Sources */, 85F1E9AC1FB49C0F00A75AC1 /* DisplayableCertificateBuilder.swift in Sources */, 85B9CB8521AEBD92009001F1 /* NavigationSearchHomeCell.swift in Sources */, @@ -3147,6 +3181,7 @@ F1D796EE1E7AF2EB0019D451 /* UIViewControllerExtension.swift in Sources */, F15D43201E706CC500BF2CDC /* AutocompleteViewController.swift in Sources */, 83004E862193E5ED00DA013C /* TabViewControllerBrowsingMenuExtension.swift in Sources */, + 9838059F2228208E00385F1A /* PositiveFeedbackViewController.swift in Sources */, F1AB2B421E3F7D5C00868554 /* SettingsViewController.swift in Sources */, 85200F911FBA38E2001AF290 /* PrivacyProtectionScoreCardController.swift in Sources */, F16390821E648B7A005B4550 /* HomeViewController.swift in Sources */, @@ -3158,6 +3193,7 @@ F1D796F41E7C2A410019D451 /* BookmarksDelegate.swift in Sources */, 8565A3471FC87EC800239327 /* PrivacyProtectionPracticesController.swift in Sources */, F17669D71E43401C003D3222 /* MainViewController.swift in Sources */, + 984D60B2222A1284003B9E3B /* SubmitFeedbackViewController.swift in Sources */, 85374D3821AC419800FF5A1E /* NavigationSearchHomeViewSectionRenderer.swift in Sources */, 85200FA41FBC607E001AF290 /* NetworkLeaderboard.xcdatamodeld in Sources */, 85BA58551F34F49E00C6E8CA /* AppUserDefaults.swift in Sources */, diff --git a/DuckDuckGo/AppFeedbackViewController.swift b/DuckDuckGo/AppFeedbackViewController.swift new file mode 100644 index 0000000000..64230b39fc --- /dev/null +++ b/DuckDuckGo/AppFeedbackViewController.swift @@ -0,0 +1,117 @@ +// +// AppFeedbackViewController.swift +// DuckDuckGo +// +// Copyright © 2019 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +class AppFeedbackViewController: UIViewController { + + @IBOutlet weak var closeButton: UIBarButtonItem! + + @IBOutlet weak var postitiveFeedbackButton: UIButton! + @IBOutlet weak var negativeFeedbackButton: UIButton! + + @IBOutlet weak var headerText: UILabel! + @IBOutlet weak var supplementaryText: UILabel! + @IBOutlet weak var footerText: UILabel! + + override func viewDidLoad() { + super.viewDidLoad() + + configureButtons() + applyTheme(ThemeManager.shared.currentTheme) + } + + private func configureButtons() { + postitiveFeedbackButton.round(corners: .allCorners, radius: 8) + negativeFeedbackButton.round(corners: .allCorners, radius: 8) + } + + @IBAction func onClosePressed(_ sender: Any) { + dismiss(animated: true, completion: nil) + } + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + guard let controller = segue.destination as? CategorizedFeedbackViewController else { + return + } + + let isNavigatingToCategories = sender as? UIButton == negativeFeedbackButton + if isNavigatingToCategories { + controller.loadViewIfNeeded() + controller.configure(with: DisambiguatedFeedbackCategory.allCases) + controller.setSelectionHandler { [weak self, controller] categoryString in + if let category = DisambiguatedFeedbackCategory(rawValue: categoryString) { + if category == .otherIssues { + controller.performSegue(withIdentifier: "PresentSubmitFeedback", sender: nil) + return + } + + if category == .websiteLoadingIssues { + return + } + } + + self?.performSegue(withIdentifier: "PresentCategories", sender: controller) + } + return + } + + if let senderVC = sender as? CategorizedFeedbackViewController { + guard let selectedCategoryString = senderVC.selectedCategory, + let category = DisambiguatedFeedbackCategory(rawValue: selectedCategoryString) else { + return + } + + controller.loadViewIfNeeded() + controller.configureForSubcategories(with: category) + controller.setSelectionHandler { [weak controller] _ in + controller?.performSegue(withIdentifier: "PresentSubmitFeedback", sender: nil) + } + } + + } +} + +extension AppFeedbackViewController { + + func presentSubcategories(for category: DisambiguatedFeedbackCategory) { + + } + + func presentFeedbackForm(for category: DisambiguatedFeedbackCategory, subcategory: String) { + + } +} + +extension AppFeedbackViewController: Themable { + + func decorate(with theme: Theme) { + decorateNavigationBar(with: theme) + + view.backgroundColor = theme.backgroundColor + + postitiveFeedbackButton.backgroundColor = theme.feedbackSentimentButtonBackgroundColor + negativeFeedbackButton.backgroundColor = theme.feedbackSentimentButtonBackgroundColor + + headerText.textColor = theme.feedbackPrimaryTextColor + supplementaryText.textColor = theme.feedbackSecondaryTextColor + footerText.textColor = theme.feedbackSecondaryTextColor + } + +} diff --git a/DuckDuckGo/Assets.xcassets/ReportIcon.imageset/Contents.json b/DuckDuckGo/Assets.xcassets/ReportIcon.imageset/Contents.json new file mode 100644 index 0000000000..3208110506 --- /dev/null +++ b/DuckDuckGo/Assets.xcassets/ReportIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "iconReport.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DuckDuckGo/Assets.xcassets/ReportIcon.imageset/iconReport.pdf b/DuckDuckGo/Assets.xcassets/ReportIcon.imageset/iconReport.pdf new file mode 100644 index 0000000000000000000000000000000000000000..00b6eb6e9a22a96aa947912b172f25a0915be456 GIT binary patch literal 4201 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 - + @@ -412,10 +412,10 @@ - + - + diff --git a/DuckDuckGo/CategorizedFeedbackViewController.swift b/DuckDuckGo/CategorizedFeedbackViewController.swift new file mode 100644 index 0000000000..1d7d583801 --- /dev/null +++ b/DuckDuckGo/CategorizedFeedbackViewController.swift @@ -0,0 +1,128 @@ +// +// CategorizedFeedbackViewController.swift +// DuckDuckGo +// +// Copyright © 2019 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +class CategorizedFeedbackViewController: UITableViewController { + + @IBOutlet weak var headerView: UIView! + @IBOutlet weak var headerText: UILabel! + @IBOutlet weak var supplementaryText: UILabel! + + private var options = [String]() + private var selectionHandler: (String) -> Void = { _ in } + + private(set) var selectedCategory: String? + + override func viewDidLoad() { + super.viewDidLoad() + + applyTheme(ThemeManager.shared.currentTheme) + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + + guard let headerView = tableView.tableHeaderView else { + return + } + + let size = headerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) + if headerView.frame.size.height != size.height { + headerView.frame.size.height = size.height + tableView.tableHeaderView = headerView + tableView.layoutIfNeeded() + } + } + + func configure(with categories: [DisambiguatedFeedbackCategory]) { + options = categories.map { $0.rawValue } + } + + func configureForSubcategories(with category: DisambiguatedFeedbackCategory) { + supplementaryText.text = category.subcategoryCaption + options = category.subcategories + } + + func setSelectionHandler(_ handler: @escaping (String) -> Void) { + self.selectionHandler = handler + } + + @IBAction func dismissButtonPressed() { + dismiss(animated: true, completion: nil) + } + + // MARK: Table View + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.deselectRow(at: indexPath, animated: true) + + selectedCategory = options[indexPath.row] + selectionHandler(options[indexPath.row]) + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return options.count + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + + guard let cell = tableView.dequeueReusableCell(withIdentifier: "CategoryCell") else { + fatalError("Failed to dequeue CategoryCell") + } + + let theme = ThemeManager.shared.currentTheme + cell.contentView.backgroundColor = theme.tableCellBackgroundColor + cell.backgroundColor = theme.tableCellBackgroundColor + cell.textLabel?.textColor = theme.tableCellTintColor + cell.textLabel?.text = options[indexPath.row] + + return cell + } + +} + +extension CategorizedFeedbackViewController { + + func presentSubmitFeedbackScreen(a: String, b: String) { + performSegue(withIdentifier: "PresentSubmitFeedback", sender: nil) + } + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + guard let controller = segue.destination as? SubmitFeedbackViewController else { + return + } + + controller.loadViewIfNeeded() + controller.configureForNegativeSentiment(headerText: "aaa", detailsText: "bbb") + } +} + +extension CategorizedFeedbackViewController: Themable { + + func decorate(with theme: Theme) { + tableView.separatorColor = theme.tableCellSeparatorColor + tableView.backgroundColor = theme.backgroundColor + + headerView.backgroundColor = theme.backgroundColor + headerText.textColor = theme.feedbackPrimaryTextColor + supplementaryText.textColor = theme.feedbackSecondaryTextColor + } + +} diff --git a/DuckDuckGo/DarkTheme.swift b/DuckDuckGo/DarkTheme.swift index 163695f3c4..3f2905935b 100644 --- a/DuckDuckGo/DarkTheme.swift +++ b/DuckDuckGo/DarkTheme.swift @@ -44,7 +44,7 @@ struct DarkTheme: Theme { var tableCellAccessoryTextColor = UIColor.greyish var tableHeaderTextColor = UIColor.lightGreyish - var toggleSwitchColor = UIColor.cornflowerBlue + var buttonTintColor = UIColor.cornflowerBlue var homeRowPrimaryTextColor = UIColor.white var homeRowSecondaryTextColor = UIColor.lightMercury @@ -59,5 +59,9 @@ struct DarkTheme: Theme { var faviconBackgroundColor = UIColor.charcoalGrey var favoriteTextColor = UIColor.greyish + var feedbackPrimaryTextColor = UIColor.white + var feedbackSecondaryTextColor = UIColor.lightGreyish + var feedbackSentimentButtonBackgroundColor = UIColor.charcoalGrey + var activityStyle: UIActivityIndicatorView.Style = .white } diff --git a/DuckDuckGo/DisambiguatedFeedbackModel.swift b/DuckDuckGo/DisambiguatedFeedbackModel.swift new file mode 100644 index 0000000000..8eb72e752e --- /dev/null +++ b/DuckDuckGo/DisambiguatedFeedbackModel.swift @@ -0,0 +1,88 @@ +// +// DisambiguatedFeedbackModel.swift +// DuckDuckGo +// +// Copyright © 2019 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +enum DisambiguatedFeedbackCategory: String, CaseIterable { + + case browserFeatureIssues = "Browsing features are missing or frustrating" + case websiteLoadingIssues = "Certain websites don't load correctly" + case ddgSearchIssues = "DuckDuckGo search isn't good enough" + case customizationIssues = "There aren't enough ways to customize the app" + case performanceIssues = "The app is slow, buggy, or crashes" + case otherIssues = "None of these" + + var subcategories: [String] { + switch self { + case .browserFeatureIssues: + return ["Navigating forward, backward, and/or refreshing", + "Creating and managing tabs", + "Ad and pop-up blocking", + "Watching videos", + "Interacting with images", + "Creating and managing bookmarks", + "None of these" + ] + case .websiteLoadingIssues: + return [] + case .ddgSearchIssues: + return ["Programming/technical search", + "The layout should be more like Google", + "Faster load time", + "Searching in a specific language or reason", + "Better autocomplete", + "None of these" + ] + case .customizationIssues: + return ["The home screen configuration", + "How tabs are displayed", + "How the app looks", + "Which data is cleared", + "When data is cleared", + "How bookmarks are displayed", + "None of these" + ] + case .performanceIssues: + return ["Web pages or search results load slowly", + "The app crashes or freezes", + "Video or media playback bugs", + "None of these" + ] + case .otherIssues: + return [] + } + } + + var subcategoryCaption: String { + switch self { + case .browserFeatureIssues: + return "Which browsing features should be added or improved to make you more likely to continue using DuckDuckGo?" + case .websiteLoadingIssues: + return "" + case .ddgSearchIssues: + return "Which DuckDuckGo search improvements would make you more likely to continue using the app?" + case .customizationIssues: + return "Which additional customization options would make you more likely to continue using the app?" + case .performanceIssues: + return "Which issues should be fixed to make you more likely to continue using DuckDuckGo?" + case .otherIssues: + return "" + } + } +} diff --git a/DuckDuckGo/Feedback.storyboard b/DuckDuckGo/Feedback.storyboard index 609ee3e588..86e5761860 100644 --- a/DuckDuckGo/Feedback.storyboard +++ b/DuckDuckGo/Feedback.storyboard @@ -1,15 +1,18 @@ - + - + + + ProximaNova-Bold + ProximaNova-Regular @@ -18,10 +21,27 @@ - + + + + + + + + + + + + + + + + + + - + @@ -39,22 +59,15 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DuckDuckGo/Feedback.xcassets/Contents.json b/DuckDuckGo/Feedback.xcassets/Contents.json new file mode 100644 index 0000000000..da4a164c91 --- /dev/null +++ b/DuckDuckGo/Feedback.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DuckDuckGo/Feedback.xcassets/happyFace.imageset/Contents.json b/DuckDuckGo/Feedback.xcassets/happyFace.imageset/Contents.json new file mode 100644 index 0000000000..9063a4ff58 --- /dev/null +++ b/DuckDuckGo/Feedback.xcassets/happyFace.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "happy.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DuckDuckGo/Feedback.xcassets/happyFace.imageset/happy.pdf b/DuckDuckGo/Feedback.xcassets/happyFace.imageset/happy.pdf new file mode 100644 index 0000000000000000000000000000000000000000..72bbee6932bc1ee58525122c10874fb9e199e4f0 GIT binary patch literal 4403 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 literal 0 HcmV?d00001 diff --git a/DuckDuckGo/LightTheme.swift b/DuckDuckGo/LightTheme.swift index 8cadf25d51..3603ee1611 100644 --- a/DuckDuckGo/LightTheme.swift +++ b/DuckDuckGo/LightTheme.swift @@ -44,7 +44,7 @@ struct LightTheme: Theme { var tableCellAccessoryTextColor = UIColor.greyish3 var tableHeaderTextColor = UIColor.greyish3 - var toggleSwitchColor = UIColor.cornflowerBlue + var buttonTintColor = UIColor.cornflowerBlue var homeRowPrimaryTextColor = UIColor.nearlyBlackLight var homeRowSecondaryTextColor = UIColor.greyishBrown2 @@ -59,5 +59,9 @@ struct LightTheme: Theme { var faviconBackgroundColor = UIColor.white var favoriteTextColor = UIColor.darkGreyish + var feedbackPrimaryTextColor = UIColor.nearlyBlackLight + var feedbackSecondaryTextColor = UIColor.nearlyBlackLight + var feedbackSentimentButtonBackgroundColor = UIColor.white + var activityStyle: UIActivityIndicatorView.Style = .gray } diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 29552e8a22..c562aeb848 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -180,7 +180,7 @@ class MainViewController: UIViewController { return } - if let controller = segue.destination as? FeedbackViewController { + if let controller = segue.destination as? SiteFeedbackViewController { controller.prepareForSegue(isBrokenSite: true, url: currentTab?.url?.absoluteString) return } diff --git a/DuckDuckGo/PositiveFeedbackViewController.swift b/DuckDuckGo/PositiveFeedbackViewController.swift new file mode 100644 index 0000000000..3721d28fc0 --- /dev/null +++ b/DuckDuckGo/PositiveFeedbackViewController.swift @@ -0,0 +1,76 @@ +// +// PositiveFeedbackViewController.swift +// DuckDuckGo +// +// Copyright © 2019 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +class PositiveFeedbackViewController: UIViewController { + + @IBOutlet weak var headerText: UILabel! + @IBOutlet weak var supplementaryText: UILabel! + + @IBOutlet weak var rateAppButton: UIButton! + @IBOutlet weak var leaveFeedbackButton: UIButton! + @IBOutlet weak var dismissButton: UIButton! + + override func viewDidLoad() { + super.viewDidLoad() + + configureButtons() + + applyTheme(ThemeManager.shared.currentTheme) + } + + private func configureButtons() { + leaveFeedbackButton.layer.borderWidth = 1 + } + + @IBAction func rateAppButtonPressed() { + let urlStr = "itms://itunes.apple.com/us/app/duckduckgo-privacy-browser/id663592361" + + UIApplication.shared.open(URL(string: urlStr)!) + } + + @IBAction func leaveFeedbackPressed() { +// dismiss(animated: true, completion: nil) + } + + @IBAction func dismissButtonPressed() { + dismiss(animated: true, completion: nil) + } + +} + +extension PositiveFeedbackViewController: Themable { + + func decorate(with theme: Theme) { + view.backgroundColor = theme.backgroundColor + + headerText.textColor = theme.feedbackPrimaryTextColor + supplementaryText.textColor = theme.feedbackSecondaryTextColor + + rateAppButton.setTitleColor(UIColor.white, for: .normal) + rateAppButton.tintColor = theme.buttonTintColor + + leaveFeedbackButton.setTitleColor(theme.buttonTintColor, for: .normal) + leaveFeedbackButton.layer.borderColor = theme.buttonTintColor.cgColor + + dismissButton.setTitleColor(theme.buttonTintColor, for: .normal) + } + +} diff --git a/DuckDuckGo/SettingsViewController.swift b/DuckDuckGo/SettingsViewController.swift index 42fd26486f..95a8f6e0b5 100644 --- a/DuckDuckGo/SettingsViewController.swift +++ b/DuckDuckGo/SettingsViewController.swift @@ -63,6 +63,12 @@ class SettingsViewController: UITableViewController { if segue.destination is AutoClearSettingsViewController { Pixel.fire(pixel: .autoClearSettingsShown) } + + if segue.destination is AppFeedbackViewController { + if UIDevice.current.userInterfaceIdiom == .pad { + segue.destination.modalPresentationStyle = .formSheet + } + } } private func configureMargins() { @@ -150,9 +156,9 @@ extension SettingsViewController: Themable { autoClearAccessoryText.textColor = theme.tableCellAccessoryTextColor versionText.textColor = theme.tableCellTintColor - lightThemeToggle.onTintColor = theme.toggleSwitchColor - autocompleteToggle.onTintColor = theme.toggleSwitchColor - authenticationToggle.onTintColor = theme.toggleSwitchColor + lightThemeToggle.onTintColor = theme.buttonTintColor + autocompleteToggle.onTintColor = theme.buttonTintColor + authenticationToggle.onTintColor = theme.buttonTintColor tableView.backgroundColor = theme.backgroundColor tableView.separatorColor = theme.tableCellSeparatorColor diff --git a/DuckDuckGo/FeedbackViewController.swift b/DuckDuckGo/SiteFeedbackViewController.swift similarity index 96% rename from DuckDuckGo/FeedbackViewController.swift rename to DuckDuckGo/SiteFeedbackViewController.swift index d0901b2df5..930b73b746 100644 --- a/DuckDuckGo/FeedbackViewController.swift +++ b/DuckDuckGo/SiteFeedbackViewController.swift @@ -1,5 +1,5 @@ // -// FeedbackViewController.swift +// SiteFeedbackViewController.swift // DuckDuckGo // // Copyright © 2018 DuckDuckGo. All rights reserved. @@ -23,7 +23,7 @@ import UIKit import Core import ToastSwiftFramework -class FeedbackViewController: UIViewController { +class SiteFeedbackViewController: UIViewController { private struct ViewConstants { static let urlTextHeight: CGFloat = 38 @@ -185,7 +185,7 @@ class FeedbackViewController: UIViewController { } } -extension FeedbackViewController: UITextViewDelegate { +extension SiteFeedbackViewController: UITextViewDelegate { func textViewDidChange(_ textView: UITextView) { messagePlaceholderText.isHidden = !textView.text.isEmpty @@ -194,7 +194,7 @@ extension FeedbackViewController: UITextViewDelegate { } } -extension FeedbackViewController: UITextFieldDelegate { +extension SiteFeedbackViewController: UITextFieldDelegate { func textFieldShouldReturn(_ textField: UITextField) -> Bool { messageTextView.becomeFirstResponder() @@ -202,7 +202,7 @@ extension FeedbackViewController: UITextFieldDelegate { } } -extension FeedbackViewController: Themable { +extension SiteFeedbackViewController: Themable { func decorate(with theme: Theme) { urlTextField.keyboardAppearance = theme.keyboardAppearance diff --git a/DuckDuckGo/SubmitFeedbackViewController.swift b/DuckDuckGo/SubmitFeedbackViewController.swift new file mode 100644 index 0000000000..5531146111 --- /dev/null +++ b/DuckDuckGo/SubmitFeedbackViewController.swift @@ -0,0 +1,128 @@ +// +// ShareFeedbackViewController.swift +// DuckDuckGo +// +// Copyright © 2019 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +class SubmitFeedbackViewController: UIViewController { + + @IBOutlet weak var scrollView: UIScrollView! + + @IBOutlet weak var headerImage: UIImageView! + @IBOutlet weak var headerText: UILabel! + @IBOutlet weak var supplementaryText: UILabel! + + @IBOutlet weak var messageTextView: UITextView! + @IBOutlet weak var messagePlaceholderText: UILabel! + + @IBOutlet weak var submitFeedbackButton: UIButton! + + override func viewDidLoad() { + super.viewDidLoad() + + registerForKeyboardNotifications() + + applyTheme(ThemeManager.shared.currentTheme) + } + + func configureForPositiveSentiment() { + headerImage.image = UIImage(named: "happyFace") + headerText.text = "Share Feedback" + supplementaryText.text = "Are there any details you’d like to share with the team?" + } + + func configureForNegativeSentiment(headerText: String, detailsText: String) { + headerImage.image = UIImage(named: "sadFace") + self.headerText.text = headerText + supplementaryText.text = detailsText + } + + @IBAction func submitFeedbackPressed() { + // TODO + dismiss(animated: true, completion: nil) + } + + @IBAction func cancelButtonPressed() { + dismiss(animated: true, completion: nil) + } + + private func refreshSubmitFeedbackButton() { + submitFeedbackButton.isEnabled = !messageTextView.text.isEmpty + } + + // MARK: Keyboard + + private func registerForKeyboardNotifications() { + NotificationCenter.default.addObserver(self, + selector: #selector(keyboardDidShow), + name: UIResponder.keyboardDidShowNotification, + object: nil) + + NotificationCenter.default.addObserver(self, + selector: #selector(keyboardWillHide), + name: UIResponder.keyboardWillHideNotification, + object: nil) + } + + @objc private func keyboardDidShow(notification: NSNotification) { + guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return } + let keyboardSize = keyboardFrame.size + let contentInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: keyboardSize.height, right: 0.0) + scrollView.contentInset = contentInsets + scrollView.scrollIndicatorInsets = contentInsets + + guard messageTextView.isFirstResponder else { return } + var rect = self.view.frame + rect.size.height -= keyboardSize.height + + let messageViewFrame = messageTextView.frame + let messageViewBottom = CGPoint(x: messageViewFrame.origin.x, + y: messageViewFrame.origin.y + messageViewFrame.size.height) + if !rect.contains(messageViewBottom) { + scrollView.scrollRectToVisible(messageTextView.frame, animated: true) + } + } + + @objc private func keyboardWillHide(notification: NSNotification) { + let contentInsets = UIEdgeInsets.zero + scrollView.contentInset = contentInsets + scrollView.scrollIndicatorInsets = contentInsets + } +} + +extension SubmitFeedbackViewController: UITextViewDelegate { + + func textViewDidChange(_ textView: UITextView) { + messagePlaceholderText.isHidden = !textView.text.isEmpty + refreshSubmitFeedbackButton() + } +} + +extension SubmitFeedbackViewController: Themable { + + func decorate(with theme: Theme) { + view.backgroundColor = theme.backgroundColor + + headerText.textColor = theme.feedbackPrimaryTextColor + supplementaryText.textColor = theme.feedbackSecondaryTextColor + + submitFeedbackButton.setTitleColor(UIColor.white, for: .normal) + submitFeedbackButton.tintColor = theme.buttonTintColor + } + +} diff --git a/DuckDuckGo/ThemableNavigationController.swift b/DuckDuckGo/ThemableNavigationController.swift new file mode 100644 index 0000000000..caf7220b84 --- /dev/null +++ b/DuckDuckGo/ThemableNavigationController.swift @@ -0,0 +1,27 @@ +// +// CustomizedNavigationController.swift +// DuckDuckGo +// +// Copyright © 2019 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +class ThemableNavigationController: UINavigationController { + + override var preferredStatusBarStyle: UIStatusBarStyle { + return ThemeManager.shared.currentTheme.statusBarStyle + } +} diff --git a/DuckDuckGo/Theme.swift b/DuckDuckGo/Theme.swift index 6c219c8280..5f0bf1b6a2 100644 --- a/DuckDuckGo/Theme.swift +++ b/DuckDuckGo/Theme.swift @@ -51,7 +51,7 @@ protocol Theme { var tableCellAccessoryTextColor: UIColor { get } var tableHeaderTextColor: UIColor { get } - var toggleSwitchColor: UIColor { get } + var buttonTintColor: UIColor { get } var homeRowPrimaryTextColor: UIColor { get } var homeRowSecondaryTextColor: UIColor { get } @@ -65,6 +65,10 @@ protocol Theme { var faviconBackgroundColor: UIColor { get } var favoriteTextColor: UIColor { get } + var feedbackPrimaryTextColor: UIColor { get } + var feedbackSecondaryTextColor: UIColor { get } + var feedbackSentimentButtonBackgroundColor: UIColor { get } + var activityStyle: UIActivityIndicatorView.Style { get } } From 500278453b02bb17bbe26b58fb14ae7e50ee5fcd Mon Sep 17 00:00:00 2001 From: Bartek Waresiak Date: Mon, 4 Mar 2019 18:47:26 +0100 Subject: [PATCH 02/39] Tweaks and refactoring --- DuckDuckGo/AppFeedbackViewController.swift | 43 ++++++-------- .../CategorizedFeedbackViewController.swift | 58 ++++++++++++++----- DuckDuckGo/DisambiguatedFeedbackModel.swift | 24 +++++++- DuckDuckGo/Feedback.storyboard | 39 ++++++++----- DuckDuckGo/SettingsViewController.swift | 2 +- DuckDuckGo/SubmitFeedbackViewController.swift | 1 + 6 files changed, 109 insertions(+), 58 deletions(-) diff --git a/DuckDuckGo/AppFeedbackViewController.swift b/DuckDuckGo/AppFeedbackViewController.swift index 64230b39fc..ff2f48c085 100644 --- a/DuckDuckGo/AppFeedbackViewController.swift +++ b/DuckDuckGo/AppFeedbackViewController.swift @@ -55,33 +55,17 @@ class AppFeedbackViewController: UIViewController { if isNavigatingToCategories { controller.loadViewIfNeeded() controller.configure(with: DisambiguatedFeedbackCategory.allCases) - controller.setSelectionHandler { [weak self, controller] categoryString in - if let category = DisambiguatedFeedbackCategory(rawValue: categoryString) { - if category == .otherIssues { - controller.performSegue(withIdentifier: "PresentSubmitFeedback", sender: nil) - return - } - - if category == .websiteLoadingIssues { - return - } - } - - self?.performSegue(withIdentifier: "PresentCategories", sender: controller) + controller.setSelectionHandler { [weak self, controller] model in + self?.categoriesScreen(controller, didFinishSelection: model) } return } - if let senderVC = sender as? CategorizedFeedbackViewController { - guard let selectedCategoryString = senderVC.selectedCategory, - let category = DisambiguatedFeedbackCategory(rawValue: selectedCategoryString) else { - return - } - + if let model = sender as? DisambiguatedFeedbackModel { controller.loadViewIfNeeded() - controller.configureForSubcategories(with: category) - controller.setSelectionHandler { [weak controller] _ in - controller?.performSegue(withIdentifier: "PresentSubmitFeedback", sender: nil) + controller.configureForSubcategories(with: model) + controller.setSelectionHandler { [weak controller] model in + controller?.performSegue(withIdentifier: "PresentSubmitFeedback", sender: model) } } @@ -90,13 +74,20 @@ class AppFeedbackViewController: UIViewController { extension AppFeedbackViewController { - func presentSubcategories(for category: DisambiguatedFeedbackCategory) { + func categoriesScreen(_ controller: UIViewController, didFinishSelection model: DisambiguatedFeedbackModel) { + if model.category == .otherIssues { + controller.performSegue(withIdentifier: "PresentSubmitFeedback", sender: model) + return + } - } - - func presentFeedbackForm(for category: DisambiguatedFeedbackCategory, subcategory: String) { + if model.category == .websiteLoadingIssues { + controller.performSegue(withIdentifier: "PresentBrokenSiteForm", sender: model) + return + } + performSegue(withIdentifier: "PresentCategories", sender: model) } + } extension AppFeedbackViewController: Themable { diff --git a/DuckDuckGo/CategorizedFeedbackViewController.swift b/DuckDuckGo/CategorizedFeedbackViewController.swift index 1d7d583801..2915eb1e69 100644 --- a/DuckDuckGo/CategorizedFeedbackViewController.swift +++ b/DuckDuckGo/CategorizedFeedbackViewController.swift @@ -26,9 +26,9 @@ class CategorizedFeedbackViewController: UITableViewController { @IBOutlet weak var supplementaryText: UILabel! private var options = [String]() - private var selectionHandler: (String) -> Void = { _ in } + private var selectionHandler: (DisambiguatedFeedbackModel) -> Void = { _ in } - private(set) var selectedCategory: String? + private var existingModel: DisambiguatedFeedbackModel? override func viewDidLoad() { super.viewDidLoad() @@ -55,12 +55,19 @@ class CategorizedFeedbackViewController: UITableViewController { options = categories.map { $0.rawValue } } - func configureForSubcategories(with category: DisambiguatedFeedbackCategory) { - supplementaryText.text = category.subcategoryCaption + func configureForSubcategories(with model: DisambiguatedFeedbackModel) { + guard let category = model.category else { + fatalError("Feedback model has empty category") + } + + existingModel = model + + headerText.text = category.caption + supplementaryText.text = category.subcategoriesCaption options = category.subcategories } - func setSelectionHandler(_ handler: @escaping (String) -> Void) { + func setSelectionHandler(_ handler: @escaping (DisambiguatedFeedbackModel) -> Void) { self.selectionHandler = handler } @@ -73,8 +80,20 @@ class CategorizedFeedbackViewController: UITableViewController { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: true) - selectedCategory = options[indexPath.row] - selectionHandler(options[indexPath.row]) + var model: DisambiguatedFeedbackModel + if let existingModel = existingModel { + model = existingModel + } else { + model = DisambiguatedFeedbackModel() + } + + if model.category == nil { + model.category = DisambiguatedFeedbackCategory(rawValue: options[indexPath.row]) + } else { + model.subcategory = options[indexPath.row] + } + + selectionHandler(model) } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { @@ -100,17 +119,28 @@ class CategorizedFeedbackViewController: UITableViewController { extension CategorizedFeedbackViewController { - func presentSubmitFeedbackScreen(a: String, b: String) { - performSegue(withIdentifier: "PresentSubmitFeedback", sender: nil) - } - override func prepare(for segue: UIStoryboardSegue, sender: Any?) { - guard let controller = segue.destination as? SubmitFeedbackViewController else { + if let controller = segue.destination as? SubmitFeedbackViewController, + let model = sender as? DisambiguatedFeedbackModel { + controller.loadViewIfNeeded() + + let detailsText: String + if let subcategory = model.subcategory, + subcategory != DisambiguatedFeedbackCategory.otherIssues.rawValue { + detailsText = subcategory + } else { + detailsText = "Please tell us what we can improve" + } + + controller.configureForNegativeSentiment(headerText: model.category?.caption ?? "", + detailsText: detailsText) return } - controller.loadViewIfNeeded() - controller.configureForNegativeSentiment(headerText: "aaa", detailsText: "bbb") + if let controller = segue.destination as? SiteFeedbackViewController { + controller.prepareForSegue(isBrokenSite: true, url: nil) + return + } } } diff --git a/DuckDuckGo/DisambiguatedFeedbackModel.swift b/DuckDuckGo/DisambiguatedFeedbackModel.swift index 8eb72e752e..1db5f5ec2b 100644 --- a/DuckDuckGo/DisambiguatedFeedbackModel.swift +++ b/DuckDuckGo/DisambiguatedFeedbackModel.swift @@ -19,6 +19,11 @@ import Foundation +struct DisambiguatedFeedbackModel { + var category: DisambiguatedFeedbackCategory? + var subcategory: String? +} + enum DisambiguatedFeedbackCategory: String, CaseIterable { case browserFeatureIssues = "Browsing features are missing or frustrating" @@ -69,7 +74,24 @@ enum DisambiguatedFeedbackCategory: String, CaseIterable { } } - var subcategoryCaption: String { + var caption: String { + switch self { + case .browserFeatureIssues: + return "Browser feature issues" + case .websiteLoadingIssues: + return "Website loading issues" + case .ddgSearchIssues: + return "Search issues" + case .customizationIssues: + return "Customization issues" + case .performanceIssues: + return "Performance issues" + case .otherIssues: + return "Other issues" + } + } + + var subcategoriesCaption: String { switch self { case .browserFeatureIssues: return "Which browsing features should be added or improved to make you more likely to continue using DuckDuckGo?" diff --git a/DuckDuckGo/Feedback.storyboard b/DuckDuckGo/Feedback.storyboard index 86e5761860..8eb66cfe5a 100644 --- a/DuckDuckGo/Feedback.storyboard +++ b/DuckDuckGo/Feedback.storyboard @@ -58,14 +58,14 @@ - @@ -519,7 +519,7 @@ - + @@ -546,13 +546,19 @@ - @@ -777,6 +784,6 @@ - + diff --git a/DuckDuckGo/SettingsViewController.swift b/DuckDuckGo/SettingsViewController.swift index 95a8f6e0b5..f6795b134e 100644 --- a/DuckDuckGo/SettingsViewController.swift +++ b/DuckDuckGo/SettingsViewController.swift @@ -64,7 +64,7 @@ class SettingsViewController: UITableViewController { Pixel.fire(pixel: .autoClearSettingsShown) } - if segue.destination is AppFeedbackViewController { + if let navController = segue.destination as? UINavigationController, navController.topViewController is AppFeedbackViewController { if UIDevice.current.userInterfaceIdiom == .pad { segue.destination.modalPresentationStyle = .formSheet } diff --git a/DuckDuckGo/SubmitFeedbackViewController.swift b/DuckDuckGo/SubmitFeedbackViewController.swift index 5531146111..4768ba9dab 100644 --- a/DuckDuckGo/SubmitFeedbackViewController.swift +++ b/DuckDuckGo/SubmitFeedbackViewController.swift @@ -53,6 +53,7 @@ class SubmitFeedbackViewController: UIViewController { } @IBAction func submitFeedbackPressed() { + view.window?.makeToast(UserText.feedbackSumbittedConfirmation) // TODO dismiss(animated: true, completion: nil) } From b43edb8f445800d35830c049d8c7d00fb32c7e9a Mon Sep 17 00:00:00 2001 From: Bartek Waresiak Date: Mon, 4 Mar 2019 19:29:50 +0100 Subject: [PATCH 03/39] AdHoc beta 1 --- DuckDuckGo/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo/Info.plist b/DuckDuckGo/Info.plist index 50f9523b2b..02f34fe1e2 100644 --- a/DuckDuckGo/Info.plist +++ b/DuckDuckGo/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 7.16.0 + 7.16.0+fd1 CFBundleURLTypes From 1b968a3230476e7b315ce591657ae25b80559578 Mon Sep 17 00:00:00 2001 From: Bartek Waresiak Date: Tue, 5 Mar 2019 21:48:18 +0100 Subject: [PATCH 04/39] UI Tweaks --- DuckDuckGo/AppFeedbackViewController.swift | 13 +++++- .../CategorizedFeedbackViewController.swift | 15 +++++-- DuckDuckGo/Feedback.storyboard | 43 +++++++++++-------- 3 files changed, 48 insertions(+), 23 deletions(-) diff --git a/DuckDuckGo/AppFeedbackViewController.swift b/DuckDuckGo/AppFeedbackViewController.swift index ff2f48c085..689aa773d9 100644 --- a/DuckDuckGo/AppFeedbackViewController.swift +++ b/DuckDuckGo/AppFeedbackViewController.swift @@ -38,8 +38,17 @@ class AppFeedbackViewController: UIViewController { } private func configureButtons() { - postitiveFeedbackButton.round(corners: .allCorners, radius: 8) - negativeFeedbackButton.round(corners: .allCorners, radius: 8) + postitiveFeedbackButton.layer.cornerRadius = 8 + postitiveFeedbackButton.layer.shadowOffset = CGSize(width: 0, height: 3) + postitiveFeedbackButton.layer.shadowColor = UIColor.black.cgColor + postitiveFeedbackButton.layer.shadowOpacity = 0.12 + postitiveFeedbackButton.layer.shadowRadius = 3 + + negativeFeedbackButton.layer.cornerRadius = 8 + negativeFeedbackButton.layer.shadowOffset = CGSize(width: 0, height: 3) + negativeFeedbackButton.layer.shadowColor = UIColor.black.cgColor + negativeFeedbackButton.layer.shadowOpacity = 0.12 + negativeFeedbackButton.layer.shadowRadius = 3 } @IBAction func onClosePressed(_ sender: Any) { diff --git a/DuckDuckGo/CategorizedFeedbackViewController.swift b/DuckDuckGo/CategorizedFeedbackViewController.swift index 2915eb1e69..f2a15cca07 100644 --- a/DuckDuckGo/CategorizedFeedbackViewController.swift +++ b/DuckDuckGo/CategorizedFeedbackViewController.swift @@ -62,8 +62,14 @@ class CategorizedFeedbackViewController: UITableViewController { existingModel = model - headerText.text = category.caption - supplementaryText.text = category.subcategoriesCaption + let headerString = headerText.attributedText?.mutableCopy() as? NSMutableAttributedString + headerString?.mutableString.setString(category.caption) + headerText.attributedText = headerString + + let supplementaryString = supplementaryText.attributedText?.mutableCopy() as? NSMutableAttributedString + supplementaryString?.mutableString.setString(category.subcategoriesCaption) + supplementaryText.attributedText = supplementaryString + options = category.subcategories } @@ -110,7 +116,10 @@ class CategorizedFeedbackViewController: UITableViewController { cell.contentView.backgroundColor = theme.tableCellBackgroundColor cell.backgroundColor = theme.tableCellBackgroundColor cell.textLabel?.textColor = theme.tableCellTintColor - cell.textLabel?.text = options[indexPath.row] + + let text = cell.textLabel?.attributedText?.mutableCopy() as? NSMutableAttributedString + text?.mutableString.setString(options[indexPath.row]) + cell.textLabel?.attributedText = text return cell } diff --git a/DuckDuckGo/Feedback.storyboard b/DuckDuckGo/Feedback.storyboard index 8eb66cfe5a..46501c3d3f 100644 --- a/DuckDuckGo/Feedback.storyboard +++ b/DuckDuckGo/Feedback.storyboard @@ -59,13 +59,13 @@ - - - - - - + + - - + + - - - - - + - + + + + + - - + + - + + + - + @@ -360,10 +360,10 @@ - + From 41fa43bc3d8ac9b1583a35e0e92df3529376e316 Mon Sep 17 00:00:00 2001 From: Bartek Waresiak Date: Wed, 13 Mar 2019 23:41:42 +0100 Subject: [PATCH 16/39] Open review page directly in AppStore --- DuckDuckGo/PositiveFeedbackViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo/PositiveFeedbackViewController.swift b/DuckDuckGo/PositiveFeedbackViewController.swift index c6358e1931..0c5274e479 100644 --- a/DuckDuckGo/PositiveFeedbackViewController.swift +++ b/DuckDuckGo/PositiveFeedbackViewController.swift @@ -51,7 +51,7 @@ class PositiveFeedbackViewController: UIViewController { } @IBAction func rateAppButtonPressed() { - let urlStr = "itms://itunes.apple.com/us/app/duckduckgo-privacy-browser/id663592361" + let urlStr = "itms-apps://itunes.apple.com/us/app/duckduckgo-privacy-browser/id663592361?action=write-review" UIApplication.shared.open(URL(string: urlStr)!) } From d936a335a03bf4c9e5618ddd4e73ec2bc74d9f9f Mon Sep 17 00:00:00 2001 From: Bartek Waresiak Date: Wed, 13 Mar 2019 23:52:04 +0100 Subject: [PATCH 17/39] Apply dark theme to feedback form input fields --- DuckDuckGo/DarkTheme.swift | 3 +++ DuckDuckGo/FeedbackFormViewController.swift | 10 ++++++++-- DuckDuckGo/LightTheme.swift | 3 +++ DuckDuckGo/Theme.swift | 3 +++ 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/DuckDuckGo/DarkTheme.swift b/DuckDuckGo/DarkTheme.swift index 4774099f4f..0cba711d31 100644 --- a/DuckDuckGo/DarkTheme.swift +++ b/DuckDuckGo/DarkTheme.swift @@ -47,6 +47,9 @@ struct DarkTheme: Theme { var buttonTintColor = UIColor.cornflowerBlue var placeholderColor = UIColor.greyish + var textFieldBackgroundColor = UIColor.nearlyBlackLight + var textFieldFontColor = UIColor.white + var homeRowPrimaryTextColor = UIColor.white var homeRowSecondaryTextColor = UIColor.lightMercury var homeRowBackgroundColor = UIColor.nearlyBlackLight diff --git a/DuckDuckGo/FeedbackFormViewController.swift b/DuckDuckGo/FeedbackFormViewController.swift index 7179f04bb0..be63d7af6b 100644 --- a/DuckDuckGo/FeedbackFormViewController.swift +++ b/DuckDuckGo/FeedbackFormViewController.swift @@ -193,7 +193,7 @@ extension FeedbackFormViewController: UITextViewDelegate { let font = UIFont.appFont(ofSize: Constants.inputFieldFontSize) let attributes = [ NSAttributedString.Key.font: font, - NSAttributedString.Key.foregroundColor: UIColor.black, + NSAttributedString.Key.foregroundColor: ThemeManager.shared.currentTheme.textFieldFontColor, NSAttributedString.Key.paragraphStyle: paragraphStyle] textView.typingAttributes = attributes } @@ -212,9 +212,15 @@ extension FeedbackFormViewController: Themable { headerText.textColor = theme.feedbackPrimaryTextColor supplementaryText.textColor = theme.feedbackSecondaryTextColor - configureWebsiteField(with: websiteTextField.placeholder ?? "") messagePlaceholderText.textColor = ThemeManager.shared.currentTheme.placeholderColor + messageTextView.backgroundColor = theme.textFieldBackgroundColor + messageTextView.keyboardAppearance = theme.keyboardAppearance + websiteTextField.backgroundColor = theme.textFieldBackgroundColor + websiteTextField.textColor = theme.textFieldFontColor + websiteTextField.keyboardAppearance = theme.keyboardAppearance + configureWebsiteField(with: websiteTextField.placeholder ?? "") + submitFeedbackButton.setTitleColor(UIColor.white, for: .normal) submitFeedbackButton.tintColor = theme.buttonTintColor } diff --git a/DuckDuckGo/LightTheme.swift b/DuckDuckGo/LightTheme.swift index 8bf9a2de40..bcf45453ea 100644 --- a/DuckDuckGo/LightTheme.swift +++ b/DuckDuckGo/LightTheme.swift @@ -47,6 +47,9 @@ struct LightTheme: Theme { var buttonTintColor = UIColor.cornflowerBlue var placeholderColor = UIColor.greyish3 + var textFieldBackgroundColor = UIColor.white + var textFieldFontColor = UIColor.nearlyBlackLight + var homeRowPrimaryTextColor = UIColor.nearlyBlackLight var homeRowSecondaryTextColor = UIColor.greyishBrown2 var homeRowBackgroundColor = UIColor.nearlyWhiteLight diff --git a/DuckDuckGo/Theme.swift b/DuckDuckGo/Theme.swift index e566e3f3ab..7e4c93d299 100644 --- a/DuckDuckGo/Theme.swift +++ b/DuckDuckGo/Theme.swift @@ -54,6 +54,9 @@ protocol Theme { var buttonTintColor: UIColor { get } var placeholderColor: UIColor { get } + var textFieldBackgroundColor: UIColor { get } + var textFieldFontColor: UIColor { get } + var homeRowPrimaryTextColor: UIColor { get } var homeRowSecondaryTextColor: UIColor { get } var homeRowBackgroundColor: UIColor { get } From 185e09a6f4eccc14cda9b36799fbc9998baa3058 Mon Sep 17 00:00:00 2001 From: Bartek Waresiak Date: Thu, 14 Mar 2019 00:21:41 +0100 Subject: [PATCH 18/39] Use title-case for category descriptions --- DuckDuckGo/en.lproj/Localizable.strings | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/DuckDuckGo/en.lproj/Localizable.strings b/DuckDuckGo/en.lproj/Localizable.strings index eb4268a047..9e5577584e 100644 --- a/DuckDuckGo/en.lproj/Localizable.strings +++ b/DuckDuckGo/en.lproj/Localizable.strings @@ -185,7 +185,7 @@ "feedback.negative.form.genericPlaceholder" = "Please be as specific as possible"; "feedback.browserFeatures.entry" = "Browsing features are missing or frustrating"; -"feedback.browserFeatures.description" = "Browser feature issues"; +"feedback.browserFeatures.description" = "Browser Feature Issues"; "feedback.browserFeatures.caption" = "Which browsing feature can we add or improve?"; "feedback.browserFeatures.navigation" = "Navigating forward, backward, and/or refreshing"; "feedback.browserFeatures.tabs" = "Creating and managing tabs"; @@ -196,13 +196,13 @@ "feedback.browserFeatures.other" = "None of these"; "feedback.websiteLoading.entry" = "Certain websites don't load correctly"; -"feedback.websiteLoading.description" = "Website loading issues"; +"feedback.websiteLoading.description" = "Website Loading Issues"; "feedback.websiteLoading.form.supplementary" = "Where are you seeing these issues?"; "feedback.websiteLoading.form.urlPlaceholder" = "Which website has issues?"; "feedback.websiteLoading.form.placeholder" = "What content seems to be affected?"; "feedback.ddgSearch.entry" = "DuckDuckGo search isn't good enough"; -"feedback.ddgSearch.description" = "DuckDuckGo search issues"; +"feedback.ddgSearch.description" = "DuckDuckGo Search Issues"; "feedback.ddgSearch.caption" = "Which search feature can we add or improve?"; "feedback.ddgSearch.technical" = "Programming/technical search"; "feedback.ddgSearch.layout" = "The layout should be more like Google"; @@ -212,7 +212,7 @@ "feedback.ddgSearch.other" = "None of these"; "feedback.customization.entry" = "There aren't enough ways to customize the app"; -"feedback.customization.description" = "Customization issues"; +"feedback.customization.description" = "Customization Issues"; "feedback.customization.caption" = "Which customization option can we add or improve?"; "feedback.customization.homeScreen" = "The home screen configuration"; "feedback.customization.tabs" = "How tabs are displayed"; @@ -223,7 +223,7 @@ "feedback.customization.other" = "None of these"; "feedback.performance.entry" = "The app is slow, buggy, or crashes"; -"feedback.performance.description" = "Performance issues"; +"feedback.performance.description" = "Performance Issues"; "feedback.performance.caption" = "Which issue are you experiencing?"; "feedback.performance.slowLoading" = "Web pages or search results load slowly"; "feedback.performance.crashes" = "The app crashes or freezes"; @@ -231,7 +231,7 @@ "feedback.performance.other" = "None of these"; "feedback.other.entry" = "None of these"; -"feedback.other.description" = "Other issues"; +"feedback.other.description" = "Other Issues"; "feedback.form.caption" = "Please tell us what we can improve"; From 531fd3befdc4ec12439e454c8be08547e8eaaa6c Mon Sep 17 00:00:00 2001 From: Bartek Waresiak Date: Thu, 14 Mar 2019 09:40:57 +0100 Subject: [PATCH 19/39] AdHoc build for Design Review 3 --- DuckDuckGo/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo/Info.plist b/DuckDuckGo/Info.plist index 46ebdda396..a85f1c35a7 100644 --- a/DuckDuckGo/Info.plist +++ b/DuckDuckGo/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 7.16.1+fd2 + 7.16.1+fd3 CFBundleURLTypes From 4c9080999fad987e2fa9d6f255d11d69f455d5c9 Mon Sep 17 00:00:00 2001 From: Bartek Waresiak Date: Thu, 14 Mar 2019 09:41:20 +0100 Subject: [PATCH 20/39] Various tweaks and cleanups --- DuckDuckGo/AppDelegate.swift | 2 +- DuckDuckGo/Base.lproj/Settings.storyboard | 72 +++++++++++------------ DuckDuckGo/Feedback.storyboard | 2 +- DuckDuckGo/Feedback.swift | 4 +- 4 files changed, 39 insertions(+), 41 deletions(-) diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index 67cb4b09ad..f17f0217ff 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -46,7 +46,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return true } - //installTouchWindow() + installTouchWindow() EasyTipView.updateGlobalPreferences() HTTPSUpgrade.shared.loadDataAsync() diff --git a/DuckDuckGo/Base.lproj/Settings.storyboard b/DuckDuckGo/Base.lproj/Settings.storyboard index 4b7b4d7b68..4000914a26 100644 --- a/DuckDuckGo/Base.lproj/Settings.storyboard +++ b/DuckDuckGo/Base.lproj/Settings.storyboard @@ -43,7 +43,7 @@