diff --git a/Client/Frontend/Browser/BrowserViewController.swift b/Client/Frontend/Browser/BrowserViewController.swift index f5984e4d3..5cc201841 100644 --- a/Client/Frontend/Browser/BrowserViewController.swift +++ b/Client/Frontend/Browser/BrowserViewController.swift @@ -890,7 +890,7 @@ class BrowserViewController: UIViewController { } } - fileprivate func showSearchController() { + func showSearchController() { /* Cliqz: Replace Search Controller if searchController != nil { return @@ -915,15 +915,20 @@ class BrowserViewController: UIViewController { searchController!.didMove(toParentViewController: self) */ - if (searchController != nil && SettingsPrefs.shared.getCliqzSearchPref() == false) || - (cliqzSearchController != nil && SettingsPrefs.shared.getCliqzSearchPref() == true){ + let shouldShowCliqzSearch = SettingsPrefs.shared.getCliqzSearchPref() || self.shouldShowSearchOnboarding() + if (searchController != nil && !shouldShowCliqzSearch) || + (cliqzSearchController != nil && shouldShowCliqzSearch){ return } searchLoader = SearchLoader(profile: profile, urlBar: urlBar) searchLoader!.addListener(HistoryListener.shared) - if SettingsPrefs.shared.getCliqzSearchPref() { + if shouldShowCliqzSearch { + if self.shouldShowSearchOnboarding() { + LegacyTelemetryHelper.logOnboarding(action: "show", topic: "search") + } + homePanelController?.view?.isHidden = true cliqzSearchController = CliqzSearchViewController(profile: self.profile) @@ -1000,7 +1005,11 @@ class BrowserViewController: UIViewController { } } - fileprivate func hideSearchController() { + private func shouldShowSearchOnboarding() -> Bool { + return UserPreferences.instance.showSearchOnboarding + } + + func hideSearchController() { /*Cliqz: Hide cliqzSearch or firefoxSearch if let searchController = searchController { searchController.willMove(toParentViewController: nil) @@ -1681,15 +1690,19 @@ extension BrowserViewController: URLBarDelegate { searchController?.searchQuery = text searchLoader?.query = text */ - if !text.isEmpty { - cliqzSearchController?.searchQuery = text - searchController?.searchQuery = text - searchLoader?.query = text - } + self.updateSearchQuery(query: text) // End Cliqz } } + func updateSearchQuery(query: String) { + if !query.isEmpty { + cliqzSearchController?.searchQuery = query + searchController?.searchQuery = query + searchLoader?.query = query + } + } + func urlBar(_ urlBar: URLBarView, didSubmitText text: String) { guard let currentTab = tabManager.selectedTab else { return } if let fixupURL = URIFixup.getURL(text) { @@ -2348,7 +2361,13 @@ extension BrowserViewController: IntroViewControllerDelegate { #endif // End Cliqz - if force || profile.prefs.intForKey(PrefsKeys.IntroSeen) == nil { + /* Cliqz: determining the first launch. */ + let isFirstLaunch = profile.prefs.intForKey(PrefsKeys.IntroSeen) == nil + if isFirstLaunch { + UserPreferences.instance.showSearchOnboarding = false + } + + if force || isFirstLaunch { #if PAID let introViewController = LumenIntroViewController() #else diff --git a/Cliqz/Extensions/BrowserViewController/SearchViewDelegate.swift b/Cliqz/Extensions/BrowserViewController/SearchViewDelegate.swift index 7f64ea3a2..e7b863b69 100644 --- a/Cliqz/Extensions/BrowserViewController/SearchViewDelegate.swift +++ b/Cliqz/Extensions/BrowserViewController/SearchViewDelegate.swift @@ -23,4 +23,21 @@ extension BrowserViewController: SearchViewDelegate { func dismissKeyboard() { urlBar.hideKeyboard() } + + func closeSearchOnboarding() { + let query = cliqzSearchController?.searchQuery + hideSearchController() + showSearchController() + if let query = query { + updateSearchQuery(query: query) + } + } + + func makeLumenDefaultSearch() { + let lumenSearchEngine = self.profile.searchEngines.orderedEngines.filter { $0.shortName == LumenSearchEngineDisplayName }.first + if let engine = lumenSearchEngine { + self.profile.searchEngines.defaultEngine = engine + self.urlBar.updatePlaceHolders() + } + } } diff --git a/Cliqz/Privacy/AdAndTrackingProtection/UserPreferences.swift b/Cliqz/Privacy/AdAndTrackingProtection/UserPreferences.swift index e63652a8a..776b59f46 100644 --- a/Cliqz/Privacy/AdAndTrackingProtection/UserPreferences.swift +++ b/Cliqz/Privacy/AdAndTrackingProtection/UserPreferences.swift @@ -52,6 +52,7 @@ extension Notification.Name { let IsDeveloperModeOnKey = "IsDeveloperModeOnKey" let ShowPromoCodeKey = "ShowPromoCodeKey" + let ShowSearchOnboarding = "showSearchOnboarding" let ShowNonPrivateSearchWarningKey = "ShowNonPrivateSearchWarningKey" /// If `true`, send a `developer` flag in the telemetry data. @@ -86,6 +87,23 @@ extension Notification.Name { } } + var showSearchOnboarding: Bool { + get { + #if PAID + if let val = userDefaults().value(forKey: ShowSearchOnboarding) as? Bool { + return val + } + + return true + #else + return false + #endif + } + set { + userDefaults().set(newValue, forKey: ShowSearchOnboarding) + } + } + var shouldShowNonPrivateSearchWarningMessage: Bool { get { if let val = userDefaults().value(forKey: ShowNonPrivateSearchWarningKey) as? Bool { diff --git a/Cliqz/React Components/Engine.swift b/Cliqz/React Components/Engine.swift index adafc79da..53e5455e7 100644 --- a/Cliqz/React Components/Engine.swift +++ b/Cliqz/React Components/Engine.swift @@ -30,8 +30,8 @@ open class Engine { #else let jsCodeLocation = Bundle.main.url(forResource: "jsengine.bundle", withExtension: "js") #endif - - rootView = RCTRootView( bundleURL: jsCodeLocation, moduleName: "ExtensionApp", initialProperties: ["showSearchOnboarding": true], launchOptions: nil ) + + rootView = RCTRootView( bundleURL: jsCodeLocation, moduleName: "ExtensionApp", initialProperties: nil, launchOptions: nil ) bridge = rootView.bridge //ConnectManager.sharedInstance.refresh() } diff --git a/Cliqz/React Components/Modules/JSBridge.swift b/Cliqz/React Components/Modules/JSBridge.swift index 76defbe0a..92735e942 100644 --- a/Cliqz/React Components/Modules/JSBridge.swift +++ b/Cliqz/React Components/Modules/JSBridge.swift @@ -93,6 +93,14 @@ class JSBridge : RCTEventEmitter { } } } + + @objc(getConfig:reject:) + func getConfig(resolve: @escaping RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) { + if SettingsPrefs.shared.isLumenDefaultSearchEngine { + UserPreferences.instance.showSearchOnboarding = false + } + resolve(["onboarding": UserPreferences.instance.showSearchOnboarding]) + } /// Call an action over the JSBridge and execute a callback with the result. Invokation of the callback is /// not guaranteed, for example if the function is never registered. Unlike the synchronous version, this diff --git a/Cliqz/React Components/Modules/JSBridgeBridge.m b/Cliqz/React Components/Modules/JSBridgeBridge.m index 21b44d660..b9389a0fd 100644 --- a/Cliqz/React Components/Modules/JSBridgeBridge.m +++ b/Cliqz/React Components/Modules/JSBridgeBridge.m @@ -13,5 +13,6 @@ @interface RCT_EXTERN_MODULE(JSBridge, NSObject) RCT_EXTERN_METHOD(replyToAction:(nonnull NSInteger *)actionId result:(NSDictionary *)result) RCT_EXTERN_METHOD(registerAction:) RCT_EXTERN_METHOD(pushEvent:(nonnull NSString *)eventId data:(NSArray *)data) +RCT_EXTERN_METHOD(getConfig:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) @end diff --git a/Cliqz/React Components/Modules/Onboarding.swift b/Cliqz/React Components/Modules/Onboarding.swift index 6f4c0be88..ada1855ac 100644 --- a/Cliqz/React Components/Modules/Onboarding.swift +++ b/Cliqz/React Components/Modules/Onboarding.swift @@ -19,9 +19,13 @@ class Onboarding: RCTEventEmitter { func tryLumenSearch(accepted: Bool) { debugPrint("tryLumenSearch -- \(accepted)") if accepted { - // TODO: PK this is try now case. Change Lumen to default search engine. UI will be handled by extenstion + DispatchQueue.main.async { + NotificationCenter.default.post(name: MakeLumenDefaultSearchNotification, object: nil, userInfo: nil) + } } else { - // TODO: PK close cliqz search and open queary with default search engine + DispatchQueue.main.async { + NotificationCenter.default.post(name: CloseSearchOnboardingNotification, object: nil, userInfo: nil) + } } } } diff --git a/Cliqz/Search/CliqzSearchViewController.swift b/Cliqz/Search/CliqzSearchViewController.swift index c16b672d1..28fb455cc 100644 --- a/Cliqz/Search/CliqzSearchViewController.swift +++ b/Cliqz/Search/CliqzSearchViewController.swift @@ -28,6 +28,8 @@ protocol SearchViewDelegate: class { func didSelectURL(_ url: URL, searchQuery: String?) func autoCompeleteQuery(_ autoCompleteText: String) func dismissKeyboard() + func closeSearchOnboarding() + func makeLumenDefaultSearch() } let OpenURLIsSearchEngineKey = "OpenURLIsSearchEngineKey" @@ -37,6 +39,8 @@ let HideKeyboardSearchNotification = NSNotification.Name(rawValue: "mobile-searc let CallSearchNotification = NSNotification.Name(rawValue: "mobile-search:call") let MapSearchNotification = NSNotification.Name(rawValue: "mobile-search:map") let ShareLocationSearchNotification = NSNotification.Name(rawValue: "mobile-search:share-location") +let MakeLumenDefaultSearchNotification = NSNotification.Name(rawValue: "mobile-search:makeLumenDefaultSearchEngine") +let CloseSearchOnboardingNotification = NSNotification.Name(rawValue: "mobile-search:closeSearchOnboarding") let SearchEngineChangedNotification = Notification.Name(rawValue: "SearchEngineChangedNotification") @@ -120,6 +124,8 @@ class CliqzSearchViewController : UIViewController, KeyboardHelperDelegate, UIAl NotificationCenter.default.addObserver(self, selector: #selector(openMap), name: MapSearchNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(shareLocation), name: ShareLocationSearchNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(autocomplete(_:)), name: AutoCompleteNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(closeSearchOnboarding(_:)), name: CloseSearchOnboardingNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(makeLumenDefaultSearch(_:)), name: MakeLumenDefaultSearchNotification, object: nil) //TODO: Send notification when search engine is changed NotificationCenter.default.addObserver(self, selector: #selector(searchEngineChanged), name: SearchEngineChangedNotification, object: nil) @@ -286,6 +292,20 @@ extension CliqzSearchViewController { LocationManager.sharedInstance.shareLocation() } + @objc func closeSearchOnboarding(_ notification: Notification) { + LegacyTelemetryHelper.logOnboarding(action: "click", target: "cancel", topic: "search") + UserPreferences.instance.showSearchOnboarding = false + delegate?.closeSearchOnboarding() + } + + @objc func makeLumenDefaultSearch(_ notification: Notification) { + LegacyTelemetryHelper.logOnboarding(action: "click", target: "try", topic: "search") + UserPreferences.instance.showSearchOnboarding = false + if !SettingsPrefs.shared.isLumenDefaultSearchEngine { + delegate?.makeLumenDefaultSearch() + } + } + fileprivate func showLumenSearchLeavingWarning(url: URL) { LegacyTelemetryHelper.logLumenSearchWarning(action: "show") UserPreferences.instance.shouldShowNonPrivateSearchWarningMessage = false diff --git a/Cliqz/Telemetry/LegacyTelemetryHelper.swift b/Cliqz/Telemetry/LegacyTelemetryHelper.swift index f6a9dac0f..4f9f7b8ee 100644 --- a/Cliqz/Telemetry/LegacyTelemetryHelper.swift +++ b/Cliqz/Telemetry/LegacyTelemetryHelper.swift @@ -10,9 +10,11 @@ import UIKit class LegacyTelemetryHelper: NSObject { - class func logOnboarding(action: String, page: Int, target: String? = nil) { - var signal: [String : Any] = ["type": "onboarding", "action": action, "page": page, "version": 1] + class func logOnboarding(action: String, page: Int? = nil, target: String? = nil, topic: String? = nil) { + var signal: [String : Any] = ["type": "onboarding", "action": action, "version": 1] if let target = target { signal["target"] = target } + if let page = page { signal["page"] = page } + if let topic = topic { signal["topic"] = topic } sendSignal(signal) } diff --git a/js/lumen-onboarding.js b/js/lumen-onboarding.js index e7a780567..aeae3fddf 100644 --- a/js/lumen-onboarding.js +++ b/js/lumen-onboarding.js @@ -102,9 +102,9 @@ export default class Onboarding extends React.Component { this.onPress(false)}> - <> + {noThanksText} - + ); diff --git a/lumen.js b/lumen.js index af69f6f5f..5cd599e65 100644 --- a/lumen.js +++ b/lumen.js @@ -59,11 +59,12 @@ class MobileCards extends React.Component { constructor(props) { super(props); this.state = { - onboarding: props.showSearchOnboarding, + onboarding: false, results: { results: [], meta: {} }, + isReady: false, hasQuery: false, theme: 'lumen-light' } @@ -71,7 +72,16 @@ class MobileCards extends React.Component { this.cliqz = new Cliqz(); this.isDeveloper = prefs.get('developer', false); this.appStart = appStart || Promise.resolve(); + this.init(); + } + async init() { + await this.appStart; + const config = await nativeBridge.getConfig(); + this.setState({ + onboarding: config.onboarding, + isReady: true, + }); events.sub('search:results', this.updateResults); events.sub('mobile-browser:notify-preferences', this.updatePreferences); events.sub('mobile-browser:set-search-engine', this.setSearchEngine); @@ -138,6 +148,9 @@ class MobileCards extends React.Component { } render() { + if (!this.state.isReady) { + return null; + } const { results, suggestions, meta, query } = this.state.results; const appearance = this.state.theme; const layout = 'vertical';