Skip to content

Commit

Permalink
New Feedback Form (#424)
Browse files Browse the repository at this point in the history
New improved way how we collect feedback reports and breakage reports
  • Loading branch information
tomasstrba authored Feb 24, 2022
1 parent eb24a6e commit f2ce3e9
Show file tree
Hide file tree
Showing 16 changed files with 1,056 additions and 22 deletions.
52 changes: 52 additions & 0 deletions DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,11 @@
AA34397B2754D55100B241FA /* trackers-3.json in Resources */ = {isa = PBXBuildFile; fileRef = AA3439752754D55100B241FA /* trackers-3.json */; };
AA34397C2754D55100B241FA /* dark-trackers-1.json in Resources */ = {isa = PBXBuildFile; fileRef = AA3439762754D55100B241FA /* dark-trackers-1.json */; };
AA34397D2754D55100B241FA /* dark-trackers-3.json in Resources */ = {isa = PBXBuildFile; fileRef = AA3439772754D55100B241FA /* dark-trackers-3.json */; };
AA3863C527A1E28F00749AB5 /* Feedback.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA3863C427A1E28F00749AB5 /* Feedback.storyboard */; };
AA3D531527A1ED9300074EC1 /* FeedbackWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3D531427A1ED9300074EC1 /* FeedbackWindow.swift */; };
AA3D531727A1EEED00074EC1 /* FeedbackViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3D531627A1EEED00074EC1 /* FeedbackViewController.swift */; };
AA3D531B27A2F57E00074EC1 /* Feedback.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3D531A27A2F57E00074EC1 /* Feedback.swift */; };
AA3D531D27A2F58F00074EC1 /* FeedbackSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3D531C27A2F58F00074EC1 /* FeedbackSender.swift */; };
AA3F895324C18AD500628DDE /* SuggestionViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3F895224C18AD500628DDE /* SuggestionViewModel.swift */; };
AA4BBA3B25C58FA200C4FB0F /* MainMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA4BBA3A25C58FA200C4FB0F /* MainMenu.swift */; };
AA4D700725545EF800C3411E /* URLEventHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA4D700625545EF800C3411E /* URLEventHandler.swift */; };
Expand Down Expand Up @@ -431,6 +436,8 @@
AAC9C01E24CB6BEB00AD1325 /* TabCollectionViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAC9C01D24CB6BEB00AD1325 /* TabCollectionViewModelTests.swift */; };
AACF6FD626BC366D00CF09F9 /* SafariVersionReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = AACF6FD526BC366D00CF09F9 /* SafariVersionReader.swift */; };
AAD6D8882696DF6D002393B3 /* CrashReportPromptViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAD6D8862696DF6D002393B3 /* CrashReportPromptViewController.swift */; };
AAD8078527B3F3BE00CF7703 /* WebsiteBreakageSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAD8078427B3F3BE00CF7703 /* WebsiteBreakageSender.swift */; };
AAD8078727B3F45600CF7703 /* WebsiteBreakage.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAD8078627B3F45600CF7703 /* WebsiteBreakage.swift */; };
AAD86E52267A0DFF005C11BE /* UpdateController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAD86E51267A0DFF005C11BE /* UpdateController.swift */; };
AADCBF3A26F7C2CE00EF67A8 /* LottieAnimationCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = AADCBF3926F7C2CE00EF67A8 /* LottieAnimationCache.swift */; };
AADE11C026D916D70032D8A7 /* StringExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AADE11BF26D916D70032D8A7 /* StringExtensionTests.swift */; };
Expand Down Expand Up @@ -1019,6 +1026,11 @@
AA3439752754D55100B241FA /* trackers-3.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "trackers-3.json"; sourceTree = "<group>"; };
AA3439762754D55100B241FA /* dark-trackers-1.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "dark-trackers-1.json"; sourceTree = "<group>"; };
AA3439772754D55100B241FA /* dark-trackers-3.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "dark-trackers-3.json"; sourceTree = "<group>"; };
AA3863C427A1E28F00749AB5 /* Feedback.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Feedback.storyboard; sourceTree = "<group>"; };
AA3D531427A1ED9300074EC1 /* FeedbackWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbackWindow.swift; sourceTree = "<group>"; };
AA3D531627A1EEED00074EC1 /* FeedbackViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbackViewController.swift; sourceTree = "<group>"; };
AA3D531A27A2F57E00074EC1 /* Feedback.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Feedback.swift; sourceTree = "<group>"; };
AA3D531C27A2F58F00074EC1 /* FeedbackSender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbackSender.swift; sourceTree = "<group>"; };
AA3F895224C18AD500628DDE /* SuggestionViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestionViewModel.swift; sourceTree = "<group>"; };
AA4BBA3A25C58FA200C4FB0F /* MainMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenu.swift; sourceTree = "<group>"; };
AA4D700625545EF800C3411E /* URLEventHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLEventHandler.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1140,6 +1152,8 @@
AAC9C01D24CB6BEB00AD1325 /* TabCollectionViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabCollectionViewModelTests.swift; sourceTree = "<group>"; };
AACF6FD526BC366D00CF09F9 /* SafariVersionReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariVersionReader.swift; sourceTree = "<group>"; };
AAD6D8862696DF6D002393B3 /* CrashReportPromptViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrashReportPromptViewController.swift; sourceTree = "<group>"; };
AAD8078427B3F3BE00CF7703 /* WebsiteBreakageSender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebsiteBreakageSender.swift; sourceTree = "<group>"; };
AAD8078627B3F45600CF7703 /* WebsiteBreakage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebsiteBreakage.swift; sourceTree = "<group>"; };
AAD86E502678D104005C11BE /* DuckDuckGoCI.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DuckDuckGoCI.entitlements; sourceTree = "<group>"; };
AAD86E51267A0DFF005C11BE /* UpdateController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateController.swift; sourceTree = "<group>"; };
AADCBF3926F7C2CE00EF67A8 /* LottieAnimationCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LottieAnimationCache.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2347,6 +2361,36 @@
path = Model;
sourceTree = "<group>";
};
AA3863C227A1E1C000749AB5 /* Feedback and Breakage */ = {
isa = PBXGroup;
children = (
AA3D531827A2F24C00074EC1 /* View */,
AA3D531927A2F47100074EC1 /* Model */,
);
path = "Feedback and Breakage";
sourceTree = "<group>";
};
AA3D531827A2F24C00074EC1 /* View */ = {
isa = PBXGroup;
children = (
AA3863C427A1E28F00749AB5 /* Feedback.storyboard */,
AA3D531427A1ED9300074EC1 /* FeedbackWindow.swift */,
AA3D531627A1EEED00074EC1 /* FeedbackViewController.swift */,
);
path = View;
sourceTree = "<group>";
};
AA3D531927A2F47100074EC1 /* Model */ = {
isa = PBXGroup;
children = (
AA3D531A27A2F57E00074EC1 /* Feedback.swift */,
AA3D531C27A2F58F00074EC1 /* FeedbackSender.swift */,
AAD8078627B3F45600CF7703 /* WebsiteBreakage.swift */,
AAD8078427B3F3BE00CF7703 /* WebsiteBreakageSender.swift */,
);
path = Model;
sourceTree = "<group>";
};
AA4D700525545EDE00C3411E /* AppDelegate */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -2410,6 +2454,7 @@
4B723DF826B0002B00E14D75 /* Data Export */,
4B65143C26392483005B46EB /* Email */,
AA5FA695275F823900DCE9C9 /* Favicons */,
AA3863C227A1E1C000749AB5 /* Feedback and Breakage */,
8556A60C256C15C60092FA9D /* FileDownload */,
85A0115D25AF1C4700FA6A0C /* FindInPage */,
AA6820E825503A21005ED0D5 /* Fire */,
Expand Down Expand Up @@ -3796,6 +3841,7 @@
AAE71E3825F7869300D74437 /* HomepageCollectionViewItem.xib in Resources */,
AA3439792754D55100B241FA /* trackers-1.json in Resources */,
AA34397C2754D55100B241FA /* dark-trackers-1.json in Resources */,
AA3863C527A1E28F00749AB5 /* Feedback.storyboard in Resources */,
B31055C527A1BA1D001AC618 /* autoconsent.html in Resources */,
4B723E1126B0006C00E14D75 /* DataImport.storyboard in Resources */,
4B92929026670D1700AD2C21 /* BookmarkTableCellView.xib in Resources */,
Expand Down Expand Up @@ -3996,6 +4042,7 @@
B610F2BB27A145C500FCEBE9 /* RulesCompilationMonitor.swift in Sources */,
AAC30A28268E045400D2D9CD /* CrashReportReader.swift in Sources */,
85AC3B3525DA82A600C7D2AA /* DataTaskProviding.swift in Sources */,
AA3D531727A1EEED00074EC1 /* FeedbackViewController.swift in Sources */,
AAEF6BC8276A081C0024DCF4 /* FaviconSelector.swift in Sources */,
4B2E7D6326FF9D6500D2DB17 /* PrintingUserScript.swift in Sources */,
0230C0A52721F3750018F728 /* GPCRequestFactory.swift in Sources */,
Expand All @@ -4011,6 +4058,7 @@
B6A924D42664BBBB001A28CA /* WKWebViewDownloadDelegate.swift in Sources */,
AA9B7C8526A199B60008D425 /* ServerTrustViewModel.swift in Sources */,
85480FCF25D1AA22009424E3 /* ConfigurationStoring.swift in Sources */,
AA3D531B27A2F57E00074EC1 /* Feedback.swift in Sources */,
4BB99D0626FE1979001E4761 /* RequestFilePermissionViewController.swift in Sources */,
858A798326A8B75F00A75A42 /* CopyHandler.swift in Sources */,
4B8AC93926B48A5100879451 /* FirefoxLoginReader.swift in Sources */,
Expand Down Expand Up @@ -4074,6 +4122,7 @@
B6B1E88426D5EB570062C350 /* DownloadsCellView.swift in Sources */,
4B723E0C26B0005D00E14D75 /* CSVImportSummaryViewController.swift in Sources */,
B6AAAC2D260330580029438D /* PublishedAfter.swift in Sources */,
AAD8078727B3F45600CF7703 /* WebsiteBreakage.swift in Sources */,
4BE6547E271FCD4D008D1D63 /* PasswordManagementIdentityModel.swift in Sources */,
85C6A29625CC1FFD00EEB5F1 /* UserDefaultsWrapper.swift in Sources */,
85625998269C9C5F00EE44BC /* PasswordManagementPopover.swift in Sources */,
Expand Down Expand Up @@ -4122,6 +4171,7 @@
4B9292D32667123700AD2C21 /* AddBookmarkModalViewController.swift in Sources */,
AA88D14B252A557100980B4E /* URLRequestExtension.swift in Sources */,
AA6197C6276B3168008396F0 /* FaviconHostReference.swift in Sources */,
AAD8078527B3F3BE00CF7703 /* WebsiteBreakageSender.swift in Sources */,
4B8AC93B26B48ADF00879451 /* ASN1Parser.swift in Sources */,
B66E9DD22670EB2A00E53BB5 /* _WKDownload+WebKitDownload.swift in Sources */,
B6A9E4612614608B0067D1B9 /* AppVersion.swift in Sources */,
Expand Down Expand Up @@ -4305,6 +4355,7 @@
85C48CD127908C1000D3263E /* BrowserImportMoreInfoViewController.swift in Sources */,
B69B50572727D16900758A2B /* AtbAndVariantCleanup.swift in Sources */,
B693954A26F04BEB0015B914 /* NibLoadable.swift in Sources */,
AA3D531527A1ED9300074EC1 /* FeedbackWindow.swift in Sources */,
AA7412B724D1687000D22FE0 /* TabBarScrollView.swift in Sources */,
4B0511C7262CAA5A00F6079C /* PreferenceTableCellView.swift in Sources */,
4B9292D92667124B00AD2C21 /* BookmarkListTreeControllerDataSource.swift in Sources */,
Expand Down Expand Up @@ -4352,6 +4403,7 @@
85378D9E274E664C007C5CBF /* PopoverMessageViewController.swift in Sources */,
AA6FFB4624DC3B5A0028F4D0 /* WebView.swift in Sources */,
B693955026F04BEB0015B914 /* ShadowView.swift in Sources */,
AA3D531D27A2F58F00074EC1 /* FeedbackSender.swift in Sources */,
B6CF78DE267B099C00CD4F13 /* WKNavigationActionExtension.swift in Sources */,
AA7412B224D0B3AC00D22FE0 /* TabBarViewItem.swift in Sources */,
856C98D52570116900A22F1F /* NSWindow+Toast.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,6 @@
"revision": "6b2aa2748a7881eebb9f84fb10c01293e15b52ca",
"version": "0.5.0"
}
},
{
"package": "TrackerRadarKit",
"repositoryURL": "https://github.com/duckduckgo/TrackerRadarKit.git",
"state": {
"branch": null,
"revision": "5f4caf35b8418700a48c64c7c61eb43308c8dacc",
"version": "1.0.3"
}
}
]
},
Expand Down
12 changes: 12 additions & 0 deletions DuckDuckGo/Assets.xcassets/Images/ThankYou.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "ThankYou.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.
4 changes: 4 additions & 0 deletions DuckDuckGo/Common/Localizables/UserText.swift
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,10 @@ struct UserText {
static let isDefaultBrowser = NSLocalizedString("preferences.default-browser.active", value: "DuckDuckGo is your default browser", comment: "Indicate that the browser is the default")
static let isNotDefaultBrowser = NSLocalizedString("preferences.default-browser.inactive", value: "DuckDuckGo is not your default browser.", comment: "Indicate that the browser is not the default")

static let feedbackBugDescription = NSLocalizedString("feedback.bug.description", value: "Please describe the problem in as much detail as possible:", comment: "Label in the feedback form")
static let feedbackFeatureRequestDescription = NSLocalizedString("feedback.feature.request.description", value: "What feature would you like to see?", comment: "Label in the feedback form")
static let feedbackOtherDescription = NSLocalizedString("feedback.other.description", value: "Please give us your feedback:", comment: "Label in the feedback form")

static func versionLabel(version: String, build: String) -> String {
let localized = NSLocalizedString("version",
value: "Version %@ (%@)",
Expand Down
35 changes: 35 additions & 0 deletions DuckDuckGo/Feedback and Breakage/Model/Feedback.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// Feedback.swift
//
// Copyright © 2022 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
import QuartzCore

struct Feedback {

enum Category {
case bug
case featureRequest
case other
}

let category: Category
let comment: String
let appVersion: String
let osVersion: String

}
55 changes: 55 additions & 0 deletions DuckDuckGo/Feedback and Breakage/Model/FeedbackSender.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//
// FeedbackSender.swift
//
// Copyright © 2022 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
import os.log

final class FeedbackSender {

static let feedbackURL = URL(string: "https://duckduckgo.com/feedback.js")!

func sendFeedback(_ feedback: Feedback) {
let parameters = [
"type": "app-feedback",
"comment": feedback.comment,
"category": feedback.category.asanaId,
"osversion": feedback.osVersion,
"appversion": feedback.appVersion
]

APIRequest.request(url: Self.feedbackURL, method: .post, parameters: parameters) { _, error in
if let error = error {
os_log("FeedbackSender: Failed to submit feedback %s", type: .error, error.localizedDescription)
Pixel.fire(.debug(event: .feedbackReportingFailed, error: error))
}
}
}

}

fileprivate extension Feedback.Category {

var asanaId: String {
switch self {
case .bug: return "1199184518165816"
case .featureRequest: return "1199184518165815"
case .other: return "1200574389728916"
}
}

}
41 changes: 41 additions & 0 deletions DuckDuckGo/Feedback and Breakage/Model/WebsiteBreakage.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// WebsiteBreakage.swift
//
// Copyright © 2022 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

struct WebsiteBreakage {

enum Category: String, CaseIterable {
case cantSignIn = "login"
case contentIsMissing = "content"
case linksDontWork = "links"
case browserIsIncompatible = "unsupported"
case theSiteAskedToDisable = "paywall"
case videoOrImagesDidntLoad = "images"
case somethingElse = "other"
}

let category: Category?
let siteUrlString: String
let osVersion: String
let upgradedHttps: Bool
let tdsETag: String?
let blockedTrackerDomains: [String]
let installedSurrogates: [String]

}
36 changes: 36 additions & 0 deletions DuckDuckGo/Feedback and Breakage/Model/WebsiteBreakageSender.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//
// WebsiteBreakageSender.swift
//
// Copyright © 2022 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

final class WebsiteBreakageSender {

func sendWebsiteBreakage(_ websiteBreakage: WebsiteBreakage) {
let parameters: [String: String] = ["category": websiteBreakage.category?.rawValue ?? "",
"siteUrl": websiteBreakage.siteUrlString,
"upgradedHttps": websiteBreakage.upgradedHttps ? "true" : "false",
"tds": websiteBreakage.tdsETag?.trimmingCharacters(in: CharacterSet(charactersIn: "\"")) ?? "",
"blockedTrackers": websiteBreakage.blockedTrackerDomains.joined(separator: ","),
"surrogates": websiteBreakage.installedSurrogates.joined(separator: ","),
"os": websiteBreakage.osVersion,
"manufacturer": "Apple"]

Pixel.fire(.brokenSiteReport, withAdditionalParameters: parameters)
}

}
Loading

0 comments on commit f2ce3e9

Please sign in to comment.