diff --git a/.env.defaults b/.env.defaults index d1981763f..4d3218a30 100644 --- a/.env.defaults +++ b/.env.defaults @@ -10,3 +10,5 @@ LBRY_WEB_API=https://api.lbry.tv LBRY_WEB_STREAMING_API=https://cdn.lbryplayer.xyz WELCOME_VERSION=1.0 DEFAULT_LANGUAGE=en +MATOMO_URL=https://analytics.lbry.com/ +MATOMO_ID=6 diff --git a/config.js b/config.js index 4fa67f6d4..4aee543b3 100644 --- a/config.js +++ b/config.js @@ -12,6 +12,8 @@ const config = { LBRY_WEB_STREAMING_API: process.env.LBRY_WEB_STREAMING_API, //cdn.lbryplayer.xyz', WELCOME_VERSION: process.env.WELCOME_VERSION, DEFAULT_LANGUAGE: process.env.DEFAULT_LANGUAGE, + MATOMO_URL: process.env.MATOMO_URL, + MATOMO_ID: process.env.MATOMO_ID, LOGO_TITLE: process.env.LOGO_TITLE, PINNED_URI_1: process.env.PINNED_URI_1, PINNED_LABEL_1: process.env.PINNED_LABEL_1, diff --git a/package.json b/package.json index 68681144e..749460946 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "@babel/preset-flow": "^7.0.0", "@babel/preset-react": "^7.0.0", "@babel/register": "^7.0.0", + "@datapunt/matomo-tracker-js": "^0.1.4", "@exponent/electron-cookies": "^2.0.0", "@hot-loader/react-dom": "^16.8", "@lbry/components": "^4.2.2", diff --git a/static/app-strings.json b/static/app-strings.json index bc32bcc5d..cca4b0ca2 100644 --- a/static/app-strings.json +++ b/static/app-strings.json @@ -1119,5 +1119,5 @@ "You don't have blocked channels.": "You don't have blocked channels.", "You have one blocked channel.": "You have one blocked channel.", "You have %channels% blocked channels.": "You have %channels% blocked channels.", - "Drop here to publish!": "Drop here to publish!" + "Your Wallet": "Your Wallet" } \ No newline at end of file diff --git a/ui/analytics.js b/ui/analytics.js index 01a3d1514..71cfbeedb 100644 --- a/ui/analytics.js +++ b/ui/analytics.js @@ -2,6 +2,7 @@ import { Lbryio } from 'lbryinc'; import ReactGA from 'react-ga'; import * as Sentry from '@sentry/browser'; +import MatomoTracker from '@datapunt/matomo-tracker-js'; import { history } from './store'; import { SDK_API_PATH } from './index'; // @if TARGET='app' @@ -9,6 +10,7 @@ import Native from 'native'; import ElectronCookies from '@exponent/electron-cookies'; import { generateInitialUrl } from 'util/url'; // @endif +import { MATOMO_ID, MATOMO_URL } from 'config'; const isProduction = process.env.NODE_ENV === 'production'; const devInternalApis = process.env.LBRY_API_URL; @@ -38,7 +40,7 @@ type Analytics = { apiLogView: (string, string, string, ?number, ?() => void) => Promise, apiLogPublish: (ChannelClaim | StreamClaim) => void, apiSyncTags: ({}) => void, - tagFollowEvent: (string, boolean, string) => void, + tagFollowEvent: (string, boolean, ?string) => void, videoStartEvent: (string, number) => void, videoBufferEvent: (string, number) => void, emailProvidedEvent: () => void, @@ -93,6 +95,11 @@ const analytics: Analytics = { if (thirdPartyAnalyticsEnabled) { ReactGA.pageview(path, [SECOND_TRACKER_NAME]); } + if (internalAnalyticsEnabled) { + MatomoInstance.trackPageView({ + href: `${path}`, + }); + } }, setUser: userId => { if (thirdPartyAnalyticsEnabled && userId) { @@ -185,41 +192,52 @@ const analytics: Analytics = { videoStartEvent: (claimId, duration) => { sendGaTimingEvent('Media', 'TimeToStart', duration, claimId); sendPromMetric('time_to_start', duration); + sendMatomoEvent('Media', 'TimeToStart', claimId, duration); }, videoBufferEvent: (claimId, currentTime) => { sendGaTimingEvent('Media', 'BufferTimestamp', currentTime * 1000, claimId); sendPromMetric('buffer'); + sendMatomoEvent('Media', 'BufferTimestamp', claimId, currentTime * 1000); }, - tagFollowEvent: (tag, following, location) => { + tagFollowEvent: (tag, following) => { sendGaEvent(following ? 'Tag-Follow' : 'Tag-Unfollow', tag); + sendMatomoEvent('Tag', following ? 'Tag-Follow' : 'Tag-Unfollow', tag); }, channelBlockEvent: (uri, blocked, location) => { sendGaEvent(blocked ? 'Channel-Hidden' : 'Channel-Unhidden', uri); + sendMatomoEvent(blocked ? 'Channel-Hidden' : 'Channel-Unhidden', uri); }, emailProvidedEvent: () => { sendGaEvent('Engagement', 'Email-Provided'); + sendMatomoEvent('Engagement', 'Email-Provided'); }, emailVerifiedEvent: () => { sendGaEvent('Engagement', 'Email-Verified'); + sendMatomoEvent('Engagement', 'Email-Verified'); }, rewardEligibleEvent: () => { sendGaEvent('Engagement', 'Reward-Eligible'); + sendMatomoEvent('Engagement', 'Reward-Eligible'); }, openUrlEvent: (url: string) => { sendGaEvent('Engagement', 'Open-Url', url); + sendMatomoEvent('Engagement', 'Open-Url', url); }, trendingAlgorithmEvent: (trendingAlgorithm: string) => { sendGaEvent('Engagement', 'Trending-Algorithm', trendingAlgorithm); }, startupEvent: () => { sendGaEvent('Startup', 'Startup'); + sendMatomoEvent('Startup', 'Startup'); }, readyEvent: (timeToReady: number) => { sendGaEvent('Startup', 'App-Ready'); sendGaTimingEvent('Startup', 'App-Ready', timeToReady); + sendMatomoEvent('Startup', 'App-Ready', 'Time', timeToReady); }, purchaseEvent: (purchaseInt: number) => { sendGaEvent('Purchase', 'Purchase-Complete', undefined, purchaseInt); + sendMatomoEvent('Purchase', 'Purchase-Complete', 'someLabel', purchaseInt); }, }; @@ -237,6 +255,13 @@ function sendGaEvent(category, action, label, value) { } } +function sendMatomoEvent(category, action, name, value) { + if (internalAnalyticsEnabled) { + const event = { category, action, name, value }; + MatomoInstance.trackEvent(event); + } +} + function sendGaTimingEvent(category: string, action: string, timeInMs: number, label?: string) { if (thirdPartyAnalyticsEnabled && isProduction) { ReactGA.timing( @@ -285,6 +310,16 @@ if (!IS_WEB) { } } +const MatomoInstance = new MatomoTracker({ + urlBase: MATOMO_URL, + siteId: MATOMO_ID, // optional, default value: `1` + // heartBeat: { // optional, enabled by default + // active: true, // optional, default value: true + // seconds: 10 // optional, default value: `15 + // }, + // linkTracking: false // optional, default value: true +}); + ReactGA.initialize(gaTrackers, { testMode: process.env.NODE_ENV !== 'production', cookieDomain: 'auto', diff --git a/ui/component/tagsSearch/view.jsx b/ui/component/tagsSearch/view.jsx index da0f268e7..3566f5998 100644 --- a/ui/component/tagsSearch/view.jsx +++ b/ui/component/tagsSearch/view.jsx @@ -4,6 +4,7 @@ import { Form, FormField } from 'component/common/form'; import Tag from 'component/tag'; import { setUnion, setDifference } from 'util/set-operations'; import I18nMessage from 'component/i18nMessage'; +import analytics from 'analytics'; type Props = { tagsPassedIn: Array, @@ -120,7 +121,9 @@ export default function TagsSearch(props: Props) { if (onSelect) { onSelect([{ name: tag }]); } else { + const wasFollowing = followedTags.map(t => t.name).includes(tag); doToggleTagFollowDesktop(tag); + analytics.tagFollowEvent(tag, !wasFollowing); } } return ( diff --git a/web/.env.defaults b/web/.env.defaults index 469f46e87..f486f5dd3 100644 --- a/web/.env.defaults +++ b/web/.env.defaults @@ -10,6 +10,8 @@ LBRY_WEB_API=https://api.lbry.tv LBRY_WEB_STREAMING_API=https://cdn.lbryplayer.xyz WELCOME_VERSION=1.0 DEFAULT_LANGUAGE=en +MATOMO_URL=https://analytics.lbry.com/ +MATOMO_ID=3 # If true, supply copy example to homepage.js in CUSTOM_HOMEPAGE=false # Add up to 2 sidebar links: diff --git a/yarn.lock b/yarn.lock index 1463c5d2a..99f3eb11e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -803,6 +803,11 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" +"@datapunt/matomo-tracker-js@^0.1.4": + version "0.1.4" + resolved "https://registry.yarnpkg.com/@datapunt/matomo-tracker-js/-/matomo-tracker-js-0.1.4.tgz#1226f0964d2c062bf9392e9c2fd89838262b10df" + integrity sha512-bi8/guszgciSNLJQIFgph27AzkiCF1DmLBxtmJE3CsLxfc0aTgI2vMg3EFoLv13Mu8HLaCs27Z7vbttlD6jp5w== + "@develar/schema-utils@~2.1.0": version "2.1.0" resolved "https://registry.yarnpkg.com/@develar/schema-utils/-/schema-utils-2.1.0.tgz#eceb1695bfbed6f6bb84666d5d3abe5e1fd54e17"