Merge branch 'master' of github.com:lbryio/lbry-desktop into issue/7152
This commit is contained in:
commit
172d4d35eb
20 changed files with 355 additions and 48 deletions
|
@ -43,6 +43,8 @@ module.name_mapper='^web\/page\(.*\)$' -> '<PROJECT_ROOT>/web/page\1'
|
||||||
module.name_mapper='^homepage\(.*\)$' -> '<PROJECT_ROOT>/ui/util/homepage\1'
|
module.name_mapper='^homepage\(.*\)$' -> '<PROJECT_ROOT>/ui/util/homepage\1'
|
||||||
module.name_mapper='^scss\/component\(.*\)$' -> '<PROJECT_ROOT>/ui/scss/component/\1'
|
module.name_mapper='^scss\/component\(.*\)$' -> '<PROJECT_ROOT>/ui/scss/component/\1'
|
||||||
|
|
||||||
|
esproposal.optional_chaining=enable
|
||||||
|
|
||||||
; Extensions
|
; Extensions
|
||||||
module.file_ext=.js
|
module.file_ext=.js
|
||||||
module.file_ext=.jsx
|
module.file_ext=.jsx
|
||||||
|
@ -51,4 +53,5 @@ module.file_ext=.css
|
||||||
module.file_ext=.scss
|
module.file_ext=.scss
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[strict]
|
[strict]
|
||||||
|
|
78
flow-typed/File.js
vendored
Normal file
78
flow-typed/File.js
vendored
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
declare type FileListItem = {
|
||||||
|
metadata: StreamMetadata,
|
||||||
|
added_on: number,
|
||||||
|
blobs_completed: number,
|
||||||
|
blobs_in_stream: number,
|
||||||
|
blobs_remaining: number,
|
||||||
|
channel_claim_id: string,
|
||||||
|
channel_name: string,
|
||||||
|
claim_id: string,
|
||||||
|
claim_name: string,
|
||||||
|
completed: false,
|
||||||
|
content_fee?: { txid: string },
|
||||||
|
purchase_receipt?: { txid: string, amount: string },
|
||||||
|
download_directory: string,
|
||||||
|
download_path: string,
|
||||||
|
file_name: string,
|
||||||
|
key: string,
|
||||||
|
mime_type: string,
|
||||||
|
nout: number,
|
||||||
|
outpoint: string,
|
||||||
|
points_paid: number,
|
||||||
|
protobuf: string,
|
||||||
|
reflector_progress: number,
|
||||||
|
sd_hash: string,
|
||||||
|
status: string,
|
||||||
|
stopped: false,
|
||||||
|
stream_hash: string,
|
||||||
|
stream_name: string,
|
||||||
|
streaming_url: string,
|
||||||
|
suggested_file_name: string,
|
||||||
|
total_bytes: number,
|
||||||
|
total_bytes_lower_bound: number,
|
||||||
|
is_fully_reflected: boolean,
|
||||||
|
// TODO: sdk plans to change `tx`
|
||||||
|
// It isn't currently used by the apps
|
||||||
|
tx: {},
|
||||||
|
txid: string,
|
||||||
|
uploading_to_reflector: boolean,
|
||||||
|
written_bytes: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type FileState = {
|
||||||
|
failedPurchaseUris: Array<string>,
|
||||||
|
purchasedUris: Array<string>,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type PurchaseUriCompleted = {
|
||||||
|
type: ACTIONS.PURCHASE_URI_COMPLETED,
|
||||||
|
data: {
|
||||||
|
uri: string,
|
||||||
|
streamingUrl: string,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type PurchaseUriFailed = {
|
||||||
|
type: ACTIONS.PURCHASE_URI_FAILED,
|
||||||
|
data: {
|
||||||
|
uri: string,
|
||||||
|
error: any,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type PurchaseUriStarted = {
|
||||||
|
type: ACTIONS.PURCHASE_URI_STARTED,
|
||||||
|
data: {
|
||||||
|
uri: string,
|
||||||
|
streamingUrl: string,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type DeletePurchasedUri = {
|
||||||
|
type: ACTIONS.CLEAR_PURCHASED_URI_SUCCESS,
|
||||||
|
data: {
|
||||||
|
uri: string,
|
||||||
|
},
|
||||||
|
};
|
|
@ -75,6 +75,7 @@
|
||||||
"@babel/plugin-proposal-class-properties": "^7.0.0",
|
"@babel/plugin-proposal-class-properties": "^7.0.0",
|
||||||
"@babel/plugin-proposal-decorators": "^7.3.0",
|
"@babel/plugin-proposal-decorators": "^7.3.0",
|
||||||
"@babel/plugin-proposal-object-rest-spread": "^7.6.2",
|
"@babel/plugin-proposal-object-rest-spread": "^7.6.2",
|
||||||
|
"@babel/plugin-proposal-optional-chaining": "^7.14.5",
|
||||||
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
||||||
"@babel/plugin-transform-flow-strip-types": "^7.2.3",
|
"@babel/plugin-transform-flow-strip-types": "^7.2.3",
|
||||||
"@babel/plugin-transform-runtime": "^7.4.3",
|
"@babel/plugin-transform-runtime": "^7.4.3",
|
||||||
|
|
|
@ -1638,6 +1638,7 @@
|
||||||
"This link leads to an external website.": "This link leads to an external website.",
|
"This link leads to an external website.": "This link leads to an external website.",
|
||||||
"No Content Found": "No Content Found",
|
"No Content Found": "No Content Found",
|
||||||
"No Lists Found": "No Lists Found",
|
"No Lists Found": "No Lists Found",
|
||||||
|
"No matching playlists": "No matching playlists",
|
||||||
"You have no lists! Create one from any playable content.": "You have no lists! Create one from any playable content.",
|
"You have no lists! Create one from any playable content.": "You have no lists! Create one from any playable content.",
|
||||||
"Pick": "Pick",
|
"Pick": "Pick",
|
||||||
"You have unpublished lists! %pick% one and publish it!": "You have unpublished lists! %pick% one and publish it!",
|
"You have unpublished lists! %pick% one and publish it!": "You have unpublished lists! %pick% one and publish it!",
|
||||||
|
@ -2058,6 +2059,8 @@
|
||||||
"MyAwesomeList": "MyAwesomeList",
|
"MyAwesomeList": "MyAwesomeList",
|
||||||
"My Awesome List": "My Awesome List",
|
"My Awesome List": "My Awesome List",
|
||||||
"This list has no items.": "This list has no items.",
|
"This list has no items.": "This list has no items.",
|
||||||
|
"1 item": "1 item",
|
||||||
|
"%collectionCount% items": "%collectionCount% items",
|
||||||
"Select File": "Select File",
|
"Select File": "Select File",
|
||||||
"File Selected": "File Selected",
|
"File Selected": "File Selected",
|
||||||
"Url": "Url",
|
"Url": "Url",
|
||||||
|
@ -2190,5 +2193,6 @@
|
||||||
"Creator": "Creator",
|
"Creator": "Creator",
|
||||||
"From comments": "From comments",
|
"From comments": "From comments",
|
||||||
"From search": "From search",
|
"From search": "From search",
|
||||||
|
"Manage tags": "Manage tags",
|
||||||
"--end--": "--end--"
|
"--end--": "--end--"
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,15 @@ import ElectronCookies from '@exponent/electron-cookies';
|
||||||
import { generateInitialUrl } from 'util/url';
|
import { generateInitialUrl } from 'util/url';
|
||||||
// @endif
|
// @endif
|
||||||
import { MATOMO_ID, MATOMO_URL } from 'config';
|
import { MATOMO_ID, MATOMO_URL } from 'config';
|
||||||
|
// import getConnectionSpeed from 'util/detect-user-bandwidth';
|
||||||
|
|
||||||
|
// let userDownloadBandwidthInBitsPerSecond;
|
||||||
|
// async function getUserBandwidth() {
|
||||||
|
// userDownloadBandwidthInBitsPerSecond = await getConnectionSpeed();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// get user bandwidth every minute, starting after an initial one minute wait
|
||||||
|
// setInterval(getUserBandwidth, 1000 * 60);
|
||||||
|
|
||||||
const isProduction = process.env.NODE_ENV === 'production';
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
const devInternalApis = process.env.LBRY_API_URL && process.env.LBRY_API_URL.includes('dev');
|
const devInternalApis = process.env.LBRY_API_URL && process.env.LBRY_API_URL.includes('dev');
|
||||||
|
@ -40,7 +49,7 @@ type Analytics = {
|
||||||
tagFollowEvent: (string, boolean, ?string) => void,
|
tagFollowEvent: (string, boolean, ?string) => void,
|
||||||
playerLoadedEvent: (?boolean) => void,
|
playerLoadedEvent: (?boolean) => void,
|
||||||
playerStartedEvent: (?boolean) => void,
|
playerStartedEvent: (?boolean) => void,
|
||||||
videoStartEvent: (string, number, string, number, string, any) => void,
|
videoStartEvent: (string, number, string, number, string, any, number) => void,
|
||||||
videoIsPlaying: (boolean, any) => void,
|
videoIsPlaying: (boolean, any) => void,
|
||||||
videoBufferEvent: (
|
videoBufferEvent: (
|
||||||
StreamClaim,
|
StreamClaim,
|
||||||
|
@ -111,7 +120,7 @@ function getDeviceType() {
|
||||||
// variables initialized for watchman
|
// variables initialized for watchman
|
||||||
let amountOfBufferEvents = 0;
|
let amountOfBufferEvents = 0;
|
||||||
let amountOfBufferTimeInMS = 0;
|
let amountOfBufferTimeInMS = 0;
|
||||||
let videoType, userId, claimUrl, playerPoweredBy, videoPlayer;
|
let videoType, userId, claimUrl, playerPoweredBy, videoPlayer, bitrateAsBitsPerSecond;
|
||||||
let lastSentTime;
|
let lastSentTime;
|
||||||
|
|
||||||
// calculate data for backend, send them, and reset buffer data for next interval
|
// calculate data for backend, send them, and reset buffer data for next interval
|
||||||
|
@ -130,6 +139,9 @@ async function sendAndResetWatchmanData() {
|
||||||
let protocol;
|
let protocol;
|
||||||
if (videoType === 'application/x-mpegURL') {
|
if (videoType === 'application/x-mpegURL') {
|
||||||
protocol = 'hls';
|
protocol = 'hls';
|
||||||
|
// get bandwidth if it exists from the texttrack (so it's accurate if user changes quality)
|
||||||
|
// $FlowFixMe
|
||||||
|
bitrateAsBitsPerSecond = videoPlayer.textTracks?.().tracks_[0]?.activeCues[0]?.value?.bandwidth;
|
||||||
} else {
|
} else {
|
||||||
protocol = 'stb';
|
protocol = 'stb';
|
||||||
}
|
}
|
||||||
|
@ -152,6 +164,9 @@ async function sendAndResetWatchmanData() {
|
||||||
user_id: userId.toString(),
|
user_id: userId.toString(),
|
||||||
position: Math.round(positionInVideo),
|
position: Math.round(positionInVideo),
|
||||||
rel_position: Math.round((positionInVideo / (totalDurationInSeconds * 1000)) * 100),
|
rel_position: Math.round((positionInVideo / (totalDurationInSeconds * 1000)) * 100),
|
||||||
|
bitrate: bitrateAsBitsPerSecond,
|
||||||
|
bandwidth: undefined,
|
||||||
|
// ...(userDownloadBandwidthInBitsPerSecond && {bandwidth: userDownloadBandwidthInBitsPerSecond}), // add bandwidth if populated
|
||||||
};
|
};
|
||||||
|
|
||||||
// post to watchman
|
// post to watchman
|
||||||
|
@ -202,7 +217,7 @@ async function sendWatchmanData(body) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const analytics: Analytics = {
|
const analytics: Analytics = {
|
||||||
// receive buffer events from tracking plugin and jklj
|
// receive buffer events from tracking plugin and save buffer amounts and times for backend call
|
||||||
videoBufferEvent: async (claim, data) => {
|
videoBufferEvent: async (claim, data) => {
|
||||||
amountOfBufferEvents = amountOfBufferEvents + 1;
|
amountOfBufferEvents = amountOfBufferEvents + 1;
|
||||||
amountOfBufferTimeInMS = amountOfBufferTimeInMS + data.bufferDuration;
|
amountOfBufferTimeInMS = amountOfBufferTimeInMS + data.bufferDuration;
|
||||||
|
@ -240,7 +255,7 @@ const analytics: Analytics = {
|
||||||
startWatchmanIntervalIfNotRunning();
|
startWatchmanIntervalIfNotRunning();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
videoStartEvent: (claimId, duration, poweredBy, passedUserId, canonicalUrl, passedPlayer) => {
|
videoStartEvent: (claimId, duration, poweredBy, passedUserId, canonicalUrl, passedPlayer, videoBitrate) => {
|
||||||
// populate values for watchman when video starts
|
// populate values for watchman when video starts
|
||||||
userId = passedUserId;
|
userId = passedUserId;
|
||||||
claimUrl = canonicalUrl;
|
claimUrl = canonicalUrl;
|
||||||
|
@ -248,6 +263,7 @@ const analytics: Analytics = {
|
||||||
|
|
||||||
videoType = passedPlayer.currentSource().type;
|
videoType = passedPlayer.currentSource().type;
|
||||||
videoPlayer = passedPlayer;
|
videoPlayer = passedPlayer;
|
||||||
|
bitrateAsBitsPerSecond = videoBitrate;
|
||||||
|
|
||||||
sendPromMetric('time_to_start', duration);
|
sendPromMetric('time_to_start', duration);
|
||||||
sendMatomoEvent('Media', 'TimeToStart', claimId, duration);
|
sendMatomoEvent('Media', 'TimeToStart', claimId, duration);
|
||||||
|
|
|
@ -6,7 +6,6 @@ import {
|
||||||
makeSelectClaimIsMine,
|
makeSelectClaimIsMine,
|
||||||
makeSelectClaimIsPending,
|
makeSelectClaimIsPending,
|
||||||
makeSelectClaimIsNsfw,
|
makeSelectClaimIsNsfw,
|
||||||
doFileGet,
|
|
||||||
makeSelectReflectingClaimForUri,
|
makeSelectReflectingClaimForUri,
|
||||||
makeSelectClaimWasPurchased,
|
makeSelectClaimWasPurchased,
|
||||||
makeSelectStreamingUrlForUri,
|
makeSelectStreamingUrlForUri,
|
||||||
|
@ -25,7 +24,7 @@ import { selectShowMatureContent } from 'redux/selectors/settings';
|
||||||
import { makeSelectHasVisitedUri } from 'redux/selectors/content';
|
import { makeSelectHasVisitedUri } from 'redux/selectors/content';
|
||||||
import { makeSelectIsSubscribed } from 'redux/selectors/subscriptions';
|
import { makeSelectIsSubscribed } from 'redux/selectors/subscriptions';
|
||||||
import { selectModerationBlockList } from 'redux/selectors/comments';
|
import { selectModerationBlockList } from 'redux/selectors/comments';
|
||||||
|
import { doFileGet } from 'redux/actions/file';
|
||||||
import ClaimPreview from './view';
|
import ClaimPreview from './view';
|
||||||
import formatMediaDuration from 'util/formatMediaDuration';
|
import formatMediaDuration from 'util/formatMediaDuration';
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ import {
|
||||||
makeSelectIsUriResolving,
|
makeSelectIsUriResolving,
|
||||||
makeSelectThumbnailForUri,
|
makeSelectThumbnailForUri,
|
||||||
makeSelectTitleForUri,
|
makeSelectTitleForUri,
|
||||||
doFileGet,
|
|
||||||
makeSelectChannelForClaimUri,
|
makeSelectChannelForClaimUri,
|
||||||
makeSelectClaimIsNsfw,
|
makeSelectClaimIsNsfw,
|
||||||
makeSelectClaimIsStreamPlaceholder,
|
makeSelectClaimIsStreamPlaceholder,
|
||||||
|
@ -14,6 +13,7 @@ import {
|
||||||
import { selectMutedChannels } from 'redux/selectors/blocked';
|
import { selectMutedChannels } from 'redux/selectors/blocked';
|
||||||
import { makeSelectViewCountForUri, selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc';
|
import { makeSelectViewCountForUri, selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc';
|
||||||
import { makeSelectIsActiveLivestream } from 'redux/selectors/livestream';
|
import { makeSelectIsActiveLivestream } from 'redux/selectors/livestream';
|
||||||
|
import { doFileGet } from 'redux/actions/file';
|
||||||
import { selectShowMatureContent } from 'redux/selectors/settings';
|
import { selectShowMatureContent } from 'redux/selectors/settings';
|
||||||
import ClaimPreviewTile from './view';
|
import ClaimPreviewTile from './view';
|
||||||
import formatMediaDuration from 'util/formatMediaDuration';
|
import formatMediaDuration from 'util/formatMediaDuration';
|
||||||
|
|
|
@ -65,7 +65,7 @@ export default function CollectionContent(props: Props) {
|
||||||
titleActions={
|
titleActions={
|
||||||
<div className="card__title-actions--link">
|
<div className="card__title-actions--link">
|
||||||
{/* TODO: BUTTON TO SAVE COLLECTION - Probably save/copy modal */}
|
{/* TODO: BUTTON TO SAVE COLLECTION - Probably save/copy modal */}
|
||||||
<Button label={'View List'} button="link" navigate={`/$/${PAGES.LIST}/${id}`} />
|
<Button label={__('View List')} button="link" navigate={`/$/${PAGES.LIST}/${id}`} />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
body={
|
body={
|
||||||
|
|
|
@ -175,7 +175,7 @@ export default function CollectionsListMine(props: Props) {
|
||||||
{filteredCollections &&
|
{filteredCollections &&
|
||||||
filteredCollections.length > 0 &&
|
filteredCollections.length > 0 &&
|
||||||
filteredCollections.map((key) => <CollectionPreviewTile tileLayout collectionId={key} key={key} />)}
|
filteredCollections.map((key) => <CollectionPreviewTile tileLayout collectionId={key} key={key} />)}
|
||||||
{!filteredCollections.length && <div className="empty main--empty">{__('No matching collections')}</div>}
|
{!filteredCollections.length && <div className="empty main--empty">{__('No matching playlists')}</div>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { selectSubscriptions } from 'redux/selectors/subscriptions';
|
import { selectSubscriptions } from 'redux/selectors/subscriptions';
|
||||||
import { selectPurchaseUriSuccess, doClearPurchasedUriSuccess } from 'lbry-redux';
|
import { selectPurchaseUriSuccess } from 'lbry-redux';
|
||||||
import { selectFollowedTags } from 'redux/selectors/tags';
|
import { selectFollowedTags } from 'redux/selectors/tags';
|
||||||
import { selectUserVerifiedEmail, selectUser } from 'redux/selectors/user';
|
import { selectUserVerifiedEmail, selectUser } from 'redux/selectors/user';
|
||||||
import { selectHomepageData, selectLanguage } from 'redux/selectors/settings';
|
import { selectHomepageData, selectLanguage } from 'redux/selectors/settings';
|
||||||
import { doSignOut } from 'redux/actions/app';
|
import { doSignOut } from 'redux/actions/app';
|
||||||
|
import { doClearPurchasedUriSuccess } from 'redux/actions/file';
|
||||||
|
|
||||||
import { selectUnseenNotificationCount } from 'redux/selectors/notifications';
|
import { selectUnseenNotificationCount } from 'redux/selectors/notifications';
|
||||||
|
|
||||||
import SideNavigation from './view';
|
import SideNavigation from './view';
|
||||||
|
|
|
@ -168,9 +168,17 @@ function VideoViewer(props: Props) {
|
||||||
}
|
}
|
||||||
analytics.playerStartedEvent(embedded);
|
analytics.playerStartedEvent(embedded);
|
||||||
|
|
||||||
|
// convert bytes to bits, and then divide by seconds
|
||||||
|
const contentInBits = Number(claim.value.source.size) * 8;
|
||||||
|
const durationInSeconds = claim.value.video && claim.value.video.duration;
|
||||||
|
let bitrateAsBitsPerSecond;
|
||||||
|
if (durationInSeconds) {
|
||||||
|
bitrateAsBitsPerSecond = Math.round(contentInBits / durationInSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
fetch(source, { method: 'HEAD', cache: 'no-store' }).then((response) => {
|
fetch(source, { method: 'HEAD', cache: 'no-store' }).then((response) => {
|
||||||
let playerPoweredBy = response.headers.get('x-powered-by') || '';
|
let playerPoweredBy = response.headers.get('x-powered-by') || '';
|
||||||
analytics.videoStartEvent(claimId, timeToStart, playerPoweredBy, userId, claim.canonical_url, this);
|
analytics.videoStartEvent(claimId, timeToStart, playerPoweredBy, userId, claim.canonical_url, this, bitrateAsBitsPerSecond);
|
||||||
});
|
});
|
||||||
|
|
||||||
doAnalyticsView(uri, timeToStart).then(() => {
|
doAnalyticsView(uri, timeToStart).then(() => {
|
||||||
|
|
|
@ -120,6 +120,13 @@ export const PLAY_VIDEO_STARTED = 'PLAY_VIDEO_STARTED';
|
||||||
export const FETCH_AVAILABILITY_STARTED = 'FETCH_AVAILABILITY_STARTED';
|
export const FETCH_AVAILABILITY_STARTED = 'FETCH_AVAILABILITY_STARTED';
|
||||||
export const FETCH_AVAILABILITY_COMPLETED = 'FETCH_AVAILABILITY_COMPLETED';
|
export const FETCH_AVAILABILITY_COMPLETED = 'FETCH_AVAILABILITY_COMPLETED';
|
||||||
export const FILE_DELETE = 'FILE_DELETE';
|
export const FILE_DELETE = 'FILE_DELETE';
|
||||||
|
export const FETCH_FILE_INFO_FAILED = 'FETCH_FILE_INFO_FAILED';
|
||||||
|
export const DOWNLOADING_CANCELED = 'DOWNLOADING_CANCELED';
|
||||||
|
export const SET_FILE_LIST_SORT = 'SET_FILE_LIST_SORT';
|
||||||
|
export const PURCHASE_URI_STARTED = 'PURCHASE_URI_STARTED';
|
||||||
|
export const PURCHASE_URI_COMPLETED = 'PURCHASE_URI_COMPLETED';
|
||||||
|
export const PURCHASE_URI_FAILED = 'PURCHASE_URI_FAILED';
|
||||||
|
export const CLEAR_PURCHASED_URI_SUCCESS = 'CLEAR_PURCHASED_URI_SUCCESS';
|
||||||
|
|
||||||
// Search
|
// Search
|
||||||
export const SEARCH_START = 'SEARCH_START';
|
export const SEARCH_START = 'SEARCH_START';
|
||||||
|
|
|
@ -102,7 +102,9 @@ export default function CollectionPage(props: Props) {
|
||||||
|
|
||||||
const subTitle = (
|
const subTitle = (
|
||||||
<div>
|
<div>
|
||||||
<span className="collection__subtitle">{collectionCount} items</span>
|
<span className="collection__subtitle">
|
||||||
|
{collectionCount === 1 ? __('1 item') : __('%collectionCount% items', { collectionCount })}
|
||||||
|
</span>
|
||||||
{uri && <ClaimAuthor uri={uri} />}
|
{uri && <ClaimAuthor uri={uri} />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -32,28 +32,30 @@ export default function NotificationSettingsPage(props: Props) {
|
||||||
const lbryIoParams = verificationToken ? { auth_token: verificationToken } : undefined;
|
const lbryIoParams = verificationToken ? { auth_token: verificationToken } : undefined;
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
Lbryio.call('tag', 'list', lbryIoParams)
|
if (isAuthenticated) {
|
||||||
.then(setTags)
|
Lbryio.call('tag', 'list', lbryIoParams)
|
||||||
.catch((e) => {
|
.then(setTags)
|
||||||
setError(true);
|
.catch((e) => {
|
||||||
});
|
setError(true);
|
||||||
|
});
|
||||||
|
|
||||||
Lbryio.call('user_email', 'status', lbryIoParams)
|
Lbryio.call('user_email', 'status', lbryIoParams)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
const enabledEmails =
|
const enabledEmails =
|
||||||
res.emails &&
|
res.emails &&
|
||||||
Object.keys(res.emails).reduce((acc, email) => {
|
Object.keys(res.emails).reduce((acc, email) => {
|
||||||
const isEnabled = res.emails[email];
|
const isEnabled = res.emails[email];
|
||||||
return [...acc, { email, isEnabled }];
|
return [...acc, { email, isEnabled }];
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
setTagMap(res.tags);
|
setTagMap(res.tags);
|
||||||
setEnabledEmails(enabledEmails);
|
setEnabledEmails(enabledEmails);
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
setError(true);
|
setError(true);
|
||||||
});
|
});
|
||||||
}, []);
|
}
|
||||||
|
}, [isAuthenticated]);
|
||||||
|
|
||||||
function handleChangeTag(name, newIsEnabled) {
|
function handleChangeTag(name, newIsEnabled) {
|
||||||
const tagParams = newIsEnabled ? { add: name } : { remove: name };
|
const tagParams = newIsEnabled ? { add: name } : { remove: name };
|
||||||
|
|
|
@ -505,6 +505,7 @@ export function doAnalyticsBuffer(uri, bufferData) {
|
||||||
const fileSizeInBits = fileSize * 8;
|
const fileSizeInBits = fileSize * 8;
|
||||||
const bitRate = parseInt(fileSizeInBits / fileDurationInSeconds);
|
const bitRate = parseInt(fileSizeInBits / fileDurationInSeconds);
|
||||||
const userId = user && user.id.toString();
|
const userId = user && user.id.toString();
|
||||||
|
// if there's a logged in user, send buffer event data to watchman
|
||||||
if (userId) {
|
if (userId) {
|
||||||
analytics.videoBufferEvent(claim, {
|
analytics.videoBufferEvent(claim, {
|
||||||
timeAtBuffer,
|
timeAtBuffer,
|
||||||
|
|
|
@ -11,7 +11,6 @@ import {
|
||||||
SETTINGS,
|
SETTINGS,
|
||||||
makeSelectFileInfoForUri,
|
makeSelectFileInfoForUri,
|
||||||
selectFileInfosByOutpoint,
|
selectFileInfosByOutpoint,
|
||||||
doPurchaseUri,
|
|
||||||
makeSelectUriIsStreamable,
|
makeSelectUriIsStreamable,
|
||||||
selectDownloadingByOutpoint,
|
selectDownloadingByOutpoint,
|
||||||
makeSelectClaimForUri,
|
makeSelectClaimForUri,
|
||||||
|
@ -20,6 +19,7 @@ import {
|
||||||
doToast,
|
doToast,
|
||||||
makeSelectUrlsForCollectionId,
|
makeSelectUrlsForCollectionId,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
|
import { doPurchaseUri } from 'redux/actions/file';
|
||||||
import { makeSelectCostInfoForUri, Lbryio } from 'lbryinc';
|
import { makeSelectCostInfoForUri, Lbryio } from 'lbryinc';
|
||||||
import { makeSelectClientSetting, selectosNotificationsEnabled, selectDaemonSettings } from 'redux/selectors/settings';
|
import { makeSelectClientSetting, selectosNotificationsEnabled, selectDaemonSettings } from 'redux/selectors/settings';
|
||||||
|
|
||||||
|
@ -137,8 +137,8 @@ export function doSetPlayingUri({
|
||||||
uri: ?string,
|
uri: ?string,
|
||||||
source?: string,
|
source?: string,
|
||||||
commentId?: string,
|
commentId?: string,
|
||||||
pathname: string,
|
pathname?: string,
|
||||||
collectionId: string,
|
collectionId?: string,
|
||||||
}) {
|
}) {
|
||||||
return (dispatch: Dispatch) => {
|
return (dispatch: Dispatch) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
|
@ -160,7 +160,7 @@ export function doPurchaseUriWrapper(uri: string, cost: number, saveFile: boolea
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(doPurchaseUri(uri, { costInfo: cost }, saveFile, onSuccess));
|
dispatch(doPurchaseUri(uri, { cost }, saveFile, onSuccess));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @flow
|
||||||
import * as ACTIONS from 'constants/action_types';
|
import * as ACTIONS from 'constants/action_types';
|
||||||
// @if TARGET='app'
|
// @if TARGET='app'
|
||||||
import { shell } from 'electron';
|
import { shell } from 'electron';
|
||||||
|
@ -7,22 +8,28 @@ import {
|
||||||
batchActions,
|
batchActions,
|
||||||
doAbandonClaim,
|
doAbandonClaim,
|
||||||
makeSelectFileInfoForUri,
|
makeSelectFileInfoForUri,
|
||||||
|
selectDownloadingByOutpoint,
|
||||||
|
makeSelectStreamingUrlForUri,
|
||||||
makeSelectClaimForUri,
|
makeSelectClaimForUri,
|
||||||
|
selectBalance,
|
||||||
ABANDON_STATES,
|
ABANDON_STATES,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
import { doHideModal } from 'redux/actions/app';
|
import { doHideModal } from 'redux/actions/app';
|
||||||
import { goBack } from 'connected-react-router';
|
import { goBack } from 'connected-react-router';
|
||||||
import { doSetPlayingUri } from 'redux/actions/content';
|
import { doSetPlayingUri } from 'redux/actions/content';
|
||||||
import { selectPlayingUri } from 'redux/selectors/content';
|
import { selectPlayingUri } from 'redux/selectors/content';
|
||||||
|
import { doToast } from 'redux/actions/notifications';
|
||||||
|
type Dispatch = (action: any) => any;
|
||||||
|
type GetState = () => { file: FileState };
|
||||||
|
|
||||||
export function doOpenFileInFolder(path) {
|
export function doOpenFileInFolder(path: string) {
|
||||||
return () => {
|
return () => {
|
||||||
shell.showItemInFolder(path);
|
shell.showItemInFolder(path);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doOpenFileInShell(path) {
|
export function doOpenFileInShell(path: string) {
|
||||||
return (dispatch) => {
|
return (dispatch: Dispatch) => {
|
||||||
const success = shell.openPath(path);
|
const success = shell.openPath(path);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
dispatch(doOpenFileInFolder(path));
|
dispatch(doOpenFileInFolder(path));
|
||||||
|
@ -30,8 +37,8 @@ export function doOpenFileInShell(path) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doDeleteFile(outpoint, deleteFromComputer, abandonClaim, cb) {
|
export function doDeleteFile(outpoint: string, deleteFromComputer?: boolean, abandonClaim?: boolean, cb: any) {
|
||||||
return (dispatch) => {
|
return (dispatch: Dispatch) => {
|
||||||
if (abandonClaim) {
|
if (abandonClaim) {
|
||||||
const [txid, nout] = outpoint.split(':');
|
const [txid, nout] = outpoint.split(':');
|
||||||
dispatch(doAbandonClaim(txid, Number(nout), cb));
|
dispatch(doAbandonClaim(txid, Number(nout), cb));
|
||||||
|
@ -53,8 +60,13 @@ export function doDeleteFile(outpoint, deleteFromComputer, abandonClaim, cb) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doDeleteFileAndMaybeGoBack(uri, deleteFromComputer, abandonClaim, doGoBack) {
|
export function doDeleteFileAndMaybeGoBack(
|
||||||
return (dispatch, getState) => {
|
uri: string,
|
||||||
|
deleteFromComputer?: boolean,
|
||||||
|
abandonClaim?: boolean,
|
||||||
|
doGoBack: (any) => void
|
||||||
|
) {
|
||||||
|
return (dispatch: Dispatch, getState: GetState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const playingUri = selectPlayingUri(state);
|
const playingUri = selectPlayingUri(state);
|
||||||
const { outpoint } = makeSelectFileInfoForUri(uri)(state) || '';
|
const { outpoint } = makeSelectFileInfoForUri(uri)(state) || '';
|
||||||
|
@ -88,3 +100,121 @@ export function doDeleteFileAndMaybeGoBack(uri, deleteFromComputer, abandonClaim
|
||||||
dispatch(batchActions(...actions));
|
dispatch(batchActions(...actions));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function doFileGet(uri: string, saveFile: boolean = true, onSuccess?: (GetResponse) => any) {
|
||||||
|
return (dispatch: Dispatch, getState: () => any) => {
|
||||||
|
const state = getState();
|
||||||
|
const { nout, txid } = makeSelectClaimForUri(uri)(state);
|
||||||
|
const outpoint = `${txid}:${nout}`;
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.FETCH_FILE_INFO_STARTED,
|
||||||
|
data: {
|
||||||
|
outpoint,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// set save_file argument to True to save the file (old behaviour)
|
||||||
|
Lbry.get({ uri, save_file: saveFile })
|
||||||
|
.then((streamInfo: GetResponse) => {
|
||||||
|
const timeout = streamInfo === null || typeof streamInfo !== 'object' || streamInfo.error === 'Timeout';
|
||||||
|
if (timeout) {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.FETCH_FILE_INFO_FAILED,
|
||||||
|
data: { outpoint },
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch(doToast({ message: `File timeout for uri ${uri}`, isError: true }));
|
||||||
|
} else {
|
||||||
|
if (streamInfo.purchase_receipt || streamInfo.content_fee) {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.PURCHASE_URI_COMPLETED,
|
||||||
|
data: { uri, purchaseReceipt: streamInfo.purchase_receipt || streamInfo.content_fee },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.FETCH_FILE_INFO_COMPLETED,
|
||||||
|
data: {
|
||||||
|
fileInfo: streamInfo,
|
||||||
|
outpoint: outpoint,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (onSuccess) {
|
||||||
|
onSuccess(streamInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.PURCHASE_URI_FAILED,
|
||||||
|
data: { uri, error },
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.FETCH_FILE_INFO_FAILED,
|
||||||
|
data: { outpoint },
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch(
|
||||||
|
doToast({
|
||||||
|
message: `Failed to view ${uri}, please try again. If this problem persists, visit https://lbry.com/faq/support for support.`,
|
||||||
|
isError: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doPurchaseUri(
|
||||||
|
uri: string,
|
||||||
|
costInfo: { cost: number },
|
||||||
|
saveFile: boolean = true,
|
||||||
|
onSuccess?: (GetResponse) => any
|
||||||
|
) {
|
||||||
|
return (dispatch: Dispatch, getState: GetState) => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.PURCHASE_URI_STARTED,
|
||||||
|
data: { uri },
|
||||||
|
});
|
||||||
|
|
||||||
|
const state = getState();
|
||||||
|
const balance = selectBalance(state);
|
||||||
|
const fileInfo = makeSelectFileInfoForUri(uri)(state);
|
||||||
|
const downloadingByOutpoint = selectDownloadingByOutpoint(state);
|
||||||
|
const alreadyDownloading = fileInfo && !!downloadingByOutpoint[fileInfo.outpoint];
|
||||||
|
const alreadyStreaming = makeSelectStreamingUrlForUri(uri)(state);
|
||||||
|
|
||||||
|
if (!saveFile && (alreadyDownloading || alreadyStreaming)) {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.PURCHASE_URI_FAILED,
|
||||||
|
data: { uri, error: `Already fetching uri: ${uri}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (onSuccess) {
|
||||||
|
onSuccess(fileInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { cost } = costInfo;
|
||||||
|
if (parseFloat(cost) > balance) {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.PURCHASE_URI_FAILED,
|
||||||
|
data: { uri, error: 'Insufficient credits' },
|
||||||
|
});
|
||||||
|
|
||||||
|
Promise.resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(doFileGet(uri, saveFile, onSuccess));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doClearPurchasedUriSuccess() {
|
||||||
|
return {
|
||||||
|
type: ACTIONS.CLEAR_PURCHASED_URI_SUCCESS,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
20
ui/util/detect-user-bandwidth.js
Normal file
20
ui/util/detect-user-bandwidth.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
const imageAddr = 'https://upload.wikimedia.org/wikipedia/commons/b/b9/Pizigani_1367_Chart_1MB.jpg';
|
||||||
|
const downloadSize = 1093957; // this must match with the image above
|
||||||
|
|
||||||
|
let startTime, endTime;
|
||||||
|
async function measureConnectionSpeed() {
|
||||||
|
startTime = (new Date()).getTime();
|
||||||
|
const cacheBuster = '?nnn=' + startTime;
|
||||||
|
|
||||||
|
const download = new Image();
|
||||||
|
download.src = imageAddr + cacheBuster;
|
||||||
|
// this returns when the image is finished downloading
|
||||||
|
await download.decode();
|
||||||
|
endTime = (new Date()).getTime();
|
||||||
|
const duration = (endTime - startTime) / 1000;
|
||||||
|
const bitsLoaded = downloadSize * 8;
|
||||||
|
const speedBps = (bitsLoaded / duration).toFixed(2);
|
||||||
|
return Math.round(Number(speedBps));
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = measureConnectionSpeed;
|
|
@ -149,7 +149,7 @@ const webConfig = {
|
||||||
test: /\.jsx?$/,
|
test: /\.jsx?$/,
|
||||||
options: {
|
options: {
|
||||||
presets: ['@babel/env', '@babel/react', '@babel/flow'],
|
presets: ['@babel/env', '@babel/react', '@babel/flow'],
|
||||||
plugins: ['@babel/plugin-proposal-object-rest-spread', '@babel/plugin-proposal-class-properties'],
|
plugins: ['@babel/plugin-proposal-optional-chaining', '@babel/plugin-proposal-object-rest-spread', '@babel/plugin-proposal-class-properties'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
42
yarn.lock
42
yarn.lock
|
@ -329,6 +329,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375"
|
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375"
|
||||||
integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==
|
integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==
|
||||||
|
|
||||||
|
"@babel/helper-plugin-utils@^7.14.5":
|
||||||
|
version "7.14.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9"
|
||||||
|
integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==
|
||||||
|
|
||||||
"@babel/helper-remap-async-to-generator@^7.12.1":
|
"@babel/helper-remap-async-to-generator@^7.12.1":
|
||||||
version "7.12.1"
|
version "7.12.1"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.1.tgz#8c4dbbf916314f6047dc05e6a2217074238347fd"
|
resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.1.tgz#8c4dbbf916314f6047dc05e6a2217074238347fd"
|
||||||
|
@ -390,6 +395,13 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/types" "^7.12.1"
|
"@babel/types" "^7.12.1"
|
||||||
|
|
||||||
|
"@babel/helper-skip-transparent-expression-wrappers@^7.14.5":
|
||||||
|
version "7.15.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.15.4.tgz#707dbdba1f4ad0fa34f9114fc8197aec7d5da2eb"
|
||||||
|
integrity sha512-BMRLsdh+D1/aap19TycS4eD1qELGrCBJwzaY9IE8LrpJtJb+H7rQkPIdsfgnMtLBA6DJls7X9z93Z4U8h7xw0A==
|
||||||
|
dependencies:
|
||||||
|
"@babel/types" "^7.15.4"
|
||||||
|
|
||||||
"@babel/helper-split-export-declaration@^7.10.1":
|
"@babel/helper-split-export-declaration@^7.10.1":
|
||||||
version "7.10.1"
|
version "7.10.1"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.1.tgz#c6f4be1cbc15e3a868e4c64a17d5d31d754da35f"
|
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.1.tgz#c6f4be1cbc15e3a868e4c64a17d5d31d754da35f"
|
||||||
|
@ -426,6 +438,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed"
|
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed"
|
||||||
integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==
|
integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==
|
||||||
|
|
||||||
|
"@babel/helper-validator-identifier@^7.14.9":
|
||||||
|
version "7.15.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389"
|
||||||
|
integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==
|
||||||
|
|
||||||
"@babel/helper-validator-option@^7.12.1", "@babel/helper-validator-option@^7.12.11":
|
"@babel/helper-validator-option@^7.12.1", "@babel/helper-validator-option@^7.12.11":
|
||||||
version "7.12.11"
|
version "7.12.11"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.11.tgz#d66cb8b7a3e7fe4c6962b32020a131ecf0847f4f"
|
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.11.tgz#d66cb8b7a3e7fe4c6962b32020a131ecf0847f4f"
|
||||||
|
@ -590,6 +607,15 @@
|
||||||
"@babel/helper-skip-transparent-expression-wrappers" "^7.12.1"
|
"@babel/helper-skip-transparent-expression-wrappers" "^7.12.1"
|
||||||
"@babel/plugin-syntax-optional-chaining" "^7.8.0"
|
"@babel/plugin-syntax-optional-chaining" "^7.8.0"
|
||||||
|
|
||||||
|
"@babel/plugin-proposal-optional-chaining@^7.14.5":
|
||||||
|
version "7.14.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.14.5.tgz#fa83651e60a360e3f13797eef00b8d519695b603"
|
||||||
|
integrity sha512-ycz+VOzo2UbWNI1rQXxIuMOzrDdHGrI23fRiz/Si2R4kv2XZQ1BK8ccdHwehMKBlcH/joGW/tzrUmo67gbJHlQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/helper-plugin-utils" "^7.14.5"
|
||||||
|
"@babel/helper-skip-transparent-expression-wrappers" "^7.14.5"
|
||||||
|
"@babel/plugin-syntax-optional-chaining" "^7.8.3"
|
||||||
|
|
||||||
"@babel/plugin-proposal-private-methods@^7.12.1":
|
"@babel/plugin-proposal-private-methods@^7.12.1":
|
||||||
version "7.12.1"
|
version "7.12.1"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.12.1.tgz#86814f6e7a21374c980c10d38b4493e703f4a389"
|
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.12.1.tgz#86814f6e7a21374c980c10d38b4493e703f4a389"
|
||||||
|
@ -694,7 +720,7 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/helper-plugin-utils" "^7.8.0"
|
"@babel/helper-plugin-utils" "^7.8.0"
|
||||||
|
|
||||||
"@babel/plugin-syntax-optional-chaining@^7.8.0":
|
"@babel/plugin-syntax-optional-chaining@^7.8.0", "@babel/plugin-syntax-optional-chaining@^7.8.3":
|
||||||
version "7.8.3"
|
version "7.8.3"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a"
|
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a"
|
||||||
integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==
|
integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==
|
||||||
|
@ -1230,6 +1256,14 @@
|
||||||
lodash "^4.17.19"
|
lodash "^4.17.19"
|
||||||
to-fast-properties "^2.0.0"
|
to-fast-properties "^2.0.0"
|
||||||
|
|
||||||
|
"@babel/types@^7.15.4":
|
||||||
|
version "7.15.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.15.6.tgz#99abdc48218b2881c058dd0a7ab05b99c9be758f"
|
||||||
|
integrity sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==
|
||||||
|
dependencies:
|
||||||
|
"@babel/helper-validator-identifier" "^7.14.9"
|
||||||
|
to-fast-properties "^2.0.0"
|
||||||
|
|
||||||
"@datapunt/matomo-tracker-js@^0.1.4":
|
"@datapunt/matomo-tracker-js@^0.1.4":
|
||||||
version "0.1.4"
|
version "0.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@datapunt/matomo-tracker-js/-/matomo-tracker-js-0.1.4.tgz#1226f0964d2c062bf9392e9c2fd89838262b10df"
|
resolved "https://registry.yarnpkg.com/@datapunt/matomo-tracker-js/-/matomo-tracker-js-0.1.4.tgz#1226f0964d2c062bf9392e9c2fd89838262b10df"
|
||||||
|
@ -16391,9 +16425,9 @@ url-parse-lax@^3.0.0:
|
||||||
prepend-http "^2.0.0"
|
prepend-http "^2.0.0"
|
||||||
|
|
||||||
url-parse@^1.1.1, url-parse@^1.1.8, url-parse@^1.4.3:
|
url-parse@^1.1.1, url-parse@^1.1.8, url-parse@^1.4.3:
|
||||||
version "1.5.1"
|
version "1.5.3"
|
||||||
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.1.tgz#d5fa9890af8a5e1f274a2c98376510f6425f6e3b"
|
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.3.tgz#71c1303d38fb6639ade183c2992c8cc0686df862"
|
||||||
integrity sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==
|
integrity sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
querystringify "^2.1.1"
|
querystringify "^2.1.1"
|
||||||
requires-port "^1.0.0"
|
requires-port "^1.0.0"
|
||||||
|
|
Loading…
Reference in a new issue