Skip to content

[macOS] Add quick feedback popup with toolbar button, screenshot, and diagnostics for internal users#4467

Open
samhouts wants to merge 14 commits intomainfrom
shouts/quickfeedback
Open

[macOS] Add quick feedback popup with toolbar button, screenshot, and diagnostics for internal users#4467
samhouts wants to merge 14 commits intomainfrom
shouts/quickfeedback

Conversation

@samhouts
Copy link
Copy Markdown
Collaborator

@samhouts samhouts commented Apr 16, 2026

Task/Issue URL: https://app.asana.com/1/137249556945/project/1211850802118632/task/1213914821996601
Tech Design URL: https://app.asana.com/1/137249556945/project/1211850802118632/task/1213878196777775?focus=true
CC:

Description

Adds a quick feedback popup for internal users on macOS, matching the Windows implementation. Instead of opening the Asana form in a full browser tab (slow, overwhelming, undiscoverable), internal users now get:

  • Toolbar button — A warm yellow/gold feedback icon in the navigation bar (internal users only), loaded from DesignSystemImages.Color.Size16.feedback with a 6pt spacer for visual separation.
image
  • Popup form — A floating NSPanel hosting a WKWebView that loads the Asana form. JavaScript hides all irrelevant fields and injects a simplified "Report a Problem" UI with just a description textarea and Submit button. Auto-fills product area, platform, OS version, and app version.
image
  • Screenshot capture — Captures the browser window via CGWindowListCreateImage before opening the popup. Displayed as an opt-in preview (unchecked by default) with a privacy warning and full-screen enlarge overlay.
  • System diagnostics — Collects app version, macOS version, architecture, GPU (IOKit), browser memory (mach_task_basic_info), disk space, tab/window count, and session length. Displayed in a collapsible section, included by default.
  • Dax discovery tip — A timed NSPopover anchored to the toolbar button with rotating motivational messages. Pre-click: shows daily. Post-click: shows weekly. Auto-dismisses after 5 seconds.
image
  • WebView reuse — User-close hides the panel (keeps login warm); fire button force-closes it. An isolated persistent WKWebsiteDataStore (via forIdentifier: on macOS 14+) keeps Asana cookies separate from the browsing session and survives Fire button presses. Asana cookies are copied from the main browser's default store on first open for seamless login inheritance.
  • Entry point routing — All internal-user feedback paths (Help menu, "..." menu, Preferences > About) now open the popup instead of a tab.
  • Form loading polish — CSS injection at document start hides the form during load (no flash). Robust beforeunload suppression prevents navigation-blocking dialogs.

Testing Steps

  1. Build and launch the browser as an internal/Alpha build.
  2. Verify a warm yellow/gold feedback icon appears in the toolbar (left of other toolbar buttons) with a gap separating it from adjacent buttons.
  3. On first launch (clear feedbackTip.lastShown and feedbackTip.buttonClicked in UserDefaults), wait ~3 seconds — verify the Dax tip popover appears and auto-dismisses after ~5 seconds.
  4. Relaunch (or wait for DEBUG pre-click interval of 30s) — verify the Dax tip popover appears again after ~3 seconds.
  5. Click the feedback toolbar button — verify the Dax tip dismisses and the popup window opens.
  6. If SSO login is required, complete it. Verify the form loads without a visible flash and no beforeunload dialog appears.
  7. Verify the form shows only a description textarea and Submit button (other Asana fields hidden).
  8. Verify a "Sign out" link appears at the top of the popup.
  9. Verify a screenshot preview appears with an "Include screenshot" checkbox (unchecked by default).
  10. Check the screenshot checkbox — verify a yellow privacy warning appears.
  11. Click the screenshot thumbnail — verify a full-screen overlay with a close button opens; click "Close" to dismiss.
  12. Verify a "System Diagnostics" section is present showing: App Version, macOS, Architecture, GPU, Memory (with browser MB), Disk, Tabs/Windows, Session length.
  13. Submit the form — verify the popup auto-hides after ~2 seconds.
  14. Reopen the popup — verify you're still logged in (WebView reuse) and the form reloads fresh.
  15. Close the popup via the window close button (X) — verify the window hides (does not destroy). Reopen — verify still logged in.
  16. Click the Fire button — verify the popup closes. Reopen — verify still logged in (cookies survive in the isolated persistent data store).
  17. Resize the popup window — verify it allows resizing with a minimum size of ~450×500.
  18. Open Help menu > "Send Feedback" — verify the popup opens (not a tab).
  19. Open "..." menu > feedback option — verify the popup opens.
  20. Open Preferences > About > "Send Feedback" — verify the popup opens.

