diff --git a/.env.example b/.env.example index c4feee42c..c171c2243 100644 --- a/.env.example +++ b/.env.example @@ -9,4 +9,5 @@ SEARCH_API_TOKEN= SEARCH_API_URL= SERVER_LIST_API= USER_AGENT= -BUGSNAG_API_KEY= \ No newline at end of file +BUGSNAG_API_KEY= +ANALYTICS_URL= \ No newline at end of file diff --git a/ios/Ecency.xcodeproj/project.pbxproj b/ios/Ecency.xcodeproj/project.pbxproj index 62d8e6ee0..ca8280071 100644 --- a/ios/Ecency.xcodeproj/project.pbxproj +++ b/ios/Ecency.xcodeproj/project.pbxproj @@ -1146,7 +1146,7 @@ "\"$(PODS_ROOT)/Headers/Private/React-Core\"", ); INFOPLIST_FILE = "$(SRCROOT)/Ecency/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MARKETING_VERSION = 3.0.2; OTHER_LDFLAGS = ( @@ -1219,7 +1219,7 @@ "\"$(PODS_ROOT)/Headers/Private/React-Core\"", ); INFOPLIST_FILE = "$(SRCROOT)/Ecency/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MARKETING_VERSION = 3.0.2; OTHER_LDFLAGS = ( diff --git a/ios/Podfile b/ios/Podfile index 752a84394..cb4bcbdb4 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,4 +1,4 @@ -platform :ios, '9.0' +platform :ios, '10.0' require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' target 'Ecency' do diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 9af417e73..29842af4d 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -135,6 +135,9 @@ PODS: - lottie-react-native (3.5.0): - lottie-ios (~> 3.1.8) - React + - MatomoTracker (7.2.2): + - MatomoTracker/Core (= 7.2.2) + - MatomoTracker/Core (7.2.2) - nanopb (1.30905.0): - nanopb/decode (= 1.30905.0) - nanopb/encode (= 1.30905.0) @@ -312,6 +315,9 @@ PODS: - react-native-config/App (= 1.3.3) - react-native-config/App (1.3.3): - React + - react-native-matomo-sdk (0.4.0): + - MatomoTracker (~> 7) + - React (~> 0.60) - react-native-netinfo (5.9.5): - React - react-native-receive-sharing-intent (1.0.4): @@ -440,6 +446,7 @@ DEPENDENCIES: - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`) - "react-native-cameraroll (from `../node_modules/@react-native-community/cameraroll`)" - react-native-config (from `../node_modules/react-native-config`) + - react-native-matomo-sdk (from `../node_modules/react-native-matomo-sdk`) - "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)" - react-native-receive-sharing-intent (from `../node_modules/react-native-receive-sharing-intent`) - react-native-splash-screen (from `../node_modules/react-native-splash-screen`) @@ -492,6 +499,7 @@ SPEC REPOS: - GoogleDataTransport - GoogleUtilities - libwebp + - MatomoTracker - nanopb - PromisesObjC - Protobuf @@ -547,6 +555,8 @@ EXTERNAL SOURCES: :path: "../node_modules/@react-native-community/cameraroll" react-native-config: :path: "../node_modules/react-native-config" + react-native-matomo-sdk: + :path: "../node_modules/react-native-matomo-sdk" react-native-netinfo: :path: "../node_modules/@react-native-community/netinfo" react-native-receive-sharing-intent: @@ -642,6 +652,7 @@ SPEC CHECKSUMS: libwebp: 946cb3063cea9236285f7e9a8505d806d30e07f3 lottie-ios: 48fac6be217c76937e36e340e2d09cf7b10b7f5f lottie-react-native: 1fb4ce21d6ad37dab8343eaff8719df76035bd93 + MatomoTracker: a59ec4da0f580be57bdc6baa708a71a86532a832 nanopb: c43f40fadfe79e8b8db116583945847910cbabc9 PromisesObjC: b48e0338dbbac2207e611750777895f7a5811b75 Protobuf: 2793fcd0622a00b546c60e7cbbcc493e043e9bb9 @@ -657,6 +668,7 @@ SPEC CHECKSUMS: React-jsinspector: fa0ecc501688c3c4c34f28834a76302233e29dc0 react-native-cameraroll: e2917a5e62da9f10c3d525e157e25e694d2d6dfa react-native-config: 9a061347e0136fdb32d43a34d60999297d672361 + react-native-matomo-sdk: 03b35acda42463614f10ac61d013f7ae7dc8beed react-native-netinfo: a53b00d949b6456913aaf507d9dba90c4008c611 react-native-receive-sharing-intent: feba0a332a07977549a85aa58b496eb44368366a react-native-splash-screen: 200d11d188e2e78cea3ad319964f6142b6384865 @@ -694,6 +706,6 @@ SPEC CHECKSUMS: toolbar-android: 85f3ef4d691469f2d304e7dee4bca013aa1ba1ff Yoga: f2a7cd4280bfe2cca5a7aed98ba0eb3d1310f18b -PODFILE CHECKSUM: 84e32ce5543427579b8c72d77fd175fe29268d92 +PODFILE CHECKSUM: 1f30c7da5061dbc47185442a6ab4a3c95ac48c04 -COCOAPODS: 1.8.4 +COCOAPODS: 1.9.3 diff --git a/package.json b/package.json index 7875c3cd1..8024dce50 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,7 @@ "react-native-image-zoom-viewer": "^2.2.27", "react-native-keyboard-aware-scroll-view": "^0.9.1", "react-native-linear-gradient": "^2.4.2", + "react-native-matomo-sdk": "^0.4.0", "react-native-modal": "^11.5.6", "react-native-modal-dropdown": "ecency/react-native-modal-dropdown", "react-native-modal-translucent": "^5.0.0", @@ -92,6 +93,7 @@ "react-native-splash-screen": "^3.2.0", "react-native-svg": "^9.5.3", "react-native-swiper": "^1.6.0-rc.3", + "react-native-unique-id": "^2.0.0", "react-native-vector-icons": "^6.6.0", "react-native-version": "^4.0.0", "react-native-version-number": "^0.3.5", diff --git a/src/components/posts/container/postsContainer.js b/src/components/posts/container/postsContainer.js index e65ba38f4..d007753de 100644 --- a/src/components/posts/container/postsContainer.js +++ b/src/components/posts/container/postsContainer.js @@ -2,8 +2,9 @@ import React, { useCallback, useState, useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import get from 'lodash/get'; import unionBy from 'lodash/unionBy'; +import Matomo from 'react-native-matomo-sdk'; -// STEEM +// HIVE import { getPostsSummary, getPost } from '../../../providers/steem/dsteem'; import { getPromotePosts } from '../../../providers/esteem/esteem'; @@ -36,6 +37,7 @@ const PostsContainer = ({ const isHideImages = useSelector((state) => state.ui.hidePostsThumbnails); const username = useSelector((state) => state.account.currentAccount.name); const isLoggedIn = useSelector((state) => state.application.isLoggedIn); + const isAnalytics = useSelector((state) => state.application.isAnalytics); const [isNoPost, setIsNoPost] = useState(false); const [startPermlink, setStartPermlink] = useState(''); @@ -208,6 +210,18 @@ const PostsContainer = ({ setIsLoading(false); _isLoadingPost = false; }); + // track filter and tag views + if (isAnalytics) { + if (tag) { + Matomo.trackView([`/${selectedFilterValue}/${tag}`]).catch((error) => + console.warn('Failed to track screen', error), + ); + } else { + Matomo.trackView([`/${selectedFilterValue}`]).catch((error) => + console.warn('Failed to track screen', error), + ); + } + } }, [ username, @@ -215,6 +229,7 @@ const PostsContainer = ({ isConnected, isLoading, isLoggedIn, + isAnalytics, nsfw, pageType, posts, @@ -247,6 +262,7 @@ const PostsContainer = ({ handleOnScroll={handleOnScroll} isHideImage={isHideImages} isLoggedIn={isLoggedIn} + isAnalytics={isAnalytics} selectedOptionIndex={selectedOptionIndex} tag={tag} filterOptionsValue={filterOptionsValue} diff --git a/src/containers/profileContainer.js b/src/containers/profileContainer.js index aa1cb9c0d..a6e0471c3 100644 --- a/src/containers/profileContainer.js +++ b/src/containers/profileContainer.js @@ -5,6 +5,7 @@ import { withNavigation } from 'react-navigation'; import { get, has, unionBy } from 'lodash'; import { Alert } from 'react-native'; import { injectIntl } from 'react-intl'; +import Matomo from 'react-native-matomo-sdk'; // Providers import { @@ -56,6 +57,7 @@ class ProfileContainer extends Component { navigation, isConnected, isLoggedIn, + isAnalytics, currentAccount: { name: currentAccountUsername }, } = this.props; const username = get(navigation, 'state.params.username'); @@ -76,16 +78,35 @@ class ProfileContainer extends Component { } this._loadProfile(targetUsername); + if (isAnalytics) { + Matomo.trackView([`/@${targetUsername}`]).catch((error) => + console.warn('Failed to track screen', error), + ); + } } _getReplies = async (query) => { - const { isOwnProfile, comments } = this.state; + const { isOwnProfile, comments, user } = this.state; + const { + currentAccount: { name: currentAccountUsername }, + isAnalytics, + } = this.props; let repliesAction; if (!isOwnProfile) { repliesAction = getUserComments; + if (isAnalytics) { + Matomo.trackView([`/@${user.name}/comments`]).catch((error) => + console.warn('Failed to track screen', error), + ); + } } else { repliesAction = getRepliesByLastUpdate; + if (isAnalytics) { + Matomo.trackView([`/@${currentAccountUsername}/replies`]).catch((error) => + console.warn('Failed to track screen', error), + ); + } } if (query) { await repliesAction({ @@ -459,6 +480,7 @@ const mapStateToProps = (state) => ({ isDarkTheme: state.application.isDarkTheme, isLoggedIn: state.application.isLoggedIn, pinCode: state.application.pin, + isAnalytics: state.application.isAnalytics, activeBottomTab: state.ui.activeBottomTab, currentAccount: state.account.currentAccount, isHideImage: state.ui.hidePostsThumbnails, diff --git a/src/redux/actions/applicationActions.js b/src/redux/actions/applicationActions.js index 37cdaa3ef..5e27a526e 100644 --- a/src/redux/actions/applicationActions.js +++ b/src/redux/actions/applicationActions.js @@ -11,6 +11,7 @@ import { CHANGE_VOTE_NOTIFICATION, CLOSE_PIN_CODE_MODAL, IS_CONNECTED, + IS_ANALYTICS, IS_DARK_THEME, IS_DEFAULT_FOOTER, IS_LOGIN_DONE, @@ -144,6 +145,11 @@ export const setConnectivityStatus = (payload) => ({ type: IS_CONNECTED, }); +export const setAnalyticsStatus = (payload) => ({ + payload, + type: IS_ANALYTICS, +}); + export const setNsfw = (payload) => ({ payload, type: SET_NSFW, diff --git a/src/redux/constants/constants.js b/src/redux/constants/constants.js index 42f335932..c97ae5e0b 100644 --- a/src/redux/constants/constants.js +++ b/src/redux/constants/constants.js @@ -7,6 +7,7 @@ export const SET_USER_DATA = 'SET_USER_DATA'; export const ACTIVE_APPLICATION = 'ACTIVE_APPLICATION'; export const CLOSE_PIN_CODE_MODAL = 'CLOSE_PIN_CODE_MODAL'; export const IS_CONNECTED = 'IS_CONNECTED'; +export const IS_ANALYTICS = 'IS_ANALYTICS'; export const IS_DARK_THEME = 'IS_DARK_THEME'; export const IS_PIN_CODE_OPEN = 'IS_PIN_CODE_OPEN'; export const IS_LOGGED_IN = 'IS_LOGGED_IN'; diff --git a/src/redux/reducers/applicationReducer.js b/src/redux/reducers/applicationReducer.js index 60da35d73..f818655eb 100644 --- a/src/redux/reducers/applicationReducer.js +++ b/src/redux/reducers/applicationReducer.js @@ -9,6 +9,7 @@ import { CHANGE_ALL_NOTIFICATION_SETTINGS, CLOSE_PIN_CODE_MODAL, IS_CONNECTED, + IS_ANALYTICS, IS_DARK_THEME, IS_DEFAULT_FOOTER, IS_LOGIN_DONE, @@ -39,6 +40,7 @@ const initialState = { isDarkTheme: false, isDefaultFooter: true, isLoggedIn: false, // Has any logged in user. + isAnalytics: false, isLoginDone: false, isLogingOut: false, isNotificationOpen: true, @@ -78,6 +80,11 @@ export default function (state = initialState, action) { ...state, isConnected: action.payload, }; + case IS_ANALYTICS: + return { + ...state, + isAnalytics: action.payload, + }; case LOGOUT: return { ...state, diff --git a/src/screens/application/container/applicationContainer.js b/src/screens/application/container/applicationContainer.js index 7a488a750..e3c8bb6fe 100644 --- a/src/screens/application/container/applicationContainer.js +++ b/src/screens/application/container/applicationContainer.js @@ -18,6 +18,8 @@ import messaging from '@react-native-firebase/messaging'; import PushNotification from 'react-native-push-notification'; import VersionNumber from 'react-native-version-number'; import ReceiveSharingIntent from 'react-native-receive-sharing-intent'; +import Matomo from 'react-native-matomo-sdk'; +import uniqueId from 'react-native-unique-id'; // Constants import AUTH_TYPE from '../../../constants/authType'; @@ -63,6 +65,7 @@ import { openPinCodeModal, setApi, setConnectivityStatus, + setAnalyticsStatus, setCurrency, setLanguage, setUpvotePercent, @@ -109,6 +112,7 @@ class ApplicationContainer extends Component { componentDidMount = () => { const { isIos } = this.state; const { appVersion } = VersionNumber; + const { dispatch, isAnalytics } = this.props; this._setNetworkListener(); @@ -160,6 +164,25 @@ class ApplicationContainer extends Component { console.log('error :>> ', error); }, ); + + // tracking init + Matomo.initialize(Config.ANALYTICS_URL, 1) + .catch((error) => console.warn('Failed to initialize matomo', error)) + .then(() => { + if (isAnalytics !== true) { + dispatch(setAnalyticsStatus(true)); + } + }); + + uniqueId() + .then(async (id) => { + await Matomo.setUserId(id).catch((error) => console.warn('Error setting user id', error)); + }) + .catch((error) => console.error(error)); + // start up event + Matomo.trackEvent('Application', 'Startup').catch((error) => + console.warn('Failed to track event', error), + ); }; componentDidUpdate(prevProps, prevState) { @@ -798,6 +821,7 @@ export default connect( isActiveApp: state.application.isActive, api: state.application.api, isGlobalRenderRequired: state.application.isRenderRequired, + isAnalytics: state.application.isAnalytics, // Account unreadActivityCount: state.account.currentAccount.unread_activity_count, diff --git a/src/screens/post/container/postContainer.js b/src/screens/post/container/postContainer.js index f4cd6cc08..706ab690d 100644 --- a/src/screens/post/container/postContainer.js +++ b/src/screens/post/container/postContainer.js @@ -4,7 +4,9 @@ import { withNavigation } from 'react-navigation'; import get from 'lodash/get'; // Services and Actions +import Matomo from 'react-native-matomo-sdk'; import { getPost } from '../../../providers/steem/dsteem'; +// import { matomo } from '../../../providers/esteem/analytics'; // Component import PostScreen from '../screen/postScreen'; @@ -14,7 +16,7 @@ import PostScreen from '../screen/postScreen'; *@props --> content which is include all post data Object * */ -const PostContainer = ({ navigation, currentAccount, isLoggedIn }) => { +const PostContainer = ({ navigation, currentAccount, isLoggedIn, isAnalytics }) => { const [post, setPost] = useState(null); const [error, setError] = useState(null); const [isNewPost, setIsNewPost] = useState(false); @@ -33,9 +35,21 @@ const PostContainer = ({ navigation, currentAccount, isLoggedIn }) => { if (content) { setPost(content); + // tracking info + if (isAnalytics) { + Matomo.trackView([`${content.url}`]).catch((error) => + console.warn('Failed to track screen', error), + ); + } } else if (_author && permlink) { _loadPost(_author, permlink); setAuthor(_author); + // tracking info + if (isAnalytics) { + Matomo.trackView([`/post/@${_author}/${permlink}`]).catch((error) => + console.warn('Failed to track screen', error), + ); + } } }, []); @@ -101,6 +115,7 @@ const PostContainer = ({ navigation, currentAccount, isLoggedIn }) => { const mapStateToProps = (state) => ({ currentAccount: state.account.currentAccount, isLoggedIn: state.application.isLoggedIn, + isAnalytics: state.application.isAnalytics, }); export default connect(mapStateToProps)(withNavigation(PostContainer)); diff --git a/yarn.lock b/yarn.lock index 40cd9c0a4..a9461c30b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1009,6 +1009,11 @@ "@types/yargs" "^15.0.0" chalk "^3.0.0" +"@react-native-community/async-storage@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@react-native-community/async-storage/-/async-storage-1.5.0.tgz#647ffcd832272068b0be57332e08d73036ed391f" + integrity sha512-2yE4RzQ5IL+UTPhuMY0ykNRKHf1m90jOnmp8fcDPUun5U97cXlorjI4p66ovDgF0FuOv8ZpiUKvunGy3qqBxwg== + "@react-native-community/async-storage@^1.11.0": version "1.11.0" resolved "https://registry.yarnpkg.com/@react-native-community/async-storage/-/async-storage-1.11.0.tgz#bf81b8813080846f150c67f531987c429b442166" @@ -7161,6 +7166,11 @@ pngjs@^3.3.0: resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f" integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w== +polygoat@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/polygoat/-/polygoat-1.1.4.tgz#329f9a0d1b2d4a45149e2539523c6f7dfd850a5f" + integrity sha1-Mp+aDRstSkUUniU5Ujxvff2FCl8= + posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" @@ -7554,6 +7564,11 @@ react-native-linear-gradient@^2.4.2: resolved "https://registry.yarnpkg.com/react-native-linear-gradient/-/react-native-linear-gradient-2.5.6.tgz#96215cbc5ec7a01247a20890888aa75b834d44a0" integrity sha512-HDwEaXcQIuXXCV70O+bK1rizFong3wj+5Q/jSyifKFLg0VWF95xh8XQgfzXwtq0NggL9vNjPKXa016KuFu+VFg== +react-native-matomo-sdk@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/react-native-matomo-sdk/-/react-native-matomo-sdk-0.4.0.tgz#c7c801525ca14d2b076285367709489310194cd9" + integrity sha512-+XkZtGB2MlAE45LnjCzCl7drDcXMRSlO+5pMlSh29jdTgDC2bsW/qyqvm5H70nMDpfFssLv4HOOhg/scPclkyA== + react-native-modal-dropdown@ecency/react-native-modal-dropdown: version "0.7.0" resolved "https://codeload.github.com/ecency/react-native-modal-dropdown/tar.gz/0269a57c8c248f91a3c599ea20ac106f43f27d1b" @@ -7666,6 +7681,14 @@ react-native-tab-view@^2.11.0: resolved "https://registry.yarnpkg.com/react-native-tab-view/-/react-native-tab-view-2.15.1.tgz#cf4df4ffdec504263a2e06a6becd8831b9a19ad9" integrity sha512-cDYl1pNWspbEHBjHrHVpIC40h4g8VhZe0CIG0CUKcouX98vHVfAojxhgWuRosq9qMhITHpLKM+7BmBLnhCJ0nw== +react-native-unique-id@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/react-native-unique-id/-/react-native-unique-id-2.0.0.tgz#7cb1dd7e7fc10cfb00c66b73bb916104ca12538f" + integrity sha512-sfPhqppzo91O1pKPSMcNFEM7altQUabpeWiiQ9GzIWm0YJ+AwuLTgaOzrBiwRyq0j51oORHGZw7Vo2iNtSvVZA== + dependencies: + "@react-native-community/async-storage" "1.5.0" + polygoat "1.1.4" + react-native-vector-icons@^6.6.0: version "6.7.0" resolved "https://registry.yarnpkg.com/react-native-vector-icons/-/react-native-vector-icons-6.7.0.tgz#8871ba52ef8e98878ba7a932847f96b6dfc1c137"