2e87b2fd22
Naomi
comment websockets
increase slow mode time to 5 seconds
fix to prevent duplicate comments
update livestream details
fix channel
pin electron boom
fix rebase
prune unused icons
updating meme
updating meme
update livestream for naomi
fix rebase
DigitalCashNetwork
remove electroboom pin
Slavguns
Joel
So he can edit his claims
add streamTypes param to claimTilesDiscover so following section can search for all types of content
fix typo
update meme
fixes
publish page fixes
pending
fix notifications
fix comments finally
fix claim preview
no mature for simplesite
Revert "no mature for simplesite"
This reverts commit 9f89242d85
.
fix livestream preview click
no mature on simple site
try fixing invite page crash
probably needs more changes.
338 lines
10 KiB
JavaScript
338 lines
10 KiB
JavaScript
// @flow
|
|
import { Lbryio } from 'lbryinc';
|
|
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'
|
|
import Native from 'native';
|
|
import ElectronCookies from '@exponent/electron-cookies';
|
|
import { generateInitialUrl } from 'util/url';
|
|
// @endif
|
|
import { MATOMO_ID, MATOMO_URL, LBRY_WEB_BUFFER_API } from 'config';
|
|
|
|
const isProduction = process.env.NODE_ENV === 'production';
|
|
const devInternalApis = process.env.LBRY_API_URL;
|
|
|
|
export const SHARE_INTERNAL = 'shareInternal';
|
|
const SHARE_THIRD_PARTY = 'shareThirdParty';
|
|
|
|
// @if TARGET='app'
|
|
if (isProduction) {
|
|
ElectronCookies.enable({
|
|
origin: 'https://lbry.tv',
|
|
});
|
|
}
|
|
// @endif
|
|
|
|
type Analytics = {
|
|
error: (string) => Promise<any>,
|
|
sentryError: ({} | string, {}) => Promise<any>,
|
|
pageView: (string, ?string) => void,
|
|
setUser: (Object) => void,
|
|
toggleInternal: (boolean, ?boolean) => void,
|
|
apiLogView: (string, string, string, ?number, ?() => void) => Promise<any>,
|
|
apiLogPublish: (ChannelClaim | StreamClaim) => void,
|
|
apiSyncTags: ({}) => void,
|
|
tagFollowEvent: (string, boolean, ?string) => void,
|
|
playerLoadedEvent: (?boolean) => void,
|
|
playerStartedEvent: (?boolean) => void,
|
|
videoFetchDuration: (string, number) => void,
|
|
videoStartEvent: (string, number) => void,
|
|
adsFetchedEvent: () => void,
|
|
adsReceivedEvent: (any) => void,
|
|
adsErrorEvent: (any) => void,
|
|
videoBufferEvent: (
|
|
StreamClaim,
|
|
{
|
|
timeAtBuffer: number,
|
|
bufferDuration: number,
|
|
bitRate: number,
|
|
duration: number,
|
|
userId: string,
|
|
playerPoweredBy: string,
|
|
readyState: number,
|
|
}
|
|
) => void,
|
|
emailProvidedEvent: () => void,
|
|
emailVerifiedEvent: () => void,
|
|
rewardEligibleEvent: () => void,
|
|
startupEvent: () => void,
|
|
purchaseEvent: (number) => void,
|
|
readyEvent: (number) => void,
|
|
openUrlEvent: (string) => void,
|
|
};
|
|
|
|
type LogPublishParams = {
|
|
uri: string,
|
|
claim_id: string,
|
|
outpoint: string,
|
|
channel_claim_id?: string,
|
|
};
|
|
|
|
let internalAnalyticsEnabled: boolean = IS_WEB || false;
|
|
// let thirdPartyAnalyticsEnabled: boolean = IS_WEB || false;
|
|
// @if TARGET='app'
|
|
if (window.localStorage.getItem(SHARE_INTERNAL) === 'true') internalAnalyticsEnabled = true;
|
|
// if (window.localStorage.getItem(SHARE_THIRD_PARTY) === 'true') thirdPartyAnalyticsEnabled = true;
|
|
// @endif
|
|
|
|
const analytics: Analytics = {
|
|
error: (message) => {
|
|
return new Promise((resolve) => {
|
|
if (internalAnalyticsEnabled && isProduction) {
|
|
return Lbryio.call('event', 'desktop_error', { error_message: message }).then(() => {
|
|
resolve(true);
|
|
});
|
|
} else {
|
|
resolve(false);
|
|
}
|
|
});
|
|
},
|
|
sentryError: (error, errorInfo) => {
|
|
return new Promise((resolve) => {
|
|
if (internalAnalyticsEnabled && isProduction) {
|
|
Sentry.withScope((scope) => {
|
|
scope.setExtras(errorInfo);
|
|
const eventId = Sentry.captureException(error);
|
|
resolve(eventId);
|
|
});
|
|
} else {
|
|
resolve(null);
|
|
}
|
|
});
|
|
},
|
|
pageView: (path, search) => {
|
|
if (internalAnalyticsEnabled) {
|
|
const params: { href: string, customDimensions?: Array<{ id: number, value: ?string }> } = { href: `${path}` };
|
|
const dimensions = [];
|
|
const searchParams = search && new URLSearchParams(search);
|
|
|
|
if (searchParams && searchParams.get('src')) {
|
|
dimensions.push({ id: 1, value: searchParams.get('src') });
|
|
}
|
|
if (dimensions.length) {
|
|
params['customDimensions'] = dimensions;
|
|
}
|
|
MatomoInstance.trackPageView(params);
|
|
}
|
|
},
|
|
setUser: (userId) => {
|
|
if (internalAnalyticsEnabled && userId) {
|
|
window._paq.push(['setUserId', String(userId)]);
|
|
// @if TARGET='app'
|
|
Native.getAppVersionInfo().then(({ localVersion }) => {
|
|
sendMatomoEvent('Version', 'Desktop-Version', localVersion);
|
|
});
|
|
// @endif
|
|
}
|
|
},
|
|
toggleInternal: (enabled: boolean): void => {
|
|
// Always collect analytics on lbry.tv
|
|
// @if TARGET='app'
|
|
internalAnalyticsEnabled = enabled;
|
|
window.localStorage.setItem(SHARE_INTERNAL, enabled);
|
|
// @endif
|
|
},
|
|
|
|
toggleThirdParty: (enabled: boolean): void => {
|
|
// Always collect analytics on lbry.tv
|
|
// @if TARGET='app'
|
|
// thirdPartyAnalyticsEnabled = enabled;
|
|
window.localStorage.setItem(SHARE_THIRD_PARTY, enabled);
|
|
// @endif
|
|
},
|
|
|
|
apiLogView: (uri, outpoint, claimId, timeToStart) => {
|
|
return new Promise((resolve, reject) => {
|
|
if (internalAnalyticsEnabled && (isProduction || devInternalApis)) {
|
|
const params: {
|
|
uri: string,
|
|
outpoint: string,
|
|
claim_id: string,
|
|
time_to_start?: number,
|
|
} = {
|
|
uri,
|
|
outpoint,
|
|
claim_id: claimId,
|
|
};
|
|
|
|
// lbry.tv streams from AWS so we don't care about the time to start
|
|
if (timeToStart && !IS_WEB) {
|
|
params.time_to_start = timeToStart;
|
|
}
|
|
|
|
resolve(Lbryio.call('file', 'view', params));
|
|
} else {
|
|
resolve();
|
|
}
|
|
});
|
|
},
|
|
apiLogSearch: () => {
|
|
if (internalAnalyticsEnabled && isProduction) {
|
|
Lbryio.call('event', 'search');
|
|
}
|
|
},
|
|
apiLogPublish: (claimResult: ChannelClaim | StreamClaim) => {
|
|
// Don't check if this is production so channels created on localhost are still linked to user
|
|
if (internalAnalyticsEnabled) {
|
|
const { permanent_url: uri, claim_id: claimId, txid, nout, signing_channel: signingChannel } = claimResult;
|
|
let channelClaimId;
|
|
if (signingChannel) {
|
|
channelClaimId = signingChannel.claim_id;
|
|
}
|
|
const outpoint = `${txid}:${nout}`;
|
|
const params: LogPublishParams = { uri, claim_id: claimId, outpoint };
|
|
if (channelClaimId) {
|
|
params['channel_claim_id'] = channelClaimId;
|
|
}
|
|
|
|
Lbryio.call('event', 'publish', params);
|
|
}
|
|
},
|
|
|
|
apiSyncTags: (params) => {
|
|
if (internalAnalyticsEnabled && isProduction) {
|
|
Lbryio.call('content_tags', 'sync', params);
|
|
}
|
|
},
|
|
|
|
apiSearchFeedback: (query, vote) => {
|
|
if (isProduction) {
|
|
// We don't need to worry about analytics enabled here because users manually click on the button to provide feedback
|
|
Lbryio.call('feedback', 'search', { query, vote });
|
|
}
|
|
},
|
|
|
|
videoFetchDuration: (source, duration) => {
|
|
sendPromMetric('time_to_fetch', duration);
|
|
sendMatomoEvent('Media', 'TimeToFetch', source, duration);
|
|
},
|
|
|
|
videoStartEvent: (claimId, duration) => {
|
|
sendPromMetric('time_to_start', duration);
|
|
sendMatomoEvent('Media', 'TimeToStart', claimId, duration);
|
|
},
|
|
|
|
videoBufferEvent: (claim, data) => {
|
|
sendMatomoEvent('Media', 'BufferTimestamp', claim.claim_id, data.timeAtBuffer);
|
|
|
|
if (LBRY_WEB_BUFFER_API) {
|
|
fetch(LBRY_WEB_BUFFER_API, {
|
|
method: 'post',
|
|
headers: {
|
|
Accept: 'application/json',
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
device: 'web',
|
|
type: 'buffering',
|
|
client: data.userId,
|
|
data: {
|
|
url: claim.canonical_url,
|
|
position: data.timeAtBuffer,
|
|
duration: data.bufferDuration,
|
|
player: data.playerPoweredBy,
|
|
readyState: data.readyState,
|
|
stream_duration: data.duration,
|
|
stream_bitrate: data.bitRate,
|
|
},
|
|
}),
|
|
});
|
|
}
|
|
},
|
|
playerLoadedEvent: (embedded) => {
|
|
sendMatomoEvent('Player', 'Loaded', embedded ? 'embedded' : 'onsite');
|
|
},
|
|
playerStartedEvent: (embedded) => {
|
|
sendMatomoEvent('Player', 'Started', embedded ? 'embedded' : 'onsite');
|
|
},
|
|
adsFetchedEvent: () => {
|
|
sendMatomoEvent('Media', 'AdsFetched');
|
|
},
|
|
adsReceivedEvent: (response) => {
|
|
sendMatomoEvent('Media', 'AdsReceived', JSON.stringify(response));
|
|
},
|
|
adsErrorEvent: (response) => {
|
|
sendMatomoEvent('Media', 'AdsError', JSON.stringify(response));
|
|
},
|
|
tagFollowEvent: (tag, following) => {
|
|
sendMatomoEvent('Tag', following ? 'Tag-Follow' : 'Tag-Unfollow', tag);
|
|
},
|
|
channelBlockEvent: (uri, blocked, location) => {
|
|
sendMatomoEvent(blocked ? 'Channel-Hidden' : 'Channel-Unhidden', uri);
|
|
},
|
|
emailProvidedEvent: () => {
|
|
sendMatomoEvent('Engagement', 'Email-Provided');
|
|
},
|
|
emailVerifiedEvent: () => {
|
|
sendMatomoEvent('Engagement', 'Email-Verified');
|
|
},
|
|
rewardEligibleEvent: () => {
|
|
sendMatomoEvent('Engagement', 'Reward-Eligible');
|
|
},
|
|
openUrlEvent: (url: string) => {
|
|
sendMatomoEvent('Engagement', 'Open-Url', url);
|
|
},
|
|
trendingAlgorithmEvent: (trendingAlgorithm: string) => {
|
|
sendMatomoEvent('Engagement', 'Trending-Algorithm', trendingAlgorithm);
|
|
},
|
|
startupEvent: () => {
|
|
sendMatomoEvent('Startup', 'Startup');
|
|
},
|
|
readyEvent: (timeToReady: number) => {
|
|
sendMatomoEvent('Startup', 'App-Ready', 'Time', timeToReady);
|
|
},
|
|
purchaseEvent: (purchaseInt: number) => {
|
|
sendMatomoEvent('Purchase', 'Purchase-Complete', 'someLabel', purchaseInt);
|
|
},
|
|
};
|
|
|
|
function sendMatomoEvent(category, action, name, value) {
|
|
if (internalAnalyticsEnabled) {
|
|
const event = { category, action, name, value };
|
|
MatomoInstance.trackEvent(event);
|
|
}
|
|
}
|
|
|
|
function sendPromMetric(name: string, value?: number) {
|
|
if (IS_WEB) {
|
|
let url = new URL(SDK_API_PATH + '/metric/ui');
|
|
const params = { name: name, value: value ? value.toString() : '' };
|
|
url.search = new URLSearchParams(params).toString();
|
|
return fetch(url, { method: 'post' });
|
|
}
|
|
}
|
|
|
|
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
|
|
});
|
|
|
|
// Manually call the first page view
|
|
// React Router doesn't include this on `history.listen`
|
|
// @if TARGET='web'
|
|
analytics.pageView(window.location.pathname + window.location.search, window.location.search);
|
|
// @endif
|
|
|
|
// @if TARGET='app'
|
|
analytics.pageView(
|
|
window.location.pathname.split('.html')[1] + window.location.search || generateInitialUrl(window.location.hash)
|
|
);
|
|
// @endif;
|
|
|
|
// Listen for url changes and report
|
|
// This will include search queries
|
|
history.listen((location) => {
|
|
const { pathname, search } = location;
|
|
|
|
const page = `${pathname}${search}`;
|
|
analytics.pageView(page, search);
|
|
});
|
|
|
|
export default analytics;
|