Skip to content

Commit

Permalink
Prevent Dismissal of Autofill UI When Window Loses Focus (#2393)
Browse files Browse the repository at this point in the history
Task/Issue URL: https://app.asana.com/0/276630244458377/1204870766898373/f

Description:
Currently, if we open the Autofill dialog and then tap out of the browser, the autofill window immediately dismisses. This is not a great UX. This change ensures that the Autofill dialog remains open until the user explicitly decides to close it.
Note that the user can close the autofill dialog by selecting anywhere inside the DDG browser, but outside the Autofill dialog.
  • Loading branch information
aataraxiaa authored Mar 14, 2024
1 parent 7830b58 commit 675bbb7
Show file tree
Hide file tree
Showing 17 changed files with 555 additions and 107 deletions.
44 changes: 44 additions & 0 deletions DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -2581,6 +2581,9 @@
B31055C427A1BA1D001AC618 /* AutoconsentUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = B31055BC27A1BA1D001AC618 /* AutoconsentUserScript.swift */; };
B31055C627A1BA1D001AC618 /* userscript.js in Resources */ = {isa = PBXBuildFile; fileRef = B31055BE27A1BA1D001AC618 /* userscript.js */; };
B31055CB27A1BA1D001AC618 /* autoconsent-bundle.js in Resources */ = {isa = PBXBuildFile; fileRef = B31055C327A1BA1D001AC618 /* autoconsent-bundle.js */; };
B60293E62BA19ECD0033186B /* NetPPopoverManagerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B60293E52BA19ECD0033186B /* NetPPopoverManagerMock.swift */; };
B60293E72BA19ECD0033186B /* NetPPopoverManagerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B60293E52BA19ECD0033186B /* NetPPopoverManagerMock.swift */; };
B60293E82BA19ECD0033186B /* NetPPopoverManagerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B60293E52BA19ECD0033186B /* NetPPopoverManagerMock.swift */; };
B602E7CF2A93A5FF00F12201 /* WKBackForwardListExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B602E7CE2A93A5FF00F12201 /* WKBackForwardListExtension.swift */; };
B602E7D02A93A5FF00F12201 /* WKBackForwardListExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B602E7CE2A93A5FF00F12201 /* WKBackForwardListExtension.swift */; };
B602E8162A1E2570006D261F /* URL+NetworkProtection.swift in Sources */ = {isa = PBXBuildFile; fileRef = B602E8152A1E2570006D261F /* URL+NetworkProtection.swift */; };
Expand Down Expand Up @@ -3075,6 +3078,13 @@
C168B9AC2B31DC7E001AFAD9 /* AutofillNeverPromptWebsitesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C168B9AB2B31DC7E001AFAD9 /* AutofillNeverPromptWebsitesManager.swift */; };
C168B9AD2B31DC7F001AFAD9 /* AutofillNeverPromptWebsitesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C168B9AB2B31DC7E001AFAD9 /* AutofillNeverPromptWebsitesManager.swift */; };
C168B9AE2B31DC7F001AFAD9 /* AutofillNeverPromptWebsitesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C168B9AB2B31DC7E001AFAD9 /* AutofillNeverPromptWebsitesManager.swift */; };
C17CA7AD2B9B52E6008EC3C1 /* NavigationBarPopoversTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C17CA7AC2B9B52E6008EC3C1 /* NavigationBarPopoversTests.swift */; };
C17CA7AE2B9B52E6008EC3C1 /* NavigationBarPopoversTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C17CA7AC2B9B52E6008EC3C1 /* NavigationBarPopoversTests.swift */; };
C17CA7B22B9B5317008EC3C1 /* MockAutofillPopoverPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C17CA7B12B9B5317008EC3C1 /* MockAutofillPopoverPresenter.swift */; };
C17CA7B32B9B5317008EC3C1 /* MockAutofillPopoverPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C17CA7B12B9B5317008EC3C1 /* MockAutofillPopoverPresenter.swift */; };
C1DAF3B52B9A44860059244F /* AutofillPopoverPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1DAF3B42B9A44860059244F /* AutofillPopoverPresenter.swift */; };
C1DAF3B62B9A44860059244F /* AutofillPopoverPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1DAF3B42B9A44860059244F /* AutofillPopoverPresenter.swift */; };
C1DAF3B72B9A44860059244F /* AutofillPopoverPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1DAF3B42B9A44860059244F /* AutofillPopoverPresenter.swift */; };
C1E961EB2B879E79001760E1 /* MockAutofillActionPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1E961E72B879E4D001760E1 /* MockAutofillActionPresenter.swift */; };
C1E961ED2B879ED9001760E1 /* MockAutofillActionExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1E961EC2B879ED9001760E1 /* MockAutofillActionExecutor.swift */; };
C1E961EF2B87AA29001760E1 /* AutofillActionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1E961EE2B87AA29001760E1 /* AutofillActionBuilder.swift */; };
Expand Down Expand Up @@ -4161,6 +4171,7 @@
B31055BC27A1BA1D001AC618 /* AutoconsentUserScript.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutoconsentUserScript.swift; sourceTree = "<group>"; };
B31055BE27A1BA1D001AC618 /* userscript.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = userscript.js; sourceTree = "<group>"; };
B31055C327A1BA1D001AC618 /* autoconsent-bundle.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "autoconsent-bundle.js"; sourceTree = "<group>"; };
B60293E52BA19ECD0033186B /* NetPPopoverManagerMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetPPopoverManagerMock.swift; sourceTree = "<group>"; };
B602E7CE2A93A5FF00F12201 /* WKBackForwardListExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WKBackForwardListExtension.swift; sourceTree = "<group>"; };
B602E8152A1E2570006D261F /* URL+NetworkProtection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "URL+NetworkProtection.swift"; sourceTree = "<group>"; };
B602E81C2A1E25B0006D261F /* NEOnDemandRuleExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NEOnDemandRuleExtension.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -4460,6 +4471,9 @@
C13909F32B85FD79001626ED /* AutofillDeleteAllPasswordsExecutorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillDeleteAllPasswordsExecutorTests.swift; sourceTree = "<group>"; };
C13909FA2B861039001626ED /* AutofillActionPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillActionPresenter.swift; sourceTree = "<group>"; };
C168B9AB2B31DC7E001AFAD9 /* AutofillNeverPromptWebsitesManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutofillNeverPromptWebsitesManager.swift; sourceTree = "<group>"; };
C17CA7AC2B9B52E6008EC3C1 /* NavigationBarPopoversTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBarPopoversTests.swift; sourceTree = "<group>"; };
C17CA7B12B9B5317008EC3C1 /* MockAutofillPopoverPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockAutofillPopoverPresenter.swift; sourceTree = "<group>"; };
C1DAF3B42B9A44860059244F /* AutofillPopoverPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillPopoverPresenter.swift; sourceTree = "<group>"; };
C1E961E72B879E4D001760E1 /* MockAutofillActionPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockAutofillActionPresenter.swift; sourceTree = "<group>"; };
C1E961EC2B879ED9001760E1 /* MockAutofillActionExecutor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockAutofillActionExecutor.swift; sourceTree = "<group>"; };
C1E961EE2B87AA29001760E1 /* AutofillActionBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillActionBuilder.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -6539,6 +6553,7 @@
4B8A4DFE27C83B29005F40E8 /* SaveIdentityViewController.swift */,
4BE4005227CF3DC3007D3161 /* SavePaymentMethodPopover.swift */,
4BE4005427CF3F19007D3161 /* SavePaymentMethodViewController.swift */,
C1DAF3B42B9A44860059244F /* AutofillPopoverPresenter.swift */,
);
path = View;
sourceTree = "<group>";
Expand Down Expand Up @@ -7220,6 +7235,7 @@
AAA0CC32252F181A0079BC96 /* NavigationButtonMenuDelegate.swift */,
85012B0129133F9F003D0DCC /* NavigationBarPopovers.swift */,
AA68C3D22490ED62001B8783 /* NavigationBarViewController.swift */,
B60293E52BA19ECD0033186B /* NetPPopoverManagerMock.swift */,
D64A5FF72AEA5C2B00B6D6E7 /* HomeButtonMenuFactory.swift */,
);
path = View;
Expand Down Expand Up @@ -7292,9 +7308,11 @@
AA91F83627076ED100771A0D /* NavigationBar */ = {
isa = PBXGroup;
children = (
C17CA7B02B9B52FF008EC3C1 /* Mocks */,
AA91F83727076EEE00771A0D /* ViewModel */,
4BF6961F28BEEE8B00D402D4 /* LocalPinningManagerTests.swift */,
56B234BE2A84EFD200F2A1CC /* NavigationBarUrlExtensionsTests.swift */,
C17CA7AF2B9B52EB008EC3C1 /* View */,
);
path = NavigationBar;
sourceTree = "<group>";
Expand Down Expand Up @@ -8300,6 +8318,22 @@
path = Tests;
sourceTree = "<group>";
};
C17CA7AF2B9B52EB008EC3C1 /* View */ = {
isa = PBXGroup;
children = (
C17CA7AC2B9B52E6008EC3C1 /* NavigationBarPopoversTests.swift */,
);
path = View;
sourceTree = "<group>";
};
C17CA7B02B9B52FF008EC3C1 /* Mocks */ = {
isa = PBXGroup;
children = (
C17CA7B12B9B5317008EC3C1 /* MockAutofillPopoverPresenter.swift */,
);
path = Mocks;
sourceTree = "<group>";
};
C1E961E62B879E2A001760E1 /* Mocks */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -10252,6 +10286,7 @@
4B9DB0452A983B24000927DB /* WaitlistModalViewController.swift in Sources */,
B66CA41F2AD910B300447CF0 /* DataImportView.swift in Sources */,
3706FC0C293F65D500E42796 /* NSAttributedStringExtension.swift in Sources */,
C1DAF3B62B9A44860059244F /* AutofillPopoverPresenter.swift in Sources */,
3706FC0D293F65D500E42796 /* AnimationView.swift in Sources */,
3706FC0E293F65D500E42796 /* NSRectExtension.swift in Sources */,
3706FC0F293F65D500E42796 /* YoutubeOverlayUserScript.swift in Sources */,
Expand Down Expand Up @@ -10338,6 +10373,7 @@
3706FC51293F65D500E42796 /* RecentlyVisitedView.swift in Sources */,
B645D8F729FA95440024461F /* WKProcessPoolExtension.swift in Sources */,
3706FC52293F65D500E42796 /* MouseOverAnimationButton.swift in Sources */,
B60293E72BA19ECD0033186B /* NetPPopoverManagerMock.swift in Sources */,
3706FC53293F65D500E42796 /* TabBarScrollView.swift in Sources */,
3706FC54293F65D500E42796 /* BookmarkListTreeControllerDataSource.swift in Sources */,
3706FC55293F65D500E42796 /* AddressBarViewController.swift in Sources */,
Expand Down Expand Up @@ -10541,6 +10577,7 @@
B6619EF72B10DFF700CD9186 /* InstructionsFormatParserTests.swift in Sources */,
3706FE21293F661700E42796 /* DownloadsPreferencesTests.swift in Sources */,
3706FE22293F661700E42796 /* FireproofDomainsTests.swift in Sources */,
C17CA7AE2B9B52E6008EC3C1 /* NavigationBarPopoversTests.swift in Sources */,
3706FE23293F661700E42796 /* SuggestionLoadingMock.swift in Sources */,
3706FE24293F661700E42796 /* PasteboardFolderTests.swift in Sources */,
B603971229B9D67E00902A34 /* PublishersExtensions.swift in Sources */,
Expand Down Expand Up @@ -10578,6 +10615,7 @@
3706FE3C293F661700E42796 /* FireproofDomainsStoreMock.swift in Sources */,
3706FE3D293F661700E42796 /* DataEncryptionTests.swift in Sources */,
3706FE3E293F661700E42796 /* ClickToLoadModelTests.swift in Sources */,
C17CA7B32B9B5317008EC3C1 /* MockAutofillPopoverPresenter.swift in Sources */,
3706FE3F293F661700E42796 /* FileStoreMock.swift in Sources */,
B6619F042B17123200CD9186 /* DataImportViewModelTests.swift in Sources */,
1D8C2FE62B70F4C4005E4BBD /* TabSnapshotExtensionTests.swift in Sources */,
Expand Down Expand Up @@ -11169,6 +11207,7 @@
4B957A3E2AC7AE700062CA31 /* EnableWaitlistFeatureView.swift in Sources */,
4B957A3F2AC7AE700062CA31 /* GrammarFeaturesManager.swift in Sources */,
4B957A402AC7AE700062CA31 /* WaitlistModalViewController.swift in Sources */,
B60293E82BA19ECD0033186B /* NetPPopoverManagerMock.swift in Sources */,
B6BCC53E2AFD15DF002C5499 /* DataImportProfilePicker.swift in Sources */,
4B957A412AC7AE700062CA31 /* WKMenuItemIdentifier.swift in Sources */,
4B957A422AC7AE700062CA31 /* SafariFaviconsReader.swift in Sources */,
Expand Down Expand Up @@ -11523,6 +11562,7 @@
4B957B7B2AC7AE700062CA31 /* DeviceAuthenticator.swift in Sources */,
4B957B7C2AC7AE700062CA31 /* NetworkProtectionWaitlistFeatureFlagOverridesMenu.swift in Sources */,
4B957B7D2AC7AE700062CA31 /* TabBarCollectionView.swift in Sources */,
C1DAF3B72B9A44860059244F /* AutofillPopoverPresenter.swift in Sources */,
4B957B7E2AC7AE700062CA31 /* NetworkProtection+ConvenienceInitializers.swift in Sources */,
7BA7CC502AD11F6F0042E5CE /* NetworkProtectionIPCTunnelController.swift in Sources */,
4B957B7F2AC7AE700062CA31 /* NavigationActionExtension.swift in Sources */,
Expand Down Expand Up @@ -11955,6 +11995,7 @@
4B9DB0442A983B24000927DB /* WaitlistModalViewController.swift in Sources */,
B6DA06E8291401D700225DE2 /* WKMenuItemIdentifier.swift in Sources */,
4B0AACAE28BC6FD0001038AC /* SafariFaviconsReader.swift in Sources */,
B60293E62BA19ECD0033186B /* NetPPopoverManagerMock.swift in Sources */,
B6B3E0E12657EA7A0040E0A2 /* NSScreenExtension.swift in Sources */,
B65E6BA026D9F10600095F96 /* NSBezierPathExtension.swift in Sources */,
4B4D60E02A0C875F00BCD287 /* Bundle+VPN.swift in Sources */,
Expand Down Expand Up @@ -12309,6 +12350,7 @@
4B9292AF26670F5300AD2C21 /* NSOutlineViewExtensions.swift in Sources */,
AA585D82248FD31100E9A3E2 /* AppDelegate.swift in Sources */,
7B1E81A027C8874900FF0E60 /* ContentOverlayViewController.swift in Sources */,
C1DAF3B52B9A44860059244F /* AutofillPopoverPresenter.swift in Sources */,
B687B7CA2947A029001DEA6F /* ContentBlockingTabExtension.swift in Sources */,
85B7184C27677C6500B4277F /* OnboardingViewController.swift in Sources */,
4B379C1E27BDB7FF008A968E /* DeviceAuthenticator.swift in Sources */,
Expand Down Expand Up @@ -12431,6 +12473,7 @@
569277C429DEE09D00B633EF /* ContinueSetUpModelTests.swift in Sources */,
85F1B0C925EF9759004792B6 /* URLEventHandlerTests.swift in Sources */,
4B9292BD2667103100AD2C21 /* BookmarkOutlineViewDataSourceTests.swift in Sources */,
C17CA7B22B9B5317008EC3C1 /* MockAutofillPopoverPresenter.swift in Sources */,
4BF6961D28BE911100D402D4 /* RecentlyVisitedSiteModelTests.swift in Sources */,
B6619F062B17138D00CD9186 /* DataImportSourceViewModelTests.swift in Sources */,
4BBF0917282DD6EF00EE1418 /* TemporaryFileHandlerTests.swift in Sources */,
Expand Down Expand Up @@ -12642,6 +12685,7 @@
B6C2C9EF276081AB005B7F0A /* DeallocationTests.swift in Sources */,
B63ED0D826AE729600A9DAD1 /* PermissionModelTests.swift in Sources */,
B69B504B2726CA2900758A2B /* MockStatisticsStore.swift in Sources */,
C17CA7AD2B9B52E6008EC3C1 /* NavigationBarPopoversTests.swift in Sources */,
37CD54BD27F2ECAE00F1F7B9 /* AutofillPreferencesModelTests.swift in Sources */,
C1E961ED2B879ED9001760E1 /* MockAutofillActionExecutor.swift in Sources */,
37D23789288009CF00BCE03B /* TabCollectionViewModelTests+PinnedTabs.swift in Sources */,
Expand Down
48 changes: 44 additions & 4 deletions DuckDuckGo/MainWindow/MainViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import Common

#if NETWORK_PROTECTION
import NetworkProtection
import NetworkProtectionIPC
#endif

final class MainViewController: NSViewController {
Expand Down Expand Up @@ -56,14 +57,53 @@ final class MainViewController: NSViewController {
fatalError("MainViewController: Bad initializer")
}

init(tabCollectionViewModel: TabCollectionViewModel? = nil,
bookmarkManager: BookmarkManager = LocalBookmarkManager.shared) {
init(tabCollectionViewModel: TabCollectionViewModel? = nil, bookmarkManager: BookmarkManager = LocalBookmarkManager.shared, autofillPopoverPresenter: AutofillPopoverPresenter) {
let tabCollectionViewModel = tabCollectionViewModel ?? TabCollectionViewModel()
self.tabCollectionViewModel = tabCollectionViewModel
self.isBurner = tabCollectionViewModel.isBurner

tabBarViewController = TabBarViewController.create(tabCollectionViewModel: tabCollectionViewModel)
navigationBarViewController = NavigationBarViewController.create(tabCollectionViewModel: tabCollectionViewModel, isBurner: isBurner)

#if NETWORK_PROTECTION
let networkProtectionPopoverManager: NetPPopoverManager = {
#if DEBUG
guard case .normal = NSApp.runType else {
return NetPPopoverManagerMock()
}
#endif
let vpnBundleID = Bundle.main.vpnMenuAgentBundleId
let ipcClient = TunnelControllerIPCClient(machServiceName: vpnBundleID)
ipcClient.register()

return NetworkProtectionNavBarPopoverManager(ipcClient: ipcClient)
}()
let networkProtectionStatusReporter: NetworkProtectionStatusReporter = {
var connectivityIssuesObserver: ConnectivityIssueObserver!
var controllerErrorMessageObserver: ControllerErrorMesssageObserver!
#if DEBUG
if ![.normal, .integrationTests].contains(NSApp.runType) {
connectivityIssuesObserver = ConnectivityIssueObserverMock()
controllerErrorMessageObserver = ControllerErrorMesssageObserverMock()
}
#endif
connectivityIssuesObserver = connectivityIssuesObserver ?? DisabledConnectivityIssueObserver()
controllerErrorMessageObserver = controllerErrorMessageObserver ?? ControllerErrorMesssageObserverThroughDistributedNotifications()

let ipcClient = networkProtectionPopoverManager.ipcClient
return DefaultNetworkProtectionStatusReporter(
statusObserver: ipcClient.ipcStatusObserver,
serverInfoObserver: ipcClient.ipcServerInfoObserver,
connectionErrorObserver: ipcClient.ipcConnectionErrorObserver,
connectivityIssuesObserver: connectivityIssuesObserver,
controllerErrorMessageObserver: controllerErrorMessageObserver
)
}()

navigationBarViewController = NavigationBarViewController.create(tabCollectionViewModel: tabCollectionViewModel, isBurner: isBurner, networkProtectionPopoverManager: networkProtectionPopoverManager, networkProtectionStatusReporter: networkProtectionStatusReporter, autofillPopoverPresenter: autofillPopoverPresenter)
#else
navigationBarViewController = NavigationBarViewController.create(tabCollectionViewModel: tabCollectionViewModel, isBurner: isBurner, autofillPopoverPresenter: AutofillPopoverPresenter)
#endif

browserTabViewController = BrowserTabViewController(tabCollectionViewModel: tabCollectionViewModel, bookmarkManager: bookmarkManager)
findInPageViewController = FindInPageViewController.create()
fireViewController = FireViewController.create(tabCollectionViewModel: tabCollectionViewModel)
Expand Down Expand Up @@ -560,7 +600,7 @@ extension MainViewController {
]))
bkman.loadBookmarks()

let vc = MainViewController(bookmarkManager: bkman)
let vc = MainViewController(bookmarkManager: bkman, autofillPopoverPresenter: DefaultAutofillPopoverPresenter())
var c: AnyCancellable!
c = vc.publisher(for: \.view.window).sink { window in
window?.titlebarAppearsTransparent = true
Expand Down
6 changes: 4 additions & 2 deletions DuckDuckGo/Menus/MainMenu.swift
Original file line number Diff line number Diff line change
Expand Up @@ -609,8 +609,10 @@ import SubscriptionUI
#endif

#if NETWORK_PROTECTION
NSMenuItem(title: "VPN")
.submenu(NetworkProtectionDebugMenu())
if case .normal = NSApp.runType {
NSMenuItem(title: "VPN")
.submenu(NetworkProtectionDebugMenu())
}
#endif

NSMenuItem(title: "Trigger Fatal Error", action: #selector(MainViewController.triggerFatalError))
Expand Down
Loading

0 comments on commit 675bbb7

Please sign in to comment.