add User type and allow lbryinc types to be used by apps

This commit is contained in:
Sean Yesmunt 2019-09-16 16:12:43 -04:00
parent 66a719ebfb
commit 09c9a6ca2b
17 changed files with 1748 additions and 1111 deletions

View file

@ -25,6 +25,7 @@
"import/no-commonjs": "warn", "import/no-commonjs": "warn",
"import/no-amd": "warn", "import/no-amd": "warn",
"import/prefer-default-export": "ignore", "import/prefer-default-export": "ignore",
"flowtype/generic-spacing": 0,
"func-names": ["warn", "as-needed"], "func-names": ["warn", "as-needed"],
"no-plusplus": 0 "no-plusplus": 0
} }

548
dist/bundle.es.js vendored
View file

@ -86,9 +86,7 @@ const SET_SYNC_COMPLETED = 'SET_SYNC_COMPLETED';
const SET_DEFAULT_ACCOUNT = 'SET_DEFAULT_ACCOUNT'; const SET_DEFAULT_ACCOUNT = 'SET_DEFAULT_ACCOUNT';
const SYNC_APPLY_STARTED = 'SYNC_APPLY_STARTED'; const SYNC_APPLY_STARTED = 'SYNC_APPLY_STARTED';
const SYNC_APPLY_COMPLETED = 'SYNC_APPLY_COMPLETED'; const SYNC_APPLY_COMPLETED = 'SYNC_APPLY_COMPLETED';
const SYNC_APPLY_FAILED = 'SYNC_APPLY_FAILED'; // User const SYNC_APPLY_FAILED = 'SYNC_APPLY_FAILED';
const USER_SETTINGS_POPULATE = 'USER_SETTINGS_POPULATE';
var action_types = /*#__PURE__*/Object.freeze({ var action_types = /*#__PURE__*/Object.freeze({
GENERATE_AUTH_TOKEN_FAILURE: GENERATE_AUTH_TOKEN_FAILURE, GENERATE_AUTH_TOKEN_FAILURE: GENERATE_AUTH_TOKEN_FAILURE,
@ -161,8 +159,7 @@ var action_types = /*#__PURE__*/Object.freeze({
SET_DEFAULT_ACCOUNT: SET_DEFAULT_ACCOUNT, SET_DEFAULT_ACCOUNT: SET_DEFAULT_ACCOUNT,
SYNC_APPLY_STARTED: SYNC_APPLY_STARTED, SYNC_APPLY_STARTED: SYNC_APPLY_STARTED,
SYNC_APPLY_COMPLETED: SYNC_APPLY_COMPLETED, SYNC_APPLY_COMPLETED: SYNC_APPLY_COMPLETED,
SYNC_APPLY_FAILED: SYNC_APPLY_FAILED, SYNC_APPLY_FAILED: SYNC_APPLY_FAILED
USER_SETTINGS_POPULATE: USER_SETTINGS_POPULATE
}); });
const Lbryio = { const Lbryio = {
@ -638,6 +635,273 @@ var subscriptions = handleActions({
}) })
}, defaultState); }, defaultState);
function swapKeyAndValue(dict) {
const ret = {}; // eslint-disable-next-line no-restricted-syntax
for (const key in dict) {
if (dict.hasOwnProperty(key)) {
ret[dict[key]] = key;
}
}
return ret;
}
const selectState = state => state.subscriptions || {}; // Returns the list of channel uris a user is subscribed to
const selectSubscriptions = reselect.createSelector(selectState, state => state.subscriptions); // Fetching list of users subscriptions
const selectIsFetchingSubscriptions = reselect.createSelector(selectState, state => state.loading); // The current view mode on the subscriptions page
const selectViewMode = reselect.createSelector(selectState, state => state.viewMode); // Suggested subscriptions from internal apis
const selectSuggested = reselect.createSelector(selectState, state => state.suggested);
const selectIsFetchingSuggested = reselect.createSelector(selectState, state => state.loadingSuggested);
const selectSuggestedChannels = reselect.createSelector(selectSubscriptions, selectSuggested, (userSubscriptions, suggested) => {
if (!suggested) {
return null;
} // Swap the key/value because we will use the uri for everything, this just makes it easier
// suggested is returned from the api with the form:
// {
// featured: { "Channel label": uri, ... },
// top_subscribed: { "@channel": uri, ... }
// top_bid: { "@channel": uri, ... }
// }
// To properly compare the suggested subscriptions from our current subscribed channels
// We only care about the uri, not the label
// We also only care about top_subscribed and featured
// top_bid could just be porn or a channel with no content
const topSubscribedSuggestions = swapKeyAndValue(suggested[SUGGESTED_TOP_SUBSCRIBED]);
const featuredSuggestions = swapKeyAndValue(suggested[SUGGESTED_FEATURED]); // Make sure there are no duplicates
// If a uri isn't already in the suggested object, add it
const suggestedChannels = { ...topSubscribedSuggestions
};
Object.keys(featuredSuggestions).forEach(uri => {
if (!suggestedChannels[uri]) {
const channelLabel = featuredSuggestions[uri];
suggestedChannels[uri] = channelLabel;
}
});
userSubscriptions.forEach(({
uri
}) => {
// Note to passer bys:
// Maybe we should just remove the `lbry://` prefix from subscription uris
// Most places don't store them like that
const subscribedUri = uri.slice('lbry://'.length);
if (suggestedChannels[subscribedUri]) {
delete suggestedChannels[subscribedUri];
}
});
return Object.keys(suggestedChannels).map(uri => ({
uri,
label: suggestedChannels[uri]
})).slice(0, 5);
});
const selectFirstRunCompleted = reselect.createSelector(selectState, state => state.firstRunCompleted);
const selectShowSuggestedSubs = reselect.createSelector(selectState, state => state.showSuggestedSubs); // Fetching any claims that are a part of a users subscriptions
const selectSubscriptionsBeingFetched = reselect.createSelector(selectSubscriptions, lbryRedux.selectAllFetchingChannelClaims, (subscriptions, fetchingChannelClaims) => {
const fetchingSubscriptionMap = {};
subscriptions.forEach(sub => {
const isFetching = fetchingChannelClaims && fetchingChannelClaims[sub.uri];
if (isFetching) {
fetchingSubscriptionMap[sub.uri] = true;
}
});
return fetchingSubscriptionMap;
});
const selectUnreadByChannel = reselect.createSelector(selectState, state => state.unread); // Returns the current total of unread subscriptions
const selectUnreadAmount = reselect.createSelector(selectUnreadByChannel, unreadByChannel => {
const unreadChannels = Object.keys(unreadByChannel);
let badges = 0;
if (!unreadChannels.length) {
return badges;
}
unreadChannels.forEach(channel => {
badges += unreadByChannel[channel].uris.length;
});
return badges;
}); // Returns the uris with channels as an array with the channel with the newest content first
// If you just want the `unread` state, use selectUnread
const selectUnreadSubscriptions = reselect.createSelector(selectUnreadAmount, selectUnreadByChannel, lbryRedux.selectClaimsByUri, (unreadAmount, unreadByChannel, claimsByUri) => {
// determine which channel has the newest content
const unreadList = [];
if (!unreadAmount) {
return unreadList;
}
const channelUriList = Object.keys(unreadByChannel); // There is only one channel with unread notifications
if (unreadAmount === 1) {
channelUriList.forEach(channel => {
const unreadChannel = {
channel,
uris: unreadByChannel[channel].uris
};
unreadList.push(unreadChannel);
});
return unreadList;
}
channelUriList.sort((channel1, channel2) => {
const latestUriFromChannel1 = unreadByChannel[channel1].uris[0];
const latestClaimFromChannel1 = claimsByUri[latestUriFromChannel1] || {};
const latestUriFromChannel2 = unreadByChannel[channel2].uris[0];
const latestClaimFromChannel2 = claimsByUri[latestUriFromChannel2] || {};
const latestHeightFromChannel1 = latestClaimFromChannel1.height || 0;
const latestHeightFromChannel2 = latestClaimFromChannel2.height || 0;
if (latestHeightFromChannel1 !== latestHeightFromChannel2) {
return latestHeightFromChannel2 - latestHeightFromChannel1;
}
return 0;
}).forEach(channel => {
const unreadSubscription = unreadByChannel[channel];
const unreadChannel = {
channel,
uris: unreadSubscription.uris
};
unreadList.push(unreadChannel);
});
return unreadList;
}); // Returns all unread subscriptions for a uri passed in
const makeSelectUnreadByChannel = uri => reselect.createSelector(selectUnreadByChannel, unread => unread[uri]); // Returns the first page of claims for every channel a user is subscribed to
const selectSubscriptionClaims = reselect.createSelector(lbryRedux.selectAllClaimsByChannel, lbryRedux.selectClaimsById, selectSubscriptions, selectUnreadByChannel, (channelIds, allClaims, savedSubscriptions, unreadByChannel) => {
// no claims loaded yet
if (!Object.keys(channelIds).length) {
return [];
}
let fetchedSubscriptions = [];
savedSubscriptions.forEach(subscription => {
let channelClaims = []; // if subscribed channel has content
if (channelIds[subscription.uri] && channelIds[subscription.uri]['1']) {
// This will need to be more robust, we will want to be able to load more than the first page
// Strip out any ids that will be shown as notifications
const pageOneChannelIds = channelIds[subscription.uri]['1']; // we have the channel ids and the corresponding claims
// loop over the list of ids and grab the claim
pageOneChannelIds.forEach(id => {
const grabbedClaim = allClaims[id];
if (unreadByChannel[subscription.uri] && unreadByChannel[subscription.uri].uris.some(uri => uri.includes(id))) {
grabbedClaim.isNew = true;
}
channelClaims = channelClaims.concat([grabbedClaim]);
});
}
fetchedSubscriptions = fetchedSubscriptions.concat(channelClaims);
});
return fetchedSubscriptions;
}); // Returns true if a user is subscribed to the channel associated with the uri passed in
// Accepts content or channel uris
const makeSelectIsSubscribed = uri => reselect.createSelector(selectSubscriptions, lbryRedux.makeSelectChannelForClaimUri(uri, true), (subscriptions, channelUri) => {
if (channelUri) {
return subscriptions.some(sub => sub.uri === channelUri);
} // If we couldn't get a channel uri from the claim uri, the uri passed in might be a channel already
const {
isChannel
} = lbryRedux.parseURI(uri);
if (isChannel) {
const uriWithPrefix = uri.startsWith('lbry://') ? uri : `lbry://${uri}`;
return subscriptions.some(sub => sub.uri === uriWithPrefix);
}
return false;
});
const makeSelectIsNew = uri => reselect.createSelector(makeSelectIsSubscribed(uri), lbryRedux.makeSelectChannelForClaimUri(uri), selectUnreadByChannel, (isSubscribed, channel, unreadByChannel) => {
if (!isSubscribed) {
return false;
}
const unreadForChannel = unreadByChannel[`lbry://${channel}`];
if (unreadForChannel) {
return unreadForChannel.uris.includes(uri);
}
return false; // If they are subscribed, check to see if this uri is in the list of unreads
});
const selectEnabledChannelNotifications = reselect.createSelector(selectState, state => state.enabledChannelNotifications);
const persistShape = {
version: '0',
shared: {}
};
function userStateSyncMiddleware() {
return ({
getState
}) => next => action => {
if (action.type === CHANNEL_SUBSCRIBE || action.type === CHANNEL_UNSUBSCRIBE || action.type === lbryRedux.ACTIONS.TOGGLE_TAG_FOLLOW) {
const newShape = { ...persistShape
};
const state = getState();
const subscriptions = selectSubscriptions(state).map(({
uri
}) => uri);
const tags = lbryRedux.selectFollowedTags(state);
newShape.shared.subscriptions = subscriptions;
newShape.shared.tags = tags;
const {
uri
} = action.data;
if (action.type === CHANNEL_SUBSCRIBE) {
const newSubscriptions = subscriptions.slice();
newSubscriptions.push(uri);
newShape.shared.subscriptions = newSubscriptions;
} else if (action.type === CHANNEL_UNSUBSCRIBE) {
let newSubscriptions = subscriptions.slice();
newSubscriptions = newSubscriptions.filter(subscribedUri => subscribedUri !== uri);
newShape.shared.subscriptions = newSubscriptions;
} else {
const toggledTag = action.data.name;
const followedTags = lbryRedux.selectFollowedTags(state).map(({
name
}) => name);
const isFollowing = lbryRedux.makeSelectIsFollowingTag(toggledTag)(state);
let newTags = followedTags.slice();
if (isFollowing) {
newTags = newTags.filter(followedTag => followedTag.name !== toggledTag);
} else {
newTags.push(toggledTag);
}
newShape.shared.tags = newTags;
}
Lbryio.call('user_settings', 'set', {
settings: newShape
});
}
return next(action);
};
}
function doGenerateAuthToken(installationId) { function doGenerateAuthToken(installationId) {
return dispatch => { return dispatch => {
dispatch({ dispatch({
@ -668,25 +932,25 @@ function doGenerateAuthToken(installationId) {
}; };
} }
const selectState = state => state.rewards || {}; const selectState$1 = state => state.rewards || {};
const selectUnclaimedRewardsByType = reselect.createSelector(selectState, state => state.unclaimedRewardsByType); const selectUnclaimedRewardsByType = reselect.createSelector(selectState$1, state => state.unclaimedRewardsByType);
const selectClaimedRewardsById = reselect.createSelector(selectState, state => state.claimedRewardsById); const selectClaimedRewardsById = reselect.createSelector(selectState$1, state => state.claimedRewardsById);
const selectClaimedRewards = reselect.createSelector(selectClaimedRewardsById, byId => Object.values(byId) || []); const selectClaimedRewards = reselect.createSelector(selectClaimedRewardsById, byId => Object.values(byId) || []);
const selectClaimedRewardsByTransactionId = reselect.createSelector(selectClaimedRewards, rewards => rewards.reduce((mapParam, reward) => { const selectClaimedRewardsByTransactionId = reselect.createSelector(selectClaimedRewards, rewards => rewards.reduce((mapParam, reward) => {
const map = mapParam; const map = mapParam;
map[reward.transaction_id] = reward; map[reward.transaction_id] = reward;
return map; return map;
}, {})); }, {}));
const selectUnclaimedRewards = reselect.createSelector(selectState, state => state.unclaimedRewards); const selectUnclaimedRewards = reselect.createSelector(selectState$1, state => state.unclaimedRewards);
const selectFetchingRewards = reselect.createSelector(selectState, state => !!state.fetching); const selectFetchingRewards = reselect.createSelector(selectState$1, state => !!state.fetching);
const selectUnclaimedRewardValue = reselect.createSelector(selectUnclaimedRewards, rewards => rewards.reduce((sum, reward) => sum + reward.reward_amount, 0)); const selectUnclaimedRewardValue = reselect.createSelector(selectUnclaimedRewards, rewards => rewards.reduce((sum, reward) => sum + reward.reward_amount, 0));
const selectClaimsPendingByType = reselect.createSelector(selectState, state => state.claimPendingByType); const selectClaimsPendingByType = reselect.createSelector(selectState$1, state => state.claimPendingByType);
const selectIsClaimRewardPending = (state, props) => selectClaimsPendingByType(state, props)[props.reward_type]; const selectIsClaimRewardPending = (state, props) => selectClaimsPendingByType(state, props)[props.reward_type];
const makeSelectIsRewardClaimPending = () => reselect.createSelector(selectIsClaimRewardPending, isClaiming => isClaiming); const makeSelectIsRewardClaimPending = () => reselect.createSelector(selectIsClaimRewardPending, isClaiming => isClaiming);
const selectClaimErrorsByType = reselect.createSelector(selectState, state => state.claimErrorsByType); const selectClaimErrorsByType = reselect.createSelector(selectState$1, state => state.claimErrorsByType);
const selectClaimRewardError = (state, props) => selectClaimErrorsByType(state, props)[props.reward_type]; const selectClaimRewardError = (state, props) => selectClaimErrorsByType(state, props)[props.reward_type];
@ -696,39 +960,39 @@ const selectRewardByType = (state, rewardType) => selectUnclaimedRewards(state).
const makeSelectRewardByType = () => reselect.createSelector(selectRewardByType, reward => reward); const makeSelectRewardByType = () => reselect.createSelector(selectRewardByType, reward => reward);
const makeSelectRewardAmountByType = () => reselect.createSelector(selectRewardByType, reward => reward ? reward.reward_amount : 0); const makeSelectRewardAmountByType = () => reselect.createSelector(selectRewardByType, reward => reward ? reward.reward_amount : 0);
const selectRewardContentClaimIds = reselect.createSelector(selectState, state => state.rewardedContentClaimIds); const selectRewardContentClaimIds = reselect.createSelector(selectState$1, state => state.rewardedContentClaimIds);
const selectReferralReward = reselect.createSelector(selectUnclaimedRewards, unclaimedRewards => unclaimedRewards.filter(reward => reward.reward_type === rewards.TYPE_REFERRAL)[0]); const selectReferralReward = reselect.createSelector(selectUnclaimedRewards, unclaimedRewards => unclaimedRewards.filter(reward => reward.reward_type === rewards.TYPE_REFERRAL)[0]);
const selectState$1 = state => state.user || {}; const selectState$2 = state => state.user || {};
const selectAuthenticationIsPending = reselect.createSelector(selectState$1, state => state.authenticationIsPending); const selectAuthenticationIsPending = reselect.createSelector(selectState$2, state => state.authenticationIsPending);
const selectUserIsPending = reselect.createSelector(selectState$1, state => state.userIsPending); const selectUserIsPending = reselect.createSelector(selectState$2, state => state.userIsPending);
const selectUser = reselect.createSelector(selectState$1, state => state.user); const selectUser = reselect.createSelector(selectState$2, state => state.user);
const selectUserEmail = reselect.createSelector(selectUser, user => user ? user.primary_email : null); const selectUserEmail = reselect.createSelector(selectUser, user => user ? user.primary_email : null);
const selectUserPhone = reselect.createSelector(selectUser, user => user ? user.phone_number : null); const selectUserPhone = reselect.createSelector(selectUser, user => user ? user.phone_number : null);
const selectUserCountryCode = reselect.createSelector(selectUser, user => user ? user.country_code : null); const selectUserCountryCode = reselect.createSelector(selectUser, user => user ? user.country_code : null);
const selectEmailToVerify = reselect.createSelector(selectState$1, selectUserEmail, (state, userEmail) => state.emailToVerify || userEmail); const selectEmailToVerify = reselect.createSelector(selectState$2, selectUserEmail, (state, userEmail) => state.emailToVerify || userEmail);
const selectPhoneToVerify = reselect.createSelector(selectState$1, selectUserPhone, (state, userPhone) => state.phoneToVerify || userPhone); const selectPhoneToVerify = reselect.createSelector(selectState$2, selectUserPhone, (state, userPhone) => state.phoneToVerify || userPhone);
const selectUserIsRewardApproved = reselect.createSelector(selectUser, user => user && user.is_reward_approved); const selectUserIsRewardApproved = reselect.createSelector(selectUser, user => user && user.is_reward_approved);
const selectEmailNewIsPending = reselect.createSelector(selectState$1, state => state.emailNewIsPending); const selectEmailNewIsPending = reselect.createSelector(selectState$2, state => state.emailNewIsPending);
const selectEmailNewErrorMessage = reselect.createSelector(selectState$1, state => state.emailNewErrorMessage); const selectEmailNewErrorMessage = reselect.createSelector(selectState$2, state => state.emailNewErrorMessage);
const selectPhoneNewErrorMessage = reselect.createSelector(selectState$1, state => state.phoneNewErrorMessage); const selectPhoneNewErrorMessage = reselect.createSelector(selectState$2, state => state.phoneNewErrorMessage);
const selectEmailVerifyIsPending = reselect.createSelector(selectState$1, state => state.emailVerifyIsPending); const selectEmailVerifyIsPending = reselect.createSelector(selectState$2, state => state.emailVerifyIsPending);
const selectEmailVerifyErrorMessage = reselect.createSelector(selectState$1, state => state.emailVerifyErrorMessage); const selectEmailVerifyErrorMessage = reselect.createSelector(selectState$2, state => state.emailVerifyErrorMessage);
const selectPhoneNewIsPending = reselect.createSelector(selectState$1, state => state.phoneNewIsPending); const selectPhoneNewIsPending = reselect.createSelector(selectState$2, state => state.phoneNewIsPending);
const selectPhoneVerifyIsPending = reselect.createSelector(selectState$1, state => state.phoneVerifyIsPending); const selectPhoneVerifyIsPending = reselect.createSelector(selectState$2, state => state.phoneVerifyIsPending);
const selectPhoneVerifyErrorMessage = reselect.createSelector(selectState$1, state => state.phoneVerifyErrorMessage); const selectPhoneVerifyErrorMessage = reselect.createSelector(selectState$2, state => state.phoneVerifyErrorMessage);
const selectIdentityVerifyIsPending = reselect.createSelector(selectState$1, state => state.identityVerifyIsPending); const selectIdentityVerifyIsPending = reselect.createSelector(selectState$2, state => state.identityVerifyIsPending);
const selectIdentityVerifyErrorMessage = reselect.createSelector(selectState$1, state => state.identityVerifyErrorMessage); const selectIdentityVerifyErrorMessage = reselect.createSelector(selectState$2, state => state.identityVerifyErrorMessage);
const selectUserVerifiedEmail = reselect.createSelector(selectUser, user => user && user.has_verified_email); const selectUserVerifiedEmail = reselect.createSelector(selectUser, user => user && user.has_verified_email);
const selectUserIsVerificationCandidate = reselect.createSelector(selectUser, user => user && (!user.has_verified_email || !user.is_identity_verified)); const selectUserIsVerificationCandidate = reselect.createSelector(selectUser, user => user && (!user.has_verified_email || !user.is_identity_verified));
const selectAccessToken = reselect.createSelector(selectState$1, state => state.accessToken); const selectAccessToken = reselect.createSelector(selectState$2, state => state.accessToken);
const selectUserInviteStatusIsPending = reselect.createSelector(selectState$1, state => state.inviteStatusIsPending); const selectUserInviteStatusIsPending = reselect.createSelector(selectState$2, state => state.inviteStatusIsPending);
const selectUserInvitesRemaining = reselect.createSelector(selectState$1, state => state.invitesRemaining); const selectUserInvitesRemaining = reselect.createSelector(selectState$2, state => state.invitesRemaining);
const selectUserInvitees = reselect.createSelector(selectState$1, state => state.invitees); const selectUserInvitees = reselect.createSelector(selectState$2, state => state.invitees);
const selectUserInviteStatusFailed = reselect.createSelector(selectUserInvitesRemaining, () => selectUserInvitesRemaining === null); const selectUserInviteStatusFailed = reselect.createSelector(selectUserInvitesRemaining, () => selectUserInvitesRemaining === null);
const selectUserInviteNewIsPending = reselect.createSelector(selectState$1, state => state.inviteNewIsPending); const selectUserInviteNewIsPending = reselect.createSelector(selectState$2, state => state.inviteNewIsPending);
const selectUserInviteNewErrorMessage = reselect.createSelector(selectState$1, state => state.inviteNewErrorMessage); const selectUserInviteNewErrorMessage = reselect.createSelector(selectState$2, state => state.inviteNewErrorMessage);
const selectUserInviteReferralLink = reselect.createSelector(selectState$1, state => state.referralLink); const selectUserInviteReferralLink = reselect.createSelector(selectState$2, state => state.referralLink);
function doFetchInviteStatus() { function doFetchInviteStatus() {
return dispatch => { return dispatch => {
@ -1263,217 +1527,6 @@ function doFetchRewardedContent() {
const PAGE_SIZE = 20; const PAGE_SIZE = 20;
function swapKeyAndValue(dict) {
const ret = {}; // eslint-disable-next-line no-restricted-syntax
for (const key in dict) {
if (dict.hasOwnProperty(key)) {
ret[dict[key]] = key;
}
}
return ret;
}
const selectState$2 = state => state.subscriptions || {}; // Returns the list of channel uris a user is subscribed to
const selectSubscriptions = reselect.createSelector(selectState$2, state => state.subscriptions); // Fetching list of users subscriptions
const selectIsFetchingSubscriptions = reselect.createSelector(selectState$2, state => state.loading); // The current view mode on the subscriptions page
const selectViewMode = reselect.createSelector(selectState$2, state => state.viewMode); // Suggested subscriptions from internal apis
const selectSuggested = reselect.createSelector(selectState$2, state => state.suggested);
const selectIsFetchingSuggested = reselect.createSelector(selectState$2, state => state.loadingSuggested);
const selectSuggestedChannels = reselect.createSelector(selectSubscriptions, selectSuggested, (userSubscriptions, suggested) => {
if (!suggested) {
return null;
} // Swap the key/value because we will use the uri for everything, this just makes it easier
// suggested is returned from the api with the form:
// {
// featured: { "Channel label": uri, ... },
// top_subscribed: { "@channel": uri, ... }
// top_bid: { "@channel": uri, ... }
// }
// To properly compare the suggested subscriptions from our current subscribed channels
// We only care about the uri, not the label
// We also only care about top_subscribed and featured
// top_bid could just be porn or a channel with no content
const topSubscribedSuggestions = swapKeyAndValue(suggested[SUGGESTED_TOP_SUBSCRIBED]);
const featuredSuggestions = swapKeyAndValue(suggested[SUGGESTED_FEATURED]); // Make sure there are no duplicates
// If a uri isn't already in the suggested object, add it
const suggestedChannels = { ...topSubscribedSuggestions
};
Object.keys(featuredSuggestions).forEach(uri => {
if (!suggestedChannels[uri]) {
const channelLabel = featuredSuggestions[uri];
suggestedChannels[uri] = channelLabel;
}
});
userSubscriptions.forEach(({
uri
}) => {
// Note to passer bys:
// Maybe we should just remove the `lbry://` prefix from subscription uris
// Most places don't store them like that
const subscribedUri = uri.slice('lbry://'.length);
if (suggestedChannels[subscribedUri]) {
delete suggestedChannels[subscribedUri];
}
});
return Object.keys(suggestedChannels).map(uri => ({
uri,
label: suggestedChannels[uri]
})).slice(0, 5);
});
const selectFirstRunCompleted = reselect.createSelector(selectState$2, state => state.firstRunCompleted);
const selectShowSuggestedSubs = reselect.createSelector(selectState$2, state => state.showSuggestedSubs); // Fetching any claims that are a part of a users subscriptions
const selectSubscriptionsBeingFetched = reselect.createSelector(selectSubscriptions, lbryRedux.selectAllFetchingChannelClaims, (subscriptions, fetchingChannelClaims) => {
const fetchingSubscriptionMap = {};
subscriptions.forEach(sub => {
const isFetching = fetchingChannelClaims && fetchingChannelClaims[sub.uri];
if (isFetching) {
fetchingSubscriptionMap[sub.uri] = true;
}
});
return fetchingSubscriptionMap;
});
const selectUnreadByChannel = reselect.createSelector(selectState$2, state => state.unread); // Returns the current total of unread subscriptions
const selectUnreadAmount = reselect.createSelector(selectUnreadByChannel, unreadByChannel => {
const unreadChannels = Object.keys(unreadByChannel);
let badges = 0;
if (!unreadChannels.length) {
return badges;
}
unreadChannels.forEach(channel => {
badges += unreadByChannel[channel].uris.length;
});
return badges;
}); // Returns the uris with channels as an array with the channel with the newest content first
// If you just want the `unread` state, use selectUnread
const selectUnreadSubscriptions = reselect.createSelector(selectUnreadAmount, selectUnreadByChannel, lbryRedux.selectClaimsByUri, (unreadAmount, unreadByChannel, claimsByUri) => {
// determine which channel has the newest content
const unreadList = [];
if (!unreadAmount) {
return unreadList;
}
const channelUriList = Object.keys(unreadByChannel); // There is only one channel with unread notifications
if (unreadAmount === 1) {
channelUriList.forEach(channel => {
const unreadChannel = {
channel,
uris: unreadByChannel[channel].uris
};
unreadList.push(unreadChannel);
});
return unreadList;
}
channelUriList.sort((channel1, channel2) => {
const latestUriFromChannel1 = unreadByChannel[channel1].uris[0];
const latestClaimFromChannel1 = claimsByUri[latestUriFromChannel1] || {};
const latestUriFromChannel2 = unreadByChannel[channel2].uris[0];
const latestClaimFromChannel2 = claimsByUri[latestUriFromChannel2] || {};
const latestHeightFromChannel1 = latestClaimFromChannel1.height || 0;
const latestHeightFromChannel2 = latestClaimFromChannel2.height || 0;
if (latestHeightFromChannel1 !== latestHeightFromChannel2) {
return latestHeightFromChannel2 - latestHeightFromChannel1;
}
return 0;
}).forEach(channel => {
const unreadSubscription = unreadByChannel[channel];
const unreadChannel = {
channel,
uris: unreadSubscription.uris
};
unreadList.push(unreadChannel);
});
return unreadList;
}); // Returns all unread subscriptions for a uri passed in
const makeSelectUnreadByChannel = uri => reselect.createSelector(selectUnreadByChannel, unread => unread[uri]); // Returns the first page of claims for every channel a user is subscribed to
const selectSubscriptionClaims = reselect.createSelector(lbryRedux.selectAllClaimsByChannel, lbryRedux.selectClaimsById, selectSubscriptions, selectUnreadByChannel, (channelIds, allClaims, savedSubscriptions, unreadByChannel) => {
// no claims loaded yet
if (!Object.keys(channelIds).length) {
return [];
}
let fetchedSubscriptions = [];
savedSubscriptions.forEach(subscription => {
let channelClaims = []; // if subscribed channel has content
if (channelIds[subscription.uri] && channelIds[subscription.uri]['1']) {
// This will need to be more robust, we will want to be able to load more than the first page
// Strip out any ids that will be shown as notifications
const pageOneChannelIds = channelIds[subscription.uri]['1']; // we have the channel ids and the corresponding claims
// loop over the list of ids and grab the claim
pageOneChannelIds.forEach(id => {
const grabbedClaim = allClaims[id];
if (unreadByChannel[subscription.uri] && unreadByChannel[subscription.uri].uris.some(uri => uri.includes(id))) {
grabbedClaim.isNew = true;
}
channelClaims = channelClaims.concat([grabbedClaim]);
});
}
fetchedSubscriptions = fetchedSubscriptions.concat(channelClaims);
});
return fetchedSubscriptions;
}); // Returns true if a user is subscribed to the channel associated with the uri passed in
// Accepts content or channel uris
const makeSelectIsSubscribed = uri => reselect.createSelector(selectSubscriptions, lbryRedux.makeSelectChannelForClaimUri(uri, true), (subscriptions, channelUri) => {
if (channelUri) {
return subscriptions.some(sub => sub.uri === channelUri);
} // If we couldn't get a channel uri from the claim uri, the uri passed in might be a channel already
const {
isChannel
} = lbryRedux.parseURI(uri);
if (isChannel) {
const uriWithPrefix = uri.startsWith('lbry://') ? uri : `lbry://${uri}`;
return subscriptions.some(sub => sub.uri === uriWithPrefix);
}
return false;
});
const makeSelectIsNew = uri => reselect.createSelector(makeSelectIsSubscribed(uri), lbryRedux.makeSelectChannelForClaimUri(uri), selectUnreadByChannel, (isSubscribed, channel, unreadByChannel) => {
if (!isSubscribed) {
return false;
}
const unreadForChannel = unreadByChannel[`lbry://${channel}`];
if (unreadForChannel) {
return unreadForChannel.uris.includes(uri);
}
return false; // If they are subscribed, check to see if this uri is in the list of unreads
});
const selectEnabledChannelNotifications = reselect.createSelector(selectState$2, state => state.enabledChannelNotifications);
// //
const CHECK_SUBSCRIPTIONS_INTERVAL = 15 * 60 * 1000; const CHECK_SUBSCRIPTIONS_INTERVAL = 15 * 60 * 1000;
const doSetViewMode = viewMode => dispatch => dispatch({ const doSetViewMode = viewMode => dispatch => dispatch({
@ -2998,3 +3051,4 @@ exports.statsReducer = statsReducer;
exports.subscriptionsReducer = subscriptions; exports.subscriptionsReducer = subscriptions;
exports.syncReducer = syncReducer; exports.syncReducer = syncReducer;
exports.userReducer = userReducer; exports.userReducer = userReducer;
exports.userStateSyncMiddleware = userStateSyncMiddleware;

1697
dist/bundle.js vendored

File diff suppressed because it is too large Load diff

View file

@ -3,4 +3,3 @@
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
export type Dispatch<T> = (action: T | Promise<T> | Array<T> | ThunkAction<T>) => any; // Need to refer to ThunkAction export type Dispatch<T> = (action: T | Promise<T> | Array<T> | ThunkAction<T>) => any; // Need to refer to ThunkAction
export type GetState = () => any; export type GetState = () => any;
export type ThunkAction<T> = (dispatch: Dispatch<T>, getState: GetState) => any;

View file

@ -1,5 +1,5 @@
// @flow // @flow
import type { Dispatch as ReduxDispatch } from 'types/redux'; import type { Dispatch } from 'flow-typed/Redux';
import * as ACTIONS from 'constants/action_types'; import * as ACTIONS from 'constants/action_types';
import { import {
DOWNLOADED, DOWNLOADED,
@ -12,7 +12,7 @@ import {
SUGGESTED_FEATURED, SUGGESTED_FEATURED,
} from 'constants/subscriptions'; } from 'constants/subscriptions';
export type Subscription = { declare type Subscription = {
channelName: string, // @CryptoCandor, channelName: string, // @CryptoCandor,
uri: string, // lbry://@CryptoCandor#9152f3b054f692076a6882d1b58a30e8781cc8e6 uri: string, // lbry://@CryptoCandor#9152f3b054f692076a6882d1b58a30e8781cc8e6
latest?: string, // substratum#b0ab143243020e7831fd070d9f871e1fda948620 latest?: string, // substratum#b0ab143243020e7831fd070d9f871e1fda948620
@ -21,26 +21,26 @@ export type Subscription = {
// Tracking for new content // Tracking for new content
// i.e. If a subscription has a DOWNLOADING type, we will trigger an OS notification // i.e. If a subscription has a DOWNLOADING type, we will trigger an OS notification
// to tell users there is new content from their subscriptions // to tell users there is new content from their subscriptions
export type SubscriptionNotificationType = DOWNLOADED | DOWNLOADING | NOTIFY_ONLY; declare type SubscriptionNotificationType = DOWNLOADED | DOWNLOADING | NOTIFY_ONLY;
export type UnreadSubscription = { declare type UnreadSubscription = {
type: SubscriptionNotificationType, type: SubscriptionNotificationType,
uris: Array<string>, uris: Array<string>,
}; };
export type UnreadSubscriptions = { declare type UnreadSubscriptions = {
[string]: UnreadSubscription, [string]: UnreadSubscription,
}; };
export type ViewMode = VIEW_LATEST_FIRST | VIEW_ALL; declare type ViewMode = VIEW_LATEST_FIRST | VIEW_ALL;
export type SuggestedType = SUGGESTED_TOP_BID | SUGGESTED_TOP_SUBSCRIBED | SUGGESTED_FEATURED; declare type SuggestedType = SUGGESTED_TOP_BID | SUGGESTED_TOP_SUBSCRIBED | SUGGESTED_FEATURED;
export type SuggestedSubscriptions = { declare type SuggestedSubscriptions = {
[SuggestedType]: string, [SuggestedType]: string,
}; };
export type SubscriptionState = { declare type SubscriptionState = {
enabledChannelNotifications: Array<string>, enabledChannelNotifications: Array<string>,
subscriptions: Array<Subscription>, subscriptions: Array<Subscription>,
unread: UnreadSubscriptions, unread: UnreadSubscriptions,
@ -55,27 +55,27 @@ export type SubscriptionState = {
// //
// Action types // Action types
// //
export type DoChannelSubscriptionEnableNotifications = { declare type DoChannelSubscriptionEnableNotifications = {
type: ACTIONS.CHANNEL_SUBSCRIPTION_ENABLE_NOTIFICATIONS, type: ACTIONS.CHANNEL_SUBSCRIPTION_ENABLE_NOTIFICATIONS,
data: string, data: string,
}; };
export type DoChannelSubscriptionDisableNotifications = { declare type DoChannelSubscriptionDisableNotifications = {
type: ACTIONS.CHANNEL_SUBSCRIPTION_DISABLE_NOTIFICATIONS, type: ACTIONS.CHANNEL_SUBSCRIPTION_DISABLE_NOTIFICATIONS,
data: string, data: string,
}; };
export type DoChannelSubscribe = { declare type DoChannelSubscribe = {
type: ACTIONS.CHANNEL_SUBSCRIBE, type: ACTIONS.CHANNEL_SUBSCRIBE,
data: Subscription, data: Subscription,
}; };
export type DoChannelUnsubscribe = { declare type DoChannelUnsubscribe = {
type: ACTIONS.CHANNEL_UNSUBSCRIBE, type: ACTIONS.CHANNEL_UNSUBSCRIBE,
data: Subscription, data: Subscription,
}; };
export type DoUpdateSubscriptionUnreads = { declare type DoUpdateSubscriptionUnreads = {
type: ACTIONS.UPDATE_SUBSCRIPTION_UNREADS, type: ACTIONS.UPDATE_SUBSCRIPTION_UNREADS,
data: { data: {
channel: string, channel: string,
@ -84,7 +84,7 @@ export type DoUpdateSubscriptionUnreads = {
}, },
}; };
export type DoRemoveSubscriptionUnreads = { declare type DoRemoveSubscriptionUnreads = {
type: ACTIONS.REMOVE_SUBSCRIPTION_UNREADS, type: ACTIONS.REMOVE_SUBSCRIPTION_UNREADS,
data: { data: {
channel: string, channel: string,
@ -92,7 +92,7 @@ export type DoRemoveSubscriptionUnreads = {
}, },
}; };
export type SetSubscriptionLatest = { declare type SetSubscriptionLatest = {
type: ACTIONS.SET_SUBSCRIPTION_LATEST, type: ACTIONS.SET_SUBSCRIPTION_LATEST,
data: { data: {
subscription: Subscription, subscription: Subscription,
@ -100,30 +100,30 @@ export type SetSubscriptionLatest = {
}, },
}; };
export type CheckSubscriptionStarted = { declare type CheckSubscriptionStarted = {
type: ACTIONS.CHECK_SUBSCRIPTION_STARTED, type: ACTIONS.CHECK_SUBSCRIPTION_STARTED,
}; };
export type CheckSubscriptionCompleted = { declare type CheckSubscriptionCompleted = {
type: ACTIONS.CHECK_SUBSCRIPTION_COMPLETED, type: ACTIONS.CHECK_SUBSCRIPTION_COMPLETED,
}; };
export type FetchedSubscriptionsSucess = { declare type FetchedSubscriptionsSucess = {
type: ACTIONS.FETCH_SUBSCRIPTIONS_SUCCESS, type: ACTIONS.FETCH_SUBSCRIPTIONS_SUCCESS,
data: Array<Subscription>, data: Array<Subscription>,
}; };
export type SetViewMode = { declare type SetViewMode = {
type: ACTIONS.SET_VIEW_MODE, type: ACTIONS.SET_VIEW_MODE,
data: ViewMode, data: ViewMode,
}; };
export type GetSuggestedSubscriptionsSuccess = { declare type GetSuggestedSubscriptionsSuccess = {
type: ACTIONS.GET_SUGGESTED_SUBSCRIPTIONS_START, type: ACTIONS.GET_SUGGESTED_SUBSCRIPTIONS_START,
data: SuggestedSubscriptions, data: SuggestedSubscriptions,
}; };
export type Action = declare type SubscriptionAction =
| DoChannelSubscribe | DoChannelSubscribe
| DoChannelUnsubscribe | DoChannelUnsubscribe
| DoUpdateSubscriptionUnreads | DoUpdateSubscriptionUnreads
@ -134,4 +134,4 @@ export type Action =
| SetViewMode | SetViewMode
| Function; | Function;
export type Dispatch = ReduxDispatch<Action>; declare type SubscriptionDispatch = Dispatch<SubscriptionAction>;

23
dist/flow-typed/User.js vendored Normal file
View file

@ -0,0 +1,23 @@
// @flow
declare type User = {
created_at: string,
family_name: ?string,
given_name: ?string,
groups: Array<string>,
has_verified_email: boolean,
id: number,
invite_reward_claimed: boolean,
invited_at: ?number,
invited_by_id: number,
invites_remaining: number,
is_email_enabled: boolean,
is_identity_verified: boolean,
is_reward_approved: boolean,
language: string,
manual_approval_user_id: ?number,
primary_email: string,
reward_status_change_trigger: string,
updated_at: string,
youtube_channels: ?Array<string>,
};

5
flow-typed/Redux.js vendored Normal file
View file

@ -0,0 +1,5 @@
// @flow
// eslint-disable-next-line no-use-before-define
export type Dispatch<T> = (action: T | Promise<T> | Array<T> | ThunkAction<T>) => any; // Need to refer to ThunkAction
export type GetState = () => any;

137
flow-typed/Subscription.js vendored Normal file
View file

@ -0,0 +1,137 @@
// @flow
import type { Dispatch } from 'flow-typed/Redux';
import * as ACTIONS from 'constants/action_types';
import {
DOWNLOADED,
DOWNLOADING,
NOTIFY_ONLY,
VIEW_ALL,
VIEW_LATEST_FIRST,
SUGGESTED_TOP_BID,
SUGGESTED_TOP_SUBSCRIBED,
SUGGESTED_FEATURED,
} from 'constants/subscriptions';
declare type Subscription = {
channelName: string, // @CryptoCandor,
uri: string, // lbry://@CryptoCandor#9152f3b054f692076a6882d1b58a30e8781cc8e6
latest?: string, // substratum#b0ab143243020e7831fd070d9f871e1fda948620
};
// Tracking for new content
// i.e. If a subscription has a DOWNLOADING type, we will trigger an OS notification
// to tell users there is new content from their subscriptions
declare type SubscriptionNotificationType = DOWNLOADED | DOWNLOADING | NOTIFY_ONLY;
declare type UnreadSubscription = {
type: SubscriptionNotificationType,
uris: Array<string>,
};
declare type UnreadSubscriptions = {
[string]: UnreadSubscription,
};
declare type ViewMode = VIEW_LATEST_FIRST | VIEW_ALL;
declare type SuggestedType = SUGGESTED_TOP_BID | SUGGESTED_TOP_SUBSCRIBED | SUGGESTED_FEATURED;
declare type SuggestedSubscriptions = {
[SuggestedType]: string,
};
declare type SubscriptionState = {
enabledChannelNotifications: Array<string>,
subscriptions: Array<Subscription>,
unread: UnreadSubscriptions,
loading: boolean,
viewMode: ViewMode,
suggested: SuggestedSubscriptions,
loadingSuggested: boolean,
firstRunCompleted: boolean,
showSuggestedSubs: boolean,
};
//
// Action types
//
declare type DoChannelSubscriptionEnableNotifications = {
type: ACTIONS.CHANNEL_SUBSCRIPTION_ENABLE_NOTIFICATIONS,
data: string,
};
declare type DoChannelSubscriptionDisableNotifications = {
type: ACTIONS.CHANNEL_SUBSCRIPTION_DISABLE_NOTIFICATIONS,
data: string,
};
declare type DoChannelSubscribe = {
type: ACTIONS.CHANNEL_SUBSCRIBE,
data: Subscription,
};
declare type DoChannelUnsubscribe = {
type: ACTIONS.CHANNEL_UNSUBSCRIBE,
data: Subscription,
};
declare type DoUpdateSubscriptionUnreads = {
type: ACTIONS.UPDATE_SUBSCRIPTION_UNREADS,
data: {
channel: string,
uris: Array<string>,
type?: SubscriptionNotificationType,
},
};
declare type DoRemoveSubscriptionUnreads = {
type: ACTIONS.REMOVE_SUBSCRIPTION_UNREADS,
data: {
channel: string,
uris: Array<string>,
},
};
declare type SetSubscriptionLatest = {
type: ACTIONS.SET_SUBSCRIPTION_LATEST,
data: {
subscription: Subscription,
uri: string,
},
};
declare type CheckSubscriptionStarted = {
type: ACTIONS.CHECK_SUBSCRIPTION_STARTED,
};
declare type CheckSubscriptionCompleted = {
type: ACTIONS.CHECK_SUBSCRIPTION_COMPLETED,
};
declare type FetchedSubscriptionsSucess = {
type: ACTIONS.FETCH_SUBSCRIPTIONS_SUCCESS,
data: Array<Subscription>,
};
declare type SetViewMode = {
type: ACTIONS.SET_VIEW_MODE,
data: ViewMode,
};
declare type GetSuggestedSubscriptionsSuccess = {
type: ACTIONS.GET_SUGGESTED_SUBSCRIPTIONS_START,
data: SuggestedSubscriptions,
};
declare type SubscriptionAction =
| DoChannelSubscribe
| DoChannelUnsubscribe
| DoUpdateSubscriptionUnreads
| DoRemoveSubscriptionUnreads
| SetSubscriptionLatest
| CheckSubscriptionStarted
| CheckSubscriptionCompleted
| SetViewMode
| Function;
declare type SubscriptionDispatch = Dispatch<SubscriptionAction>;

23
flow-typed/User.js vendored Normal file
View file

@ -0,0 +1,23 @@
// @flow
declare type User = {
created_at: string,
family_name: ?string,
given_name: ?string,
groups: Array<string>,
has_verified_email: boolean,
id: number,
invite_reward_claimed: boolean,
invited_at: ?number,
invited_by_id: number,
invites_remaining: number,
is_email_enabled: boolean,
is_identity_verified: boolean,
is_reward_approved: boolean,
language: string,
manual_approval_user_id: ?number,
primary_email: string,
reward_status_change_trigger: string,
updated_at: string,
youtube_channels: ?Array<string>,
};

View file

@ -57,7 +57,9 @@
"lint-staged": "^7.0.4", "lint-staged": "^7.0.4",
"prettier": "^1.4.2", "prettier": "^1.4.2",
"rollup": "^1.8.0", "rollup": "^1.8.0",
"rollup-plugin-alias": "^2.0.0",
"rollup-plugin-babel": "^4.3.2", "rollup-plugin-babel": "^4.3.2",
"rollup-plugin-copy": "^3.1.0",
"rollup-plugin-flow": "^1.1.1", "rollup-plugin-flow": "^1.1.1",
"rollup-plugin-includepaths": "^0.2.3", "rollup-plugin-includepaths": "^0.2.3",
"webpack": "^4.5.0", "webpack": "^4.5.0",

View file

@ -1,27 +1,38 @@
import babel from 'rollup-plugin-babel'; import babel from 'rollup-plugin-babel';
import flow from 'rollup-plugin-flow'; import flow from 'rollup-plugin-flow';
import includePaths from 'rollup-plugin-includepaths'; import includePaths from 'rollup-plugin-includepaths';
import copy from 'rollup-plugin-copy';
import alias from 'rollup-plugin-alias';
let includePathOptions = { let includePathOptions = {
include: {}, include: {},
paths: ['src'], paths: ['src'],
external: [], external: [],
extensions: ['.js'] extensions: ['.js'],
}; };
export default { export default {
input: 'src/index.js', input: 'src/index.js',
output: { output: {
file: 'dist/bundle.es.js', file: 'dist/bundle.es.js',
format: 'cjs' format: 'cjs',
}, },
plugins: [ plugins: [
alias({
entries: [
{
find: 'flow-typed',
replacement: './flow-typed',
},
],
}),
flow({ all: true }), flow({ all: true }),
includePaths(includePathOptions), includePaths(includePathOptions),
babel({ babel({
babelrc: false, babelrc: false,
presets: [], presets: [],
}), }),
copy({ targets: [{ src: './flow-typed', dest: 'dist' }] }),
], ],
external: ['lbry-redux'] external: ['lbry-redux'],
} };

View file

@ -3,6 +3,9 @@ import Lbryio from 'lbryio';
import rewards from 'rewards'; import rewards from 'rewards';
import subscriptionsReducer from 'redux/reducers/subscriptions'; import subscriptionsReducer from 'redux/reducers/subscriptions';
// middleware
export { userStateSyncMiddleware } from 'redux/middleware/sync';
// constants // constants
export { LBRYINC_ACTIONS }; export { LBRYINC_ACTIONS };

View file

@ -1,13 +1,5 @@
// @flow // @flow
import type { GetState } from 'types/redux'; import type { SubscriptionDispatch } from 'flow-typed/Subscription';
import type {
Dispatch as ReduxDispatch,
SubscriptionState,
Subscription,
SubscriptionNotificationType,
ViewMode,
UnreadSubscription,
} from 'types/subscription';
import { PAGE_SIZE } from 'constants/claim'; import { PAGE_SIZE } from 'constants/claim';
import { doClaimRewardType } from 'redux/actions/rewards'; import { doClaimRewardType } from 'redux/actions/rewards';
import { selectSubscriptions, selectUnreadByChannel } from 'redux/selectors/subscriptions'; import { selectSubscriptions, selectUnreadByChannel } from 'redux/selectors/subscriptions';
@ -20,14 +12,14 @@ import rewards from 'rewards';
const CHECK_SUBSCRIPTIONS_INTERVAL = 15 * 60 * 1000; const CHECK_SUBSCRIPTIONS_INTERVAL = 15 * 60 * 1000;
const SUBSCRIPTION_DOWNLOAD_LIMIT = 1; const SUBSCRIPTION_DOWNLOAD_LIMIT = 1;
export const doSetViewMode = (viewMode: ViewMode) => (dispatch: ReduxDispatch) => export const doSetViewMode = (viewMode: ViewMode) => (dispatch: SubscriptionDispatch) =>
dispatch({ dispatch({
type: ACTIONS.SET_VIEW_MODE, type: ACTIONS.SET_VIEW_MODE,
data: viewMode, data: viewMode,
}); });
export const setSubscriptionLatest = (subscription: Subscription, uri: string) => ( export const setSubscriptionLatest = (subscription: Subscription, uri: string) => (
dispatch: ReduxDispatch dispatch: SubscriptionDispatch
) => ) =>
dispatch({ dispatch({
type: ACTIONS.SET_SUBSCRIPTION_LATEST, type: ACTIONS.SET_SUBSCRIPTION_LATEST,
@ -42,7 +34,7 @@ export const doUpdateUnreadSubscriptions = (
channelUri: string, channelUri: string,
uris: ?Array<string>, uris: ?Array<string>,
type: ?SubscriptionNotificationType type: ?SubscriptionNotificationType
) => (dispatch: ReduxDispatch, getState: GetState) => { ) => (dispatch: SubscriptionDispatch, getState: GetState) => {
const state = getState(); const state = getState();
const unreadByChannel = selectUnreadByChannel(state); const unreadByChannel = selectUnreadByChannel(state);
const currentUnreadForChannel: UnreadSubscription = unreadByChannel[channelUri]; const currentUnreadForChannel: UnreadSubscription = unreadByChannel[channelUri];
@ -84,7 +76,7 @@ export const doUpdateUnreadSubscriptions = (
// Remove multiple files (or all) from a channels unread subscriptions // Remove multiple files (or all) from a channels unread subscriptions
export const doRemoveUnreadSubscriptions = (channelUri: ?string, readUris: ?Array<string>) => ( export const doRemoveUnreadSubscriptions = (channelUri: ?string, readUris: ?Array<string>) => (
dispatch: ReduxDispatch, dispatch: SubscriptionDispatch,
getState: GetState getState: GetState
) => { ) => {
const state = getState(); const state = getState();
@ -133,13 +125,13 @@ export const doRemoveUnreadSubscriptions = (channelUri: ?string, readUris: ?Arra
// Remove a single file from a channels unread subscriptions // Remove a single file from a channels unread subscriptions
export const doRemoveUnreadSubscription = (channelUri: string, readUri: string) => ( export const doRemoveUnreadSubscription = (channelUri: string, readUri: string) => (
dispatch: ReduxDispatch dispatch: SubscriptionDispatch
) => { ) => {
dispatch(doRemoveUnreadSubscriptions(channelUri, [readUri])); dispatch(doRemoveUnreadSubscriptions(channelUri, [readUri]));
}; };
export const doCheckSubscription = (subscriptionUri: string, shouldNotify?: boolean) => ( export const doCheckSubscription = (subscriptionUri: string, shouldNotify?: boolean) => (
dispatch: ReduxDispatch, dispatch: SubscriptionDispatch,
getState: GetState getState: GetState
) => { ) => {
// no dispatching FETCH_CHANNEL_CLAIMS_STARTED; causes loading issues on <SubscriptionsPage> // no dispatching FETCH_CHANNEL_CLAIMS_STARTED; causes loading issues on <SubscriptionsPage>
@ -238,7 +230,7 @@ export const doCheckSubscription = (subscriptionUri: string, shouldNotify?: bool
}; };
export const doChannelSubscribe = (subscription: Subscription) => ( export const doChannelSubscribe = (subscription: Subscription) => (
dispatch: ReduxDispatch, dispatch: SubscriptionDispatch,
getState: GetState getState: GetState
) => { ) => {
const { const {
@ -275,7 +267,7 @@ export const doChannelSubscribe = (subscription: Subscription) => (
}; };
export const doChannelUnsubscribe = (subscription: Subscription) => ( export const doChannelUnsubscribe = (subscription: Subscription) => (
dispatch: ReduxDispatch, dispatch: SubscriptionDispatch,
getState: GetState getState: GetState
) => { ) => {
const { const {
@ -296,7 +288,7 @@ export const doChannelUnsubscribe = (subscription: Subscription) => (
} }
}; };
export const doCheckSubscriptions = () => (dispatch: ReduxDispatch, getState: GetState) => { export const doCheckSubscriptions = () => (dispatch: SubscriptionDispatch, getState: GetState) => {
const state = getState(); const state = getState();
const subscriptions = selectSubscriptions(state); const subscriptions = selectSubscriptions(state);
@ -305,7 +297,10 @@ export const doCheckSubscriptions = () => (dispatch: ReduxDispatch, getState: Ge
}); });
}; };
export const doFetchMySubscriptions = () => (dispatch: ReduxDispatch, getState: GetState) => { export const doFetchMySubscriptions = () => (
dispatch: SubscriptionDispatch,
getState: GetState
) => {
const state: { subscriptions: SubscriptionState, settings: any } = getState(); const state: { subscriptions: SubscriptionState, settings: any } = getState();
const { subscriptions: reduxSubscriptions } = state.subscriptions; const { subscriptions: reduxSubscriptions } = state.subscriptions;
@ -386,7 +381,7 @@ export const doFetchMySubscriptions = () => (dispatch: ReduxDispatch, getState:
}); });
}; };
export const doCheckSubscriptionsInit = () => (dispatch: ReduxDispatch) => { export const doCheckSubscriptionsInit = () => (dispatch: SubscriptionDispatch) => {
// doCheckSubscriptionsInit is called by doDaemonReady // doCheckSubscriptionsInit is called by doDaemonReady
// setTimeout below is a hack to ensure redux is hydrated when subscriptions are checked // setTimeout below is a hack to ensure redux is hydrated when subscriptions are checked
// this will be replaced with <PersistGate> which reqiures a package upgrade // this will be replaced with <PersistGate> which reqiures a package upgrade
@ -402,7 +397,7 @@ export const doCheckSubscriptionsInit = () => (dispatch: ReduxDispatch) => {
setInterval(() => dispatch(doCheckSubscriptions()), CHECK_SUBSCRIPTIONS_INTERVAL); setInterval(() => dispatch(doCheckSubscriptions()), CHECK_SUBSCRIPTIONS_INTERVAL);
}; };
export const doFetchRecommendedSubscriptions = () => (dispatch: ReduxDispatch) => { export const doFetchRecommendedSubscriptions = () => (dispatch: SubscriptionDispatch) => {
dispatch({ dispatch({
type: ACTIONS.GET_SUGGESTED_SUBSCRIPTIONS_START, type: ACTIONS.GET_SUGGESTED_SUBSCRIPTIONS_START,
}); });
@ -422,18 +417,18 @@ export const doFetchRecommendedSubscriptions = () => (dispatch: ReduxDispatch) =
); );
}; };
export const doCompleteFirstRun = () => (dispatch: ReduxDispatch) => export const doCompleteFirstRun = () => (dispatch: SubscriptionDispatch) =>
dispatch({ dispatch({
type: ACTIONS.SUBSCRIPTION_FIRST_RUN_COMPLETED, type: ACTIONS.SUBSCRIPTION_FIRST_RUN_COMPLETED,
}); });
export const doShowSuggestedSubs = () => (dispatch: ReduxDispatch) => export const doShowSuggestedSubs = () => (dispatch: SubscriptionDispatch) =>
dispatch({ dispatch({
type: ACTIONS.VIEW_SUGGESTED_SUBSCRIPTIONS, type: ACTIONS.VIEW_SUGGESTED_SUBSCRIPTIONS,
}); });
export const doChannelSubscriptionEnableNotifications = (channelName: string) => ( export const doChannelSubscriptionEnableNotifications = (channelName: string) => (
dispatch: ReduxDispatch dispatch: SubscriptionDispatch
) => ) =>
dispatch({ dispatch({
type: ACTIONS.CHANNEL_SUBSCRIPTION_ENABLE_NOTIFICATIONS, type: ACTIONS.CHANNEL_SUBSCRIPTION_ENABLE_NOTIFICATIONS,
@ -441,7 +436,7 @@ export const doChannelSubscriptionEnableNotifications = (channelName: string) =>
}); });
export const doChannelSubscriptionDisableNotifications = (channelName: string) => ( export const doChannelSubscriptionDisableNotifications = (channelName: string) => (
dispatch: ReduxDispatch dispatch: SubscriptionDispatch
) => ) =>
dispatch({ dispatch({
type: ACTIONS.CHANNEL_SUBSCRIPTION_DISABLE_NOTIFICATIONS, type: ACTIONS.CHANNEL_SUBSCRIPTION_DISABLE_NOTIFICATIONS,

View file

@ -0,0 +1,58 @@
import {
ACTIONS as LBRY_REDUX_ACTIONS,
makeSelectIsFollowingTag,
selectFollowedTags,
} from 'lbry-redux';
import Lbryio from 'lbryio';
import * as ACTIONS from 'constants/action_types';
import { selectSubscriptions } from 'redux/selectors/subscriptions';
const persistShape = {
version: '0',
shared: {},
};
export function userStateSyncMiddleware() {
return ({ getState }) => next => action => {
if (
action.type === ACTIONS.CHANNEL_SUBSCRIBE ||
action.type === ACTIONS.CHANNEL_UNSUBSCRIBE ||
action.type === LBRY_REDUX_ACTIONS.TOGGLE_TAG_FOLLOW
) {
const newShape = { ...persistShape };
const state = getState();
const subscriptions = selectSubscriptions(state).map(({ uri }) => uri);
const tags = selectFollowedTags(state);
newShape.shared.subscriptions = subscriptions;
newShape.shared.tags = tags;
const { uri } = action.data;
if (action.type === ACTIONS.CHANNEL_SUBSCRIBE) {
const newSubscriptions = subscriptions.slice();
newSubscriptions.push(uri);
newShape.shared.subscriptions = newSubscriptions;
} else if (action.type === ACTIONS.CHANNEL_UNSUBSCRIBE) {
let newSubscriptions = subscriptions.slice();
newSubscriptions = newSubscriptions.filter(subscribedUri => subscribedUri !== uri);
newShape.shared.subscriptions = newSubscriptions;
} else {
const toggledTag = action.data.name;
const followedTags = selectFollowedTags(state).map(({ name }) => name);
const isFollowing = makeSelectIsFollowingTag(toggledTag)(state);
let newTags = followedTags.slice();
if (isFollowing) {
newTags = newTags.filter(followedTag => followedTag.name !== toggledTag);
} else {
newTags.push(toggledTag);
}
newShape.shared.tags = newTags;
}
Lbryio.call('user_settings', 'set', { settings: newShape });
}
return next(action);
};
}

View file

@ -2,20 +2,6 @@
import * as ACTIONS from 'constants/action_types'; import * as ACTIONS from 'constants/action_types';
import { VIEW_ALL } from 'constants/subscriptions'; import { VIEW_ALL } from 'constants/subscriptions';
import { handleActions } from 'util/redux-utils'; import { handleActions } from 'util/redux-utils';
import type {
SubscriptionState,
Subscription,
DoChannelSubscribe,
DoChannelUnsubscribe,
DoChannelSubscriptionEnableNotifications,
DoChannelSubscriptionDisableNotifications,
SetSubscriptionLatest,
DoUpdateSubscriptionUnreads,
DoRemoveSubscriptionUnreads,
FetchedSubscriptionsSucess,
SetViewMode,
GetSuggestedSubscriptionsSuccess,
} from 'types/subscription';
const defaultState: SubscriptionState = { const defaultState: SubscriptionState = {
enabledChannelNotifications: [], enabledChannelNotifications: [],

View file

@ -20,6 +20,9 @@ module.exports = {
}, },
resolve: { resolve: {
modules: [path.resolve(__dirname, 'src'), 'node_modules'], modules: [path.resolve(__dirname, 'src'), 'node_modules'],
alias: {
'flow-typed': path.resolve(__dirname, './flow-typed'),
},
}, },
externals: 'lbry-redux', externals: 'lbry-redux',
}; };

226
yarn.lock
View file

@ -642,6 +642,27 @@
lodash "^4.17.11" lodash "^4.17.11"
to-fast-properties "^2.0.0" to-fast-properties "^2.0.0"
"@nodelib/fs.scandir@2.1.2":
version "2.1.2"
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.2.tgz#1f981cd5b83e85cfdeb386fc693d4baab392fa54"
integrity sha512-wrIBsjA5pl13f0RN4Zx4FNWmU71lv03meGKnqRUoCyan17s4V3WL92f3w3AIuWbNnpcrQyFBU5qMavJoB8d27w==
dependencies:
"@nodelib/fs.stat" "2.0.2"
run-parallel "^1.1.9"
"@nodelib/fs.stat@2.0.2", "@nodelib/fs.stat@^2.0.1":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.2.tgz#2762aea8fe78ea256860182dcb52d61ee4b8fda6"
integrity sha512-z8+wGWV2dgUhLqrtRYa03yDx4HWMvXKi1z8g3m2JyxAx8F7xk74asqPk5LAETjqDSGLFML/6CDl0+yFunSYicw==
"@nodelib/fs.walk@^1.2.1":
version "1.2.3"
resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.3.tgz#a555dc256acaf00c62b0db29529028dd4d4cb141"
integrity sha512-l6t8xEhfK9Sa4YO5mIRdau7XSOADfmh3jCr0evNHdY+HNkW6xuQhgMH7D73VV6WpZOagrW0UludvMTiifiwTfA==
dependencies:
"@nodelib/fs.scandir" "2.1.2"
fastq "^1.6.0"
"@samverschueren/stream-to-observable@^0.3.0": "@samverschueren/stream-to-observable@^0.3.0":
version "0.3.0" version "0.3.0"
resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f" resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f"
@ -652,6 +673,37 @@
version "0.0.39" version "0.0.39"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
"@types/events@*":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==
"@types/fs-extra@^8.0.0":
version "8.0.0"
resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-8.0.0.tgz#d3e2c313ca29f95059f198dd60d1f774642d4b25"
integrity sha512-bCtL5v9zdbQW86yexOlXWTEGvLNqWxMFyi7gQA7Gcthbezr2cPSOb8SkESVKA937QD5cIwOFLDFt0MQoXOEr9Q==
dependencies:
"@types/node" "*"
"@types/glob@^7.1.1":
version "7.1.1"
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575"
integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==
dependencies:
"@types/events" "*"
"@types/minimatch" "*"
"@types/node" "*"
"@types/minimatch@*":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
"@types/node@*":
version "12.7.5"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.5.tgz#e19436e7f8e9b4601005d73673b6dc4784ffcc2f"
integrity sha512-9fq4jZVhPNW8r+UYKnxF1e2HkDWOWKM5bC2/7c9wPV835I0aOrVbS/Hw/pWPk2uKrNXQqg9Z959Kz+IYDd5p3w==
"@types/node@^11.11.6": "@types/node@^11.11.6":
version "11.13.0" version "11.13.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-11.13.0.tgz#b0df8d6ef9b5001b2be3a94d909ce3c29a80f9e1" resolved "https://registry.yarnpkg.com/@types/node/-/node-11.13.0.tgz#b0df8d6ef9b5001b2be3a94d909ce3c29a80f9e1"
@ -974,6 +1026,11 @@ array-union@^1.0.1:
dependencies: dependencies:
array-uniq "^1.0.1" array-uniq "^1.0.1"
array-union@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
array-uniq@^1.0.1: array-uniq@^1.0.1:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
@ -1154,6 +1211,13 @@ braces@^2.3.1, braces@^2.3.2:
split-string "^3.0.2" split-string "^3.0.2"
to-regex "^3.0.1" to-regex "^3.0.1"
braces@^3.0.1:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
dependencies:
fill-range "^7.0.1"
brorand@^1.0.1: brorand@^1.0.1:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
@ -1477,6 +1541,11 @@ color-name@1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689"
colorette@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.1.0.tgz#1f943e5a357fac10b4e0f5aaef3b14cdc1af6ec7"
integrity sha512-6S062WDQUXi6hOfkO/sBPVwE5ASXY4G2+b4atvhJfSsuUUhIaUKlkjLe9692Ipyt5/a+IPF5aVTu3V5gvXq5cg==
colors@^1.1.2: colors@^1.1.2:
version "1.3.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.0.tgz#5f20c9fef6945cb1134260aab33bfbdc8295e04e" resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.0.tgz#5f20c9fef6945cb1134260aab33bfbdc8295e04e"
@ -1790,6 +1859,13 @@ diffie-hellman@^5.0.0:
miller-rabin "^4.0.0" miller-rabin "^4.0.0"
randombytes "^2.0.0" randombytes "^2.0.0"
dir-glob@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==
dependencies:
path-type "^4.0.0"
doctrine@1.5.0: doctrine@1.5.0:
version "1.5.0" version "1.5.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"
@ -2201,6 +2277,18 @@ fast-diff@^1.1.1:
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.1.2.tgz#4b62c42b8e03de3f848460b639079920695d0154" resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.1.2.tgz#4b62c42b8e03de3f848460b639079920695d0154"
fast-glob@^3.0.3:
version "3.0.4"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.0.4.tgz#d484a41005cb6faeb399b951fd1bd70ddaebb602"
integrity sha512-wkIbV6qg37xTJwqSsdnIphL1e+LaGz4AIQqr00mIubMaEhv1/HEmJ0uuCGZRNRUkZZmOB5mJKO0ZUTVq+SxMQg==
dependencies:
"@nodelib/fs.stat" "^2.0.1"
"@nodelib/fs.walk" "^1.2.1"
glob-parent "^5.0.0"
is-glob "^4.0.1"
merge2 "^1.2.3"
micromatch "^4.0.2"
fast-json-stable-stringify@^2.0.0: fast-json-stable-stringify@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
@ -2209,6 +2297,13 @@ fast-levenshtein@~2.0.4:
version "2.0.6" version "2.0.6"
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
fastq@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.6.0.tgz#4ec8a38f4ac25f21492673adb7eae9cfef47d1c2"
integrity sha512-jmxqQ3Z/nXoeyDmWAzF9kH1aGZSis6e/SbfPmJpUnyZ0ogr6iscHQaml4wsEepEWSdtmpy+eVXmCRIMpxaXqOA==
dependencies:
reusify "^1.0.0"
figgy-pudding@^3.5.1: figgy-pudding@^3.5.1:
version "3.5.1" version "3.5.1"
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790"
@ -2243,6 +2338,13 @@ fill-range@^4.0.0:
repeat-string "^1.6.1" repeat-string "^1.6.1"
to-regex-range "^2.1.0" to-regex-range "^2.1.0"
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
dependencies:
to-regex-range "^5.0.1"
find-babel-config@^1.1.0: find-babel-config@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/find-babel-config/-/find-babel-config-1.1.0.tgz#acc01043a6749fec34429be6b64f542ebb5d6355" resolved "https://registry.yarnpkg.com/find-babel-config/-/find-babel-config-1.1.0.tgz#acc01043a6749fec34429be6b64f542ebb5d6355"
@ -2370,6 +2472,15 @@ fs-extra@^5.0.0:
jsonfile "^4.0.0" jsonfile "^4.0.0"
universalify "^0.1.0" universalify "^0.1.0"
fs-extra@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
dependencies:
graceful-fs "^4.2.0"
jsonfile "^4.0.0"
universalify "^0.1.0"
fs-minipass@^1.2.5: fs-minipass@^1.2.5:
version "1.2.6" version "1.2.6"
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07"
@ -2476,6 +2587,13 @@ glob-parent@^3.1.0:
is-glob "^3.1.0" is-glob "^3.1.0"
path-dirname "^1.0.0" path-dirname "^1.0.0"
glob-parent@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.0.0.tgz#1dc99f0f39b006d3e92c2c284068382f0c20e954"
integrity sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==
dependencies:
is-glob "^4.0.1"
glob@^7.0.3, glob@^7.1.2: glob@^7.0.3, glob@^7.1.2:
version "7.1.2" version "7.1.2"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
@ -2538,6 +2656,20 @@ globals@^11.0.1, globals@^11.1.0:
version "11.7.0" version "11.7.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-11.7.0.tgz#a583faa43055b1aca771914bf68258e2fc125673" resolved "https://registry.yarnpkg.com/globals/-/globals-11.7.0.tgz#a583faa43055b1aca771914bf68258e2fc125673"
globby@10.0.1:
version "10.0.1"
resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.1.tgz#4782c34cb75dd683351335c5829cc3420e606b22"
integrity sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==
dependencies:
"@types/glob" "^7.1.1"
array-union "^2.1.0"
dir-glob "^3.0.1"
fast-glob "^3.0.3"
glob "^7.1.3"
ignore "^5.1.1"
merge2 "^1.2.3"
slash "^3.0.0"
globby@^5.0.0: globby@^5.0.0:
version "5.0.0" version "5.0.0"
resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d"
@ -2568,7 +2700,7 @@ got@^7.1.0:
url-parse-lax "^1.0.0" url-parse-lax "^1.0.0"
url-to-options "^1.0.1" url-to-options "^1.0.1"
graceful-fs@^4.1.11, graceful-fs@^4.1.15: graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.2.0:
version "4.2.2" version "4.2.2"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02"
integrity sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q== integrity sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==
@ -2717,6 +2849,11 @@ ignore@^3.3.3:
version "3.3.10" version "3.3.10"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043"
ignore@^5.1.1:
version "5.1.4"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.4.tgz#84b7b3dbe64552b6ef0eca99f6743dbec6d97adf"
integrity sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==
import-local@2.0.0: import-local@2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d"
@ -2945,12 +3082,24 @@ is-glob@^4.0.0:
dependencies: dependencies:
is-extglob "^2.1.1" is-extglob "^2.1.1"
is-glob@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
dependencies:
is-extglob "^2.1.1"
is-number@^3.0.0: is-number@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
dependencies: dependencies:
kind-of "^3.0.2" kind-of "^3.0.2"
is-number@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
is-obj@^1.0.1: is-obj@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
@ -2992,6 +3141,13 @@ is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4:
dependencies: dependencies:
isobject "^3.0.1" isobject "^3.0.1"
is-plain-object@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-3.0.0.tgz#47bfc5da1b5d50d64110806c199359482e75a928"
integrity sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==
dependencies:
isobject "^4.0.0"
is-promise@^2.1.0: is-promise@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
@ -3072,6 +3228,11 @@ isobject@^3.0.0, isobject@^3.0.1:
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
isobject@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0"
integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==
isurl@^1.0.0-alpha5: isurl@^1.0.0-alpha5:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67"
@ -3468,6 +3629,11 @@ memory-fs@^0.4.0, memory-fs@^0.4.1:
errno "^0.1.3" errno "^0.1.3"
readable-stream "^2.0.1" readable-stream "^2.0.1"
merge2@^1.2.3:
version "1.3.0"
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.3.0.tgz#5b366ee83b2f1582c48f87e47cf1a9352103ca81"
integrity sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==
micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8: micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8:
version "3.1.10" version "3.1.10"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
@ -3486,6 +3652,14 @@ micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8:
snapdragon "^0.8.1" snapdragon "^0.8.1"
to-regex "^3.0.2" to-regex "^3.0.2"
micromatch@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259"
integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==
dependencies:
braces "^3.0.1"
picomatch "^2.0.5"
miller-rabin@^4.0.0: miller-rabin@^4.0.0:
version "4.0.1" version "4.0.1"
resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d"
@ -4073,6 +4247,11 @@ path-type@^2.0.0:
dependencies: dependencies:
pify "^2.0.0" pify "^2.0.0"
path-type@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
pbkdf2@^3.0.3: pbkdf2@^3.0.3:
version "3.0.16" version "3.0.16"
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.16.tgz#7404208ec6b01b62d85bf83853a8064f8d9c2a5c" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.16.tgz#7404208ec6b01b62d85bf83853a8064f8d9c2a5c"
@ -4083,6 +4262,11 @@ pbkdf2@^3.0.3:
safe-buffer "^5.0.1" safe-buffer "^5.0.1"
sha.js "^2.4.8" sha.js "^2.4.8"
picomatch@^2.0.5:
version "2.0.7"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.0.7.tgz#514169d8c7cd0bdbeecc8a2609e34a7163de69f6"
integrity sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==
pify@^2.0.0: pify@^2.0.0:
version "2.3.0" version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
@ -4496,6 +4680,11 @@ ret@~0.1.10:
version "0.1.15" version "0.1.15"
resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
reusify@^1.0.0:
version "1.0.4"
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
rimraf@2, rimraf@^2.2.8, rimraf@^2.6.2: rimraf@2, rimraf@^2.2.8, rimraf@^2.6.2:
version "2.6.3" version "2.6.3"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
@ -4516,6 +4705,13 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
hash-base "^3.0.0" hash-base "^3.0.0"
inherits "^2.0.1" inherits "^2.0.1"
rollup-plugin-alias@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/rollup-plugin-alias/-/rollup-plugin-alias-2.0.0.tgz#eea77466a8a8a063007c8edb2e9d7a3d06cbb889"
integrity sha512-JVwxV9nwzjc0q7JmrV9jvZlJ8FykNd5r7RmYps8i/7v+vcmmVpeMvXrljhCboYErf4VZ2z9FEj+fP7eJlEGfug==
dependencies:
slash "^3.0.0"
rollup-plugin-babel@^4.3.2: rollup-plugin-babel@^4.3.2:
version "4.3.2" version "4.3.2"
resolved "https://registry.yarnpkg.com/rollup-plugin-babel/-/rollup-plugin-babel-4.3.2.tgz#8c0e1bd7aa9826e90769cf76895007098ffd1413" resolved "https://registry.yarnpkg.com/rollup-plugin-babel/-/rollup-plugin-babel-4.3.2.tgz#8c0e1bd7aa9826e90769cf76895007098ffd1413"
@ -4523,6 +4719,17 @@ rollup-plugin-babel@^4.3.2:
"@babel/helper-module-imports" "^7.0.0" "@babel/helper-module-imports" "^7.0.0"
rollup-pluginutils "^2.3.0" rollup-pluginutils "^2.3.0"
rollup-plugin-copy@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/rollup-plugin-copy/-/rollup-plugin-copy-3.1.0.tgz#435589857f4e346ea63fc137d99dcc1b25f51c3b"
integrity sha512-oVw3ljRV5jv7Yw/6eCEHntVs9Mc+NFglc0iU0J8ei76gldYmtBQ0M/j6WAkZUFVRSrhgfCrEakUllnN87V2f4w==
dependencies:
"@types/fs-extra" "^8.0.0"
colorette "^1.1.0"
fs-extra "^8.1.0"
globby "10.0.1"
is-plain-object "^3.0.0"
rollup-plugin-flow@^1.1.1: rollup-plugin-flow@^1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/rollup-plugin-flow/-/rollup-plugin-flow-1.1.1.tgz#6ce568f1dd559666b77ab76b4bae251407528db6" resolved "https://registry.yarnpkg.com/rollup-plugin-flow/-/rollup-plugin-flow-1.1.1.tgz#6ce568f1dd559666b77ab76b4bae251407528db6"
@ -4562,6 +4769,11 @@ run-async@^2.2.0:
dependencies: dependencies:
is-promise "^2.1.0" is-promise "^2.1.0"
run-parallel@^1.1.9:
version "1.1.9"
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679"
integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==
run-queue@^1.0.0, run-queue@^1.0.3: run-queue@^1.0.0, run-queue@^1.0.3:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47"
@ -4682,6 +4894,11 @@ signal-exit@^3.0.0, signal-exit@^3.0.2:
version "3.0.2" version "3.0.2"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
slash@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
slice-ansi@0.0.4: slice-ansi@0.0.4:
version "0.0.4" version "0.0.4"
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
@ -5064,6 +5281,13 @@ to-regex-range@^2.1.0:
is-number "^3.0.0" is-number "^3.0.0"
repeat-string "^1.6.1" repeat-string "^1.6.1"
to-regex-range@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
dependencies:
is-number "^7.0.0"
to-regex@^3.0.1, to-regex@^3.0.2: to-regex@^3.0.1, to-regex@^3.0.2:
version "3.0.2" version "3.0.2"
resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce"