lbry-desktop/ui/redux/selectors/subscriptions.js

162 lines
5.8 KiB
JavaScript

import { SUGGESTED_FEATURED, SUGGESTED_TOP_SUBSCRIBED } from 'constants/subscriptions';
import { createSelector } from 'reselect';
import { createCachedSelector } from 're-reselect';
import { parseURI, isURIEqual } from 'util/lbryURI';
import {
selectAllFetchingChannelClaims,
makeSelectChannelForClaimUri,
makeSelectClaimForUri,
selectClaimForUri,
} from 'redux/selectors/claims';
import { swapKeyAndValue } from 'util/swap-json';
import { getChannelFromClaim, isChannelClaim } from 'util/claim';
// Returns the entire subscriptions state
const selectState = (state) => state.subscriptions || {};
// Returns the list of channel uris a user is subscribed to
export const selectSubscriptions = createSelector(
selectState,
(state) => state.subscriptions && state.subscriptions.sort((a, b) => a.channelName.localeCompare(b.channelName))
);
export const selectSubscriptionUris = createSelector(
selectSubscriptions,
(subscriptions) => subscriptions && subscriptions.map((sub) => sub.uri)
);
export const selectFollowing = createSelector(selectState, (state) => state.following && state.following);
// Fetching list of users subscriptions
export const selectIsFetchingSubscriptions = createSelector(selectState, (state) => state.loading);
// The current view mode on the subscriptions page
export const selectViewMode = createSelector(selectState, (state) => state.viewMode);
// Suggested subscriptions from internal apis
export const selectSuggested = createSelector(selectState, (state) => state.suggested);
export const selectIsFetchingSuggested = createSelector(selectState, (state) => state.loadingSuggested);
export const selectSuggestedChannels = 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],
}));
}
);
export const selectFirstRunCompleted = createSelector(selectState, (state) => state.firstRunCompleted);
export const selectshowSuggestedSubs = createSelector(selectState, (state) => state.showSuggestedSubs);
// Fetching any claims that are a part of a users subscriptions
export const selectSubscriptionsBeingFetched = createSelector(
selectSubscriptions,
selectAllFetchingChannelClaims,
(subscriptions, fetchingChannelClaims) => {
const fetchingSubscriptionMap = {};
subscriptions.forEach((sub) => {
const isFetching = fetchingChannelClaims && fetchingChannelClaims[sub.uri];
if (isFetching) {
fetchingSubscriptionMap[sub.uri] = true;
}
});
return fetchingSubscriptionMap;
}
);
// Returns true if a user is subscribed to the channel associated with the uri passed in
// Accepts content or channel uris
export const makeSelectChannelInSubscriptions = (uri) =>
createSelector(selectSubscriptions, (subscriptions) => subscriptions.some((sub) => sub.uri === uri));
export const selectIsSubscribedForUri = createCachedSelector(
(state, uri) => uri,
selectClaimForUri,
selectSubscriptions,
(uri, claim, subscriptions) => {
const channelClaim = getChannelFromClaim(claim);
if (channelClaim) {
const permanentUrl = channelClaim.permanent_url;
return subscriptions.some((sub) => isURIEqual(sub.uri, permanentUrl));
}
// If it failed, it could be an abandoned channel. Try parseURI:
if (isChannelClaim(claim, uri)) {
return subscriptions.some((sub) => isURIEqual(sub.uri, uri));
}
return false;
}
)((state, uri) => String(uri));
export const makeSelectNotificationsDisabled = (uri) =>
createSelector(
selectFollowing,
makeSelectChannelForClaimUri(uri, true),
makeSelectClaimForUri(uri),
(following, channelUri, claim) => {
if (channelUri) {
return following.some((following) => following.uri === channelUri && following.notificationsDisabled);
}
// If we couldn't get a channel uri from the claim uri, the uri passed in might be a channel already
let isChannel;
try {
({ isChannel } = parseURI(uri));
} catch (e) {}
if (isChannel && claim) {
const uri = claim.permanent_url;
const disabled = following.some((sub) => {
return sub.uri === uri && sub.notificationsDisabled === true;
});
return disabled;
}
return true;
}
);