Impact and Risks

Impact: None — This is internal tooling for DuckDuckGo employees only. The feature is gated behind internalUserDecider.isInternalUser and has no effect on external users.

What could go wrong?

  • If the Asana form structure changes, the JS field hiding/injection could break — but this only affects internal users and degrades gracefully (the full Asana form would show instead).
  • If DesignResourcesKitIcons removes the feedback icon, the code won't compile (compile-time safe, no runtime failure).

Quality Considerations

  • Edge cases: SSO redirects during login don't trigger false script injection — JS is only injected when the navigation finishes on form.asana.com. Fire button during popup open correctly destroys the window controller. WebView reuse correctly re-navigates on reopen.
  • Performance: No startup impact. QuickFeedbackService is lazily created. All diagnostics (IOKit, mach_task_basic_info, disk query) run only on user-initiated popup open in sub-millisecond time. Screenshot capture is ~5-20ms.
  • Privacy: No PII collected. Diagnostics are machine-level (GPU model, memory sizes, disk capacity). Screenshot is opt-in (unchecked by default) with a privacy warning. Isolated WKWebsiteDataStore (via forIdentifier: on macOS 14+) keeps Asana cookies separate from the browsing session. Only asana.com cookies are synced from the main browser for login inheritance — Microsoft SSO cookies are excluded. Sign out clears all data in the isolated store. No pixels or analytics added.
  • Testing: Unit tests cover diagnostics collector (15 tests), tip controller timing logic (8 tests), and distribution label (4 tests). QuickFeedbackService is not unit-tested due to tight coupling with WKWebView/NSPanel — covered by manual test steps above.

Notes to Reviewer

  • This mirrors the Windows Quick Feedback feature. The tech design documents the full design rationale and cross-platform alignment.
  • The icon must use DesignSystemImages.Color.Size16.feedback (not NSImage(named:)) to load from the correct Swift package bundle, and isTemplate = false to render in color instead of monochrome.
  • The internal-feedback-autofiller.js file is significantly expanded — it now supports a quickMode flag that hides all Asana fields and injects the simplified form UI. The existing full-mode behavior (quickMode = false) is preserved for backward compatibility with the tab-based flow.

Internal references:

Definition of Done | Engineering Expectations | Tech Design Template


Note

Medium Risk
Introduces a new internal-user-only feedback flow built on WKWebView + custom JS injection and cookie/data-store handling; regressions could affect internal feedback submission or cookie isolation, but external users are largely unaffected.

Overview
Adds an internal-user Quick Feedback experience that opens the Asana feedback form in a floating popup (reused NSPanel + WKWebView) instead of a full browser tab, and routes existing internal feedback entry points (Help/menu actions and WindowControllersManager.showFeedbackModal) to this popup.

The popup auto-collects and optionally submits diagnostics (app/distribution label, OS, arch, GPU, memory, disk, tab/window counts, session length) and captures an opt-in screenshot of the current window; it uses an isolated persistent WKWebsiteDataStore on macOS 14+ (with Asana-cookie copying/sign-out clearing) and expands internal-feedback-autofiller.js to support “quick mode” UI simplification, field hiding, diagnostics inclusion, and screenshot attachment.

Adds a toolbar feedback button (internal users only) with a Dax tip popover cadence, plus new unit tests covering distribution labeling, diagnostics collection, and tip scheduling logic.

Reviewed by Cursor Bugbot for commit 8615c34. Bugbot is set up for automated code reviews on this repo. Configure here.

samhouts added 4 commits April 9, 2026 20:02
Introduce a Quick Feedback flow for internal users: adds QuickFeedbackService, QuickFeedbackWindowController, QuickFeedbackDiagnosticsCollector, and QuickFeedbackTipController to present a lightweight feedback popup with diagnostics and optional screenshot attachment. Integrates the feature into AppDelegate, main menus, navigation bar (button + tip), and WindowControllersManager (tab/window counts provider). Updates internal-feedback-autofiller.js to support quick mode, diagnostics injection, and screenshot handling. Adds AppVersionModel.distributionLabel and unit tests for distribution label and diagnostics collector behavior.
Add QuickFeedback module to the Xcode project and wire up UI and tests. Key changes:

- Update project.pbxproj: add QuickFeedback group, source files and tests to targets and build phases.
- New/updated QuickFeedback files: QuickFeedbackService, QuickFeedbackDiagnosticsCollector, window/tip controllers and tests.
- QuickFeedbackService:
  - Marked lazy property usage @mainactor in AppDelegate where service is created.
  - Import os.log and switch WKWebsiteDataStore to .default()
  - Remove old native screenshot-injection code and formLoaded state; instead pass a %SCREENSHOT_BASE64% token into the JS template and add a timeout to restore hidden sections.
  - Simplify navigation delegate logic and sign-out/cleanup flows.
- Resources JS (internal-feedback-autofiller.js): accept %SCREENSHOT_BASE64%, inject a screenshot section with checkbox/warning/enlarge overlay, add email field padding, and other UI tweaks.
- Diagnostics collector: use appropriate I/O Kit port for pre-macOS-12 compatibility when matching IOPCIDevice.
- NavigationBarViewController: add QuickFeedback button handling for internal users, subscribe to internalUser publisher to add/remove the button, swap icon name, and manage tip controller and spacer views.
- AppVersionModel: import AppKit to fix usage context.

These changes enable internal quick feedback with optional screenshot attachment, add tests and project references, and ensure compatibility across macOS versions.
Import DesignResourcesKitIcons and replace the previous NSImage/name lookup and SF Symbol fallback with DesignSystemImages.Color.Size16.feedback. Ensure the icon preserves its color by setting isTemplate = false and remove the previous contentTintColor fallback logic.
@samhouts samhouts changed the title Shouts/quickfeedback Add quick feedback popup with toolbar button, screenshot, and diagnostics for internal users Apr 16, 2026
Use an isolated WKWebsiteDataStore for the internal feedback webview on macOS 14+ to preserve Asana sessions, and fall back to the default store on older macOS. Copy Asana cookies from the default store into the isolated store at startup. Add a script message handler and postMessage from the autofill JS so the app can dismiss the feedback popup after form submission. Restrict webview navigation to a small set of allowed domains. Simplify sign-out to clear all website data. Adjust Quick Feedback tip timings, swap the Dax image resource name, and update unit tests to match the revised scheduling and behavior.
@samhouts samhouts marked this pull request as ready for review April 17, 2026 01:38
Copilot AI review requested due to automatic review settings April 17, 2026 01:38
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 17, 2026

Warnings
⚠️ PR has 1583 lines of added code (excluding Xcode projects and assets). Consider splitting into smaller PRs if possible.

Generated by 🚫 dangerJS against 8615c34

Comment thread macOS/DuckDuckGo/Feedback/QuickFeedback/QuickFeedbackDiagnosticsCollector.swift Outdated
Comment thread macOS/DuckDuckGo/Feedback/Resources/internal-feedback-autofiller.js
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an internal-user-only “Quick Feedback” popup flow on macOS (toolbar entry point + in-panel Asana form customization), aligning with the Windows implementation and routing existing internal feedback entry points to the popup instead of opening a tab.

Changes:

  • Add QuickFeedbackService + NSPanel window controller + Dax tip controller, and wire them into AppDelegate and internal feedback entry points.
  • Add diagnostics + screenshot placeholders to the Asana autofill JS and expand the JS to support a “quick mode” simplified UI.
  • Add unit tests for the tip timing logic, diagnostics collector output, and distribution label formatting.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
macOS/UnitTests/Feedback/QuickFeedback/QuickFeedbackTipControllerTests.swift Adds unit coverage for tip scheduling/cooldown and button-click persistence.
macOS/UnitTests/Feedback/QuickFeedback/QuickFeedbackDiagnosticsCollectorTests.swift Adds unit coverage for diagnostics output structure, ordering, and optional tab/window counts.
macOS/UnitTests/Feedback/QuickFeedback/AppVersionModelDistributionLabelTests.swift Adds tests for new distribution label behavior (DMG/App Store + Alpha suffix).
macOS/DuckDuckGo/Windows/View/WindowControllersManager.swift Routes internal-user feedback modal entry to the popup; provides tab/window count via TabCountProviding.
macOS/DuckDuckGo/Tab/TabExtensions/InternalFeedbackFormTabExtension.swift Updates JS placeholder replacements (app distribution label + quick-mode-related placeholders).
macOS/DuckDuckGo/NavigationBar/View/NavigationBarViewController.swift Adds internal-only toolbar feedback button + tip scheduling + click routing to the popup.
macOS/DuckDuckGo/Menus/MainMenuActions.swift Routes internal-user feedback menu actions to the popup instead of opening a tab.
macOS/DuckDuckGo/Feedback/Resources/internal-feedback-autofiller.js Adds quick-mode behavior (hide fields, inject diagnostics/screenshot UI, submit hook).
macOS/DuckDuckGo/Feedback/QuickFeedback/QuickFeedbackWindowController.swift Introduces the floating panel hosting a web view and sign-out bar.
macOS/DuckDuckGo/Feedback/QuickFeedback/QuickFeedbackTipController.swift Introduces Dax discovery tip logic with cooldowns and auto-dismiss.
macOS/DuckDuckGo/Feedback/QuickFeedback/QuickFeedbackService.swift Implements popup lifecycle, cookie inheritance, diagnostics/screenshot injection, and submission auto-hide.
macOS/DuckDuckGo/Feedback/QuickFeedback/QuickFeedbackDiagnosticsCollector.swift Implements diagnostics collection (app/os/arch/gpu/memory/disk/tabs/session).
macOS/DuckDuckGo/Application/AppVersionModel.swift Adds distributionLabel used by diagnostics and JS autofill.
macOS/DuckDuckGo/Application/AppDelegate.swift Lazily wires up quickFeedbackService with diagnostics + fire-button publisher.
macOS/DuckDuckGo-macOS.xcodeproj/project.pbxproj Registers new QuickFeedback sources and unit test files in the Xcode project.

Comment thread macOS/DuckDuckGo/Feedback/QuickFeedback/QuickFeedbackService.swift
Comment thread macOS/DuckDuckGo/Feedback/QuickFeedback/QuickFeedbackService.swift
Comment thread macOS/DuckDuckGo/Feedback/Resources/internal-feedback-autofiller.js
Injects a configurable launchDate into QuickFeedbackDiagnosticsCollector (defaults to Date()) and passes appLaunchDate from AppDelegate so diagnostics are deterministic/testable. Changes QuickFeedbackService.signOut to avoid wiping the entire default WKWebsiteDataStore: it now fetches data records and only removes records whose displayName contains 'asana' for the default store, while still clearing other non-default stores entirely. Updates internal feedback autofiller JS to always select 'Native Apps & Extensions' (removes the autoSelect parameter) and includes a minor whitespace cleanup in AppVersionModel.
Comment thread macOS/DuckDuckGo/Feedback/QuickFeedback/QuickFeedbackService.swift
Comment thread macOS/DuckDuckGo/Feedback/Resources/internal-feedback-autofiller.js Outdated
Change QuickFeedbackService.forceClosePopup to call window.orderOut(nil) instead of window.close() to hide the feedback window without triggering a full close lifecycle. Remove the unused setTextareaValue helper from internal-feedback-autofiller.js to eliminate the textarea autofill logic (no longer needed/causing issues), leaving the other input autofill helpers intact.
Remove a stray blank line in AppVersionModel.swift and delete an outdated doc comment above the VPN setup in NavigationBarViewController.swift. Pure formatting/cleanup changes with no functional impact.
Comment thread macOS/DuckDuckGo/Menus/MainMenuActions.swift
Switch openRequestANewFeature behavior for internal users to open quickFeedbackService.openFeedbackPopup(from: NSApp.mainWindow) instead of showing the internal feedback tab via WindowControllersManager. This displays a popup-based feedback UI for internal users.
Comment thread macOS/DuckDuckGo/Feedback/QuickFeedback/QuickFeedbackService.swift
Comment thread macOS/DuckDuckGo/Feedback/Resources/internal-feedback-autofiller.js
Remove the webkit message-based handshake used when the feedback form is submitted. Deleted the formSubmittedMessageName constant, the WKScriptMessageHandler conformance and its delayed hidePopup logic, and the registration of the script message handler in QuickFeedbackService. Also removed the corresponding window.webkit.messageHandlers.feedbackFormSubmitted.postMessage call from internal-feedback-autofiller.js. These changes remove the in-page postMessage and related native handling for form submissions.
@samhouts samhouts changed the title Add quick feedback popup with toolbar button, screenshot, and diagnostics for internal users [macOS] Add quick feedback popup with toolbar button, screenshot, and diagnostics for internal users Apr 17, 2026
Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit f3b966a. Configure here.

Add a stored NSLayoutConstraint (signOutBarHeightConstraint) for the sign-out bar height and use it when activating constraints. setSignOutVisible now updates the constraint's constant to 28 or 0 to show/hide the bar instead of using a fixed height constraint, allowing the height to be toggled dynamically.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants