diff --git a/Client.xcodeproj/project.pbxproj b/Client.xcodeproj/project.pbxproj index 38877fc07..89ec5c24d 100644 --- a/Client.xcodeproj/project.pbxproj +++ b/Client.xcodeproj/project.pbxproj @@ -212,6 +212,8 @@ 28F596A11ACA13CA0071DDCC /* InfoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28F596A01ACA13CA0071DDCC /* InfoTests.swift */; }; 28F657EA1ABFCA7A00A608BD /* LiveAccountTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FA4363B1ABB8448008031D1 /* LiveAccountTest.swift */; }; 28FDFF0C1C1F725800840F86 /* SeparatorTableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28FDFF0B1C1F725800840F86 /* SeparatorTableCell.swift */; }; + 2979623922DF695E00604955 /* Onboarding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2979623822DF695E00604955 /* Onboarding.swift */; }; + 2979624322DF697900604955 /* OnboardingBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 2979624222DF697900604955 /* OnboardingBridge.m */; }; 2C28F96C201B2D4C00ABA8A5 /* MailAppSettingsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C28F96B201B2D4C00ABA8A5 /* MailAppSettingsTests.swift */; }; 2C2A5EF41E68469500F02659 /* PrivateBrowsingTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C2A5EF31E68469500F02659 /* PrivateBrowsingTest.swift */; }; 2C2A91291FA2410D002E36BD /* HistoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C2A91281FA2410D002E36BD /* HistoryTests.swift */; }; @@ -1668,6 +1670,8 @@ 28ED02281B262E0A003948B2 /* IndependentRecordSynchronizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = IndependentRecordSynchronizer.swift; path = Synchronizers/IndependentRecordSynchronizer.swift; sourceTree = ""; }; 28F596A01ACA13CA0071DDCC /* InfoTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = InfoTests.swift; path = SyncTests/InfoTests.swift; sourceTree = ""; }; 28FDFF0B1C1F725800840F86 /* SeparatorTableCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SeparatorTableCell.swift; sourceTree = ""; }; + 2979623822DF695E00604955 /* Onboarding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Onboarding.swift; sourceTree = ""; }; + 2979624222DF697900604955 /* OnboardingBridge.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OnboardingBridge.m; sourceTree = ""; }; 2C28F96B201B2D4C00ABA8A5 /* MailAppSettingsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MailAppSettingsTests.swift; sourceTree = ""; }; 2C2A5EF31E68469500F02659 /* PrivateBrowsingTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrivateBrowsingTest.swift; sourceTree = ""; }; 2C2A91281FA2410D002E36BD /* HistoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryTests.swift; sourceTree = ""; }; @@ -3518,6 +3522,8 @@ children = ( 4F30F4F42051670C0049E4F6 /* AutoCompletion.swift */, 4F30F4F52051670C0049E4F6 /* AutoCompletionBridge.m */, + 2979623822DF695E00604955 /* Onboarding.swift */, + 2979624222DF697900604955 /* OnboardingBridge.m */, 4F30F4F62051670C0049E4F6 /* BrowserActions.swift */, 4F30F4F72051670C0049E4F6 /* BrowserActionsBridge.m */, 4F30F4F82051670C0049E4F6 /* Crypto.swift */, @@ -6935,6 +6941,7 @@ E68AEDB01B18F81A00133D99 /* SwipeAnimator.swift in Sources */, AFA1DE0C2088BE0500B27B8A /* BlockListFileManager.swift in Sources */, 1E23E61D22258D0B0004FAAF /* LegacyTelemetryHelper.swift in Sources */, + 2979624322DF697900604955 /* OnboardingBridge.m in Sources */, 4F30F50A2051670C0049E4F6 /* BrowserActions.swift in Sources */, 1E2C435C21F89E8100891FB2 /* LumenDarkTheme.swift in Sources */, 3BF56D271CDBBE1F00AC4D75 /* SimpleToast.swift in Sources */, @@ -7046,6 +7053,7 @@ 4FE43F5B221EE96D00045527 /* WelcomeView.swift in Sources */, 1E3CBC152057D83700898B05 /* CliqzAppSettingsTableViewController.swift in Sources */, D04CD718215EBD85004FF5B0 /* SettingsLoadingView.swift in Sources */, + 2979623922DF695E00604955 /* Onboarding.swift in Sources */, D04CD74C216CF86B004FF5B0 /* InstructionsViewController.swift in Sources */, E49943F51AE6879C00BF9DE4 /* IntroViewController.swift in Sources */, ); diff --git a/Cliqz/React Components/Engine.swift b/Cliqz/React Components/Engine.swift index 6e7ed7d8d..adafc79da 100644 --- a/Cliqz/React Components/Engine.swift +++ b/Cliqz/React Components/Engine.swift @@ -31,7 +31,7 @@ open class Engine { let jsCodeLocation = Bundle.main.url(forResource: "jsengine.bundle", withExtension: "js") #endif - rootView = RCTRootView( bundleURL: jsCodeLocation, moduleName: "ExtensionApp", initialProperties: nil, launchOptions: nil ) + rootView = RCTRootView( bundleURL: jsCodeLocation, moduleName: "ExtensionApp", initialProperties: ["showSearchOnboarding": true], 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 cb9580f8b..76defbe0a 100644 --- a/Cliqz/React Components/Modules/JSBridge.swift +++ b/Cliqz/React Components/Modules/JSBridge.swift @@ -59,7 +59,8 @@ class JSBridge : RCTEventEmitter { } override open func supportedEvents() -> [String]! { - return ["callAction", "publishEvent"] + // TODO chrmod: clean native bridge + return ["action", "callAction", "publishEvent"] } override open func constantsToExport() -> [AnyHashable : Any]! { @@ -118,7 +119,7 @@ class JSBridge : RCTEventEmitter { self.eventCallbacks[actionId] = callback } // only send event - callback is handled in action reply - self.sendEvent(withName: "callAction", body: ["id": actionId, "action": functionName, "args": args]) + self.sendEvent(withName: "action", body: ["id": actionId, "action": functionName, "args": args]) } diff --git a/Cliqz/React Components/Modules/NativeDrawable.m b/Cliqz/React Components/Modules/NativeDrawable.m index 04e5609ba..3a710f8a5 100644 --- a/Cliqz/React Components/Modules/NativeDrawable.m +++ b/Cliqz/React Components/Modules/NativeDrawable.m @@ -11,13 +11,14 @@ #import @interface NativeDrawable : RCTViewManager +@property BOOL hasTint; @end @implementation NativeDrawable RCT_EXPORT_MODULE() -- (UIView *)view +- (UIImageView *)view { UIImageView* imageView = [[UIImageView alloc] init]; [imageView setContentMode:UIViewContentModeScaleAspectFit]; @@ -30,6 +31,7 @@ - (UIView *)view if (color != nil) { view.image = [view.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; [view setTintColor: color]; + [self setHasTint:YES]; } else { NSLog(@"color %@ is not valid", json); @@ -41,6 +43,10 @@ - (UIView *)view NSString *imageName = (NSString*)json; if (imageName != nil) { UIImage* image = [UIImage imageNamed:imageName]; + if (self.hasTint) { + image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + } + if (image != nil) { [view setImage:image]; } diff --git a/Cliqz/React Components/Modules/Onboarding.swift b/Cliqz/React Components/Modules/Onboarding.swift new file mode 100644 index 000000000..6f4c0be88 --- /dev/null +++ b/Cliqz/React Components/Modules/Onboarding.swift @@ -0,0 +1,27 @@ +// +// Onboarding.swift +// Client +// +// Created by Khaled Tantawy on 17.07.19. +// Copyright © 2019 Cliqz. All rights reserved. +// + +import React + +@objc(Onboarding) +class Onboarding: RCTEventEmitter { + + override static func requiresMainQueueSetup() -> Bool { + return false + } + + @objc(tryLumenSearch:) + 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 + } else { + // TODO: PK close cliqz search and open queary with default search engine + } + } +} diff --git a/Cliqz/React Components/Modules/OnboardingBridge.m b/Cliqz/React Components/Modules/OnboardingBridge.m new file mode 100644 index 000000000..d62c2d717 --- /dev/null +++ b/Cliqz/React Components/Modules/OnboardingBridge.m @@ -0,0 +1,15 @@ +// +// OnboardingBridge.m +// Client +// +// Created by Khaled Tantawy on 17.07.19. +// Copyright © 2019 Cliqz. All rights reserved. +// + +#import +#import +@interface RCT_EXTERN_MODULE(Onboarding, NSObject) +RCT_EXTERN_METHOD(tryLumenSearch:(BOOL) accepted) +@end + + diff --git a/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_bing.imageset/Contents.json b/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_bing.imageset/Contents.json new file mode 100644 index 000000000..23f6a32e3 --- /dev/null +++ b/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_bing.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "bing@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "bing@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "bing@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_bing.imageset/bing@1x.png b/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_bing.imageset/bing@1x.png new file mode 100644 index 000000000..9d89ebe7e Binary files /dev/null and b/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_bing.imageset/bing@1x.png differ diff --git a/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_bing.imageset/bing@2x.png b/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_bing.imageset/bing@2x.png new file mode 100644 index 000000000..760d9acde Binary files /dev/null and b/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_bing.imageset/bing@2x.png differ diff --git a/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_bing.imageset/bing@3x.png b/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_bing.imageset/bing@3x.png new file mode 100644 index 000000000..9d410b822 Binary files /dev/null and b/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_bing.imageset/bing@3x.png differ diff --git a/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_ddg.imageset/Contents.json b/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_ddg.imageset/Contents.json new file mode 100644 index 000000000..d96ae4740 --- /dev/null +++ b/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_ddg.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ddg@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ddg@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ddg@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_ddg.imageset/ddg@1x.png b/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_ddg.imageset/ddg@1x.png new file mode 100644 index 000000000..6c3383685 Binary files /dev/null and b/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_ddg.imageset/ddg@1x.png differ diff --git a/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_ddg.imageset/ddg@2x.png b/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_ddg.imageset/ddg@2x.png new file mode 100644 index 000000000..769f70b4a Binary files /dev/null and b/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_ddg.imageset/ddg@2x.png differ diff --git a/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_ddg.imageset/ddg@3x.png b/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_ddg.imageset/ddg@3x.png new file mode 100644 index 000000000..527faecf8 Binary files /dev/null and b/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_ddg.imageset/ddg@3x.png differ diff --git a/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_google.imageset/Contents.json b/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_google.imageset/Contents.json new file mode 100644 index 000000000..1cc77a273 --- /dev/null +++ b/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_google.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "google@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "google@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "google@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_google.imageset/google@1x.png b/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_google.imageset/google@1x.png new file mode 100644 index 000000000..62e7c3dd4 Binary files /dev/null and b/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_google.imageset/google@1x.png differ diff --git a/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_google.imageset/google@2x.png b/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_google.imageset/google@2x.png new file mode 100644 index 000000000..0085500fb Binary files /dev/null and b/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_google.imageset/google@2x.png differ diff --git a/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_google.imageset/google@3x.png b/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_google.imageset/google@3x.png new file mode 100644 index 000000000..0bc74b1d1 Binary files /dev/null and b/Cliqz/Resources/Common.xcassets/ReactCards/ic_ez_google.imageset/google@3x.png differ diff --git a/README.md b/README.md index 2889b2781..7625c87f0 100644 --- a/README.md +++ b/README.md @@ -58,14 +58,9 @@ brew install carthage 4. Install Ruby `bundler`: ```sh sudo gem install bundler -bundle install -``` -5. Install [cocoadpods](https://cocoapods.org/) version **1.5.3** -```shell -sudo gem install cocoapods -v 1.5.3 ``` -6. Fork the repository https://github.com/ghostery/browser-ios from GitHub UI -7. Clone the forked repository + add upstream remote: +5. Fork the repository https://github.com/ghostery/browser-ios from GitHub UI +6. Clone the forked repository + add upstream remote: ```shell git clone https://github.com/YOUR_USERNAME/ghostery-ios cd ghostery-ios @@ -74,18 +69,19 @@ git fetch upstream git checkout upstream/master git checkout -b my-working-branch ``` -8. Pull in the project dependencies: +7. Pull in the project dependencies: ```shell sh ./bootstrap.sh -npm ci -npm run bundle-lumen -# Or for Ghostery: npm run bundle-ghostery +npm run bundle-ghostery +# Or for Cliqz: npm run bundle-cliqz +# Or for Lumen: npm run bundle-lumen rm -rf Pods +bundle install bundle exec pod install npm run postinstall ``` -9. Open `Client.xcworkspace` in Xcode. -10. Build the `Fennec` scheme in Xcode. +8. Open `Client.xcworkspace` in Xcode. +9. Build the `Ghostery`/`Cliqz`/`Lumen` scheme in Xcode. Note: When you run `bundle install`, you might get following error `An error occurred while installing unf_ext (0.0.7.5), and Bundler cannot continue.`. Above error happens with ruby 2.5.1. Just make sure to use 2.5.3 ruby version `rvm use 2.5.3` and problem will be fixed. diff --git a/js/i18n.js b/js/i18n.js new file mode 100644 index 000000000..648302a08 --- /dev/null +++ b/js/i18n.js @@ -0,0 +1,25 @@ +import { NativeModules } from 'react-native'; +import console from 'browser-core-lumen-ios/build/modules/core/console'; + +let translations; + +export default function t(key) { + if (!translations) { + const locale = NativeModules.LocaleConstants.lang; + switch (locale) { + case 'de': + translations = require('./localization/de.json'); + break; + default: + translations = require('./localization/en.json'); + } + } + const translation = translations[key]; + + if (!translation) { + console.warn(`Cannot find translation for key "${key}"`); + return key; + } + + return translation.message; +} \ No newline at end of file diff --git a/js/localization/de.json b/js/localization/de.json new file mode 100644 index 000000000..1aaf5f583 --- /dev/null +++ b/js/localization/de.json @@ -0,0 +1,35 @@ +{ + "search_no_results": { + "message": "KEINE TREFFER GEFUNDEN" + }, + "search_footer": { + "message": "DIESE SUCHANFRAGE IST ANONYM" + }, + "search_alternative_search_engines_info": { + "message": "Lumen Suche verlassen?" + }, + "onboarding_title": { + "message": "Neu: Anonyme Suche" + }, + "onboarding_description_line1": { + "message": "Jetzt aktivieren um anonym zu suchen." + }, + "onboarding_description_line2": { + "message": "Dies kann jederzeit in den Einstellungen geändert werden." + }, + "onboarding_action_accept": { + "message": "AUSPROBIEREN" + }, + "onboarding_action_reject": { + "message": "NEIN, DANKE" + }, + "onboarding_result_with_query": { + "message": "SUCHE AKTIVIERT" + }, + "onboarding_result_without_query": { + "message": "EINFACH LOSTIPPEN" + }, + "test": { + "message": "de" + } +} \ No newline at end of file diff --git a/js/localization/en.json b/js/localization/en.json new file mode 100644 index 000000000..ee1ef0c10 --- /dev/null +++ b/js/localization/en.json @@ -0,0 +1,35 @@ +{ + "search_no_results": { + "message": "NO RESULTS FOUND" + }, + "search_footer": { + "message": "THIS SEARCH QUERY IS ANONYMOUS" + }, + "search_alternative_search_engines_info": { + "message": "Leave Lumen Search?" + }, + "onboarding_title": { + "message": "New: Anonymous Search" + }, + "onboarding_description_line1": { + "message": "Activate now to search anonymously." + }, + "onboarding_description_line2": { + "message": "This can be changed anytime in settings." + }, + "onboarding_action_accept": { + "message": "TRY NOW" + }, + "onboarding_action_reject": { + "message": "NO, THANKS" + }, + "onboarding_result_with_query": { + "message": "SEARCH ACTIVATED" + }, + "onboarding_result_without_query": { + "message": "START TYPING" + }, + "test": { + "message": "de" + } +} \ No newline at end of file diff --git a/js/lumen-onboarding.js b/js/lumen-onboarding.js new file mode 100644 index 000000000..e7a780567 --- /dev/null +++ b/js/lumen-onboarding.js @@ -0,0 +1,112 @@ +import React from 'react'; +import { StyleSheet, View, Text, TouchableWithoutFeedback, Animated, Easing } from 'react-native'; +import { XmlEntities } from 'html-entities'; +import t from './i18n'; + +const styles = StyleSheet.create({ + container: { + backgroundColor: 'white', + borderBottomLeftRadius: 9, + borderBottomRightRadius: 9, + alignItems: 'center', + justifyContent: 'center', + paddingTop: 35, + paddingBottom: 25, + }, + tryNow: { + backgroundColor: '#3647D0', + borderRadius: 14, + alignItems: 'center', + justifyContent: 'center', + height: 36, + marginTop: 20, + }, + title: { + letterSpacing: 0.25, + fontSize: 18, + fontWeight: '700', + lineHeight: 21, + }, + body: { + marginTop: 8, + fontSize: 13, + lineHeight: 16, + fontWeight: '500', + } +}); + +export default class Onboarding extends React.Component { + state = { + isClicked: false, + } + + get checkMark() { + if (!this._checkMark) { + const entities = new XmlEntities(); + this._checkMark = entities.decode('✓'); + } + + return this._checkMark; + } + + componentWillMount() { + this.animatedValue = new Animated.Value(0); + this.interpolateColor = (from, to) => this.animatedValue.interpolate({ + inputRange: [0, 150], + outputRange: [from, to] + }); + this.interplateWidth = this.animatedValue.interpolate({ + inputRange: [0, 150], + outputRange: [95, 36] + }); + } + + onPress = (choice) => { + this.props.onChoice(choice); + Animated.timing(this.animatedValue, { + toValue: 150, + duration: 250, + easing: Easing.ease + }).start(); + this.setState({ isClicked: true }); + } + + render() { + // TODO chrmod: translations + const tryNowText = this.state.isClicked ? this.checkMark : t('onboarding_action_accept'); + const noThanksText = this.state.isClicked ? (this.props.hasQuery ? t('onboarding_result_with_query') : t('onboarding_result_without_query')) : t('onboarding_action_reject'); + const animatedStyle = { + backgroundColor: this.interpolateColor('#3647D0', '#AEAFFF'), + width: this.interplateWidth, + }; + return ( + + + {t('onboarding_title')} + + + {t('onboarding_description_line1')} + + + {t('onboarding_description_line2')} + + this.onPress(true)}> + + {tryNowText} + + + this.onPress(false)}> + <> + {noThanksText} + + + + ); + } +} \ No newline at end of file diff --git a/lumen.js b/lumen.js index e3fa63240..af69f6f5f 100644 --- a/lumen.js +++ b/lumen.js @@ -2,7 +2,7 @@ import 'react-native/Libraries/Core/InitializeCore'; import './setup'; import 'process-nextick-args'; import React from 'react'; -import { AppRegistry, StyleSheet, View, AsyncStorage } from 'react-native'; +import { AppRegistry, StyleSheet, View, Text, ScrollView, NativeModules, NativeEventEmitter, TouchableWithoutFeedback } from 'react-native'; import { startup } from 'browser-core-lumen-ios'; import Cliqz from './cliqzWrapper'; import { setDefaultSearchEngine } from 'browser-core-lumen-ios/build/modules/core/search-engines'; @@ -13,10 +13,18 @@ import SearchUI from 'browser-core-lumen-ios/build/modules/mobile-cards/SearchUI import SearchUIVertical from 'browser-core-lumen-ios/build/modules/mobile-cards-vertical/SearchUI'; import { Provider as CliqzProvider } from 'browser-core-lumen-ios/build/modules/mobile-cards/cliqz'; import { Provider as ThemeProvider } from 'browser-core-lumen-ios/build/modules/mobile-cards-vertical/withTheme'; +import Onboarding from './js/lumen-onboarding'; +import inject from 'browser-core-lumen-ios/build/modules/core/kord/inject'; +import NativeDrawable, { normalizeUrl } from 'browser-core-lumen-ios/build/modules/mobile-cards/components/custom/NativeDrawable'; +import t from './js/i18n'; + +const nativeBridge = NativeModules.JSBridge; // set app global for debugging +// TODO chrmod: get rid of startup const appStart = startup.then((app) => { global.app = app; + return app; }); const styles = StyleSheet.create({ @@ -25,36 +33,51 @@ const styles = StyleSheet.create({ flexDirection: 'column', backgroundColor: 'transparent' }, + footer: { + height: 20, + backgroundColor: '#656d7e', + alignItems: 'center', + justifyContent: 'center', + borderBottomLeftRadius: 5, + borderBottomRightRadius: 5 + }, + searchEnginesContainer: { + flexDirection: 'row', + justifyContent: 'space-evenly', + marginTop: 20, + marginBottom: 100, + }, + searchEngineIcon: { + height: 73, + width: 73, + borderRadius: 10, + overflow: 'hidden', + }, }); -// wrapper for a component to add top padding on iOS -function AppContainer(App, appStart) { - return () => ( - - - - ); -} - class MobileCards extends React.Component { constructor(props) { super(props); + this.state = { + onboarding: props.showSearchOnboarding, + results: { + results: [], + meta: {} + }, + hasQuery: false, + theme: 'lumen-light' + } + this.cliqz = new Cliqz(); this.isDeveloper = prefs.get('developer', false); - this.appStart = props.appStart || Promise.resolve(); + this.appStart = appStart || Promise.resolve(); events.sub('search:results', this.updateResults); events.sub('mobile-browser:notify-preferences', this.updatePreferences); events.sub('mobile-browser:set-search-engine', this.setSearchEngine); addConnectionChangeListener(); - } - - state = { - results: { - results: [], - meta: {} - }, - theme: 'light' + this.eventEmitter = new NativeEventEmitter(nativeBridge); + this.eventEmitter.addListener('action', this.onAction); } componentWillUnmount() { @@ -62,15 +85,27 @@ class MobileCards extends React.Component { events.un_sub('mobile-browser:set-search-engine', this.setSearchEngine); events.un_sub('search:results', this.updateResults); removeConnectionChangeListener(); + this.eventEmitter.removeAllListeners(); } - setSearchEngine = (engine) => { - setDefaultSearchEngine(engine); + onAction = async ({ action, args, id }) => { + const [module, name] = action.split(':'); + if (this.state.onboarding === true && module === 'search' && name === 'startSearch') { + // don't start search until onboarding is finished + this.retryLastSearch = () => this.onAction({ action, args, id }); + this.setState({ + hasQuery: true, + }); + return; + } + const response = await inject.module(module).action(name, ...args); + if (typeof id !== 'undefined') { + nativeBridge.replyToAction(id, response); + } } - _setTheme(incognito) { - const theme = incognito ? 'dark' : 'light'; - this.setState({ theme }); + setSearchEngine = (engine) => { + setDefaultSearchEngine(engine); } updatePreferences = (_prefs) => { @@ -78,36 +113,107 @@ class MobileCards extends React.Component { this.appStart.then(() => { Object.keys(_prefs).forEach((key) => { prefs.set(key, _prefs[key]); - if ((key === 'incognito')) { - this._setTheme(_prefs[key]); - } }); }); } - updateResults = results => this.setState({ results }); + updateResults = results => this.setState({ results, onboarding: false }); + + onTryNowPressed = (choice) => { + NativeModules.Onboarding.tryLumenSearch(choice); + setTimeout(() => { + this.setState({ + onboarding: false, + }, () => { + if (choice && this.retryLastSearch) { + this.retryLastSearch(); + this.retryLastSearch = null; + } + }); + }, 1000); // wait for onboarding animation to finish + } + + openLink = (url) => { + NativeModules.BrowserActions.openLink(url, this.state.results.query, true); + } render() { - const { results, suggestions, meta } = this.state.results; + const { results, suggestions, meta, query } = this.state.results; const appearance = this.state.theme; const layout = 'vertical'; const SearchComponent = layout === "horizontal" ? SearchUI : SearchUIVertical; - return ( + if (this.state.onboarding) { + return ( + + ); + } else { + NativeModules.QuerySuggestion.showQuerySuggestions(query, suggestions); + return ( - + + } + separator={} + footer={} + /> + <> + { /* TODO chrmod: colors and font sizes */ } + { results.length === 0 && + + + + } + + + {t('search_footer')} + + + + {t('search_alternative_search_engines_info')} + + + { /* TODO chrmod: list + send openlink event onclick + real pngs */ } + this.openLink(`https://google.com/search?q=${encodeURIComponent(this.state.results.query)}`)} + > + + + this.openLink(`https://duckduckgo.com/?q=${encodeURIComponent(this.state.results.query)}`)} + > + + + this.openLink(`https://www.bing.com/search?q=${encodeURIComponent(this.state.results.query)}`)} + > + + + + + - ); + ); + } } } -AppRegistry.registerComponent('ExtensionApp', () => AppContainer(MobileCards, appStart)); \ No newline at end of file +AppRegistry.registerComponent('ExtensionApp', () => MobileCards); diff --git a/package-lock.json b/package-lock.json index acd701cb6..5dc919ca6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1075,10 +1075,10 @@ "requires": { "@babel/polyfill": "^7.0.0", "eventtargeter": "0.5.0", - "sync-promise": "git+https://github.com/brettz9/sync-promise.git#25845a49a00aa2d2c985a5149b97c86a1fcdc75a", + "sync-promise": "git+https://github.com/brettz9/sync-promise.git#full-sync-missing-promise-features", "typeson": "5.11.0", "typeson-registry": "1.0.0-alpha.26", - "websql": "git+https://github.com/brettz9/node-websql.git#5149bc0763376ca757fc32dc74345ada0467bfbb" + "websql": "git+https://github.com/brettz9/node-websql.git#configurable-secure2" } }, "@cliqz/url-parser": { @@ -14827,8 +14827,8 @@ } }, "browser-core-lumen-ios": { - "version": "https://s3.amazonaws.com/cdncliqz/update/edge/lumen-ios/master/13.39.0.25.4908295.tgz", - "integrity": "sha512-nbPFmRkmZYo2rI7oqG9EFeSpDAvI4gNu1v+unDpWmnmE7Q4wY1XQSu/1eM4UhhQvM9BOt1g0HBn/6DKTGJMMpw==", + "version": "https://s3.amazonaws.com/cdncliqz/update/edge/lumen-ios/master/13.39.0.50.76d6dc1.tgz", + "integrity": "sha512-HKIfolwrHCWUJ8FxgakffgfEJrhYH2NxC6iIOoGazM1HD6SfCk8i2h0jU2O3K3MXjV+nrbYoqOFYmTXYXNEVdw==", "requires": { "@cliqz-oss/dexie": "^2.0.4", "@cliqz/adblocker": "^0.11.0", @@ -14871,9 +14871,9 @@ }, "dependencies": { "ajv": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.1.tgz", - "integrity": "sha512-w1YQaVGNC6t2UCPjEawK/vo/dG8OOrVtUmhBT1uJJYxbl5kU2Tj3v6LGqBcsysN1yhuCStJCCA3GqdvKY8sqXQ==", + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", "requires": { "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", @@ -16987,7 +16987,8 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "optional": true }, "aproba": { "version": "1.2.0", @@ -17008,12 +17009,14 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "optional": true }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -17028,17 +17031,20 @@ "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "optional": true }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "optional": true }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -17155,7 +17161,8 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "optional": true }, "ini": { "version": "1.3.5", @@ -17167,6 +17174,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -17181,6 +17189,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -17188,12 +17197,14 @@ "minimist": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "optional": true }, "minipass": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.2.4.tgz", "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==", + "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -17212,6 +17223,7 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "optional": true, "requires": { "minimist": "0.0.8" } @@ -17298,7 +17310,8 @@ "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "optional": true }, "object-assign": { "version": "4.1.1", @@ -17310,6 +17323,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "optional": true, "requires": { "wrappy": "1" } @@ -17395,7 +17409,8 @@ "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -17431,6 +17446,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -17450,6 +17466,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -17493,12 +17510,14 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "optional": true }, "yallist": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", - "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=" + "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=", + "optional": true } } }, diff --git a/package.json b/package.json index b599fb5b9..60572dcbe 100644 --- a/package.json +++ b/package.json @@ -30,8 +30,9 @@ "appium": "1.9.0", "browser-core": "https://s3.amazonaws.com/cdncliqz/update/edge/cliqz-ios/master/1.38.0.1b131.7bdc200.tgz", "browser-core-cliqz-ios": "https://s3.amazonaws.com/cdncliqz/update/edge/cliqz-ios/master/3.39.0.25.4908295.tgz", - "browser-core-lumen-ios": "https://s3.amazonaws.com/cdncliqz/update/edge/lumen-ios/master/13.39.0.25.4908295.tgz", + "browser-core-lumen-ios": "https://s3.amazonaws.com/cdncliqz/update/edge/lumen-ios/master/13.39.0.50.76d6dc1.tgz", "buffer": "5.0.7", + "html-entities": "^1.2.1", "https-browserify": "1.0.0", "page-metadata-parser": "^1.1.2", "path-browserify": "0.0.0",