allow unsubscribe from deleted channels by navigating from sidebar

This commit is contained in:
zeppi 2021-04-28 23:44:29 -04:00 committed by jessopb
parent cba369a5ce
commit b9fc9b6319
5 changed files with 68 additions and 31 deletions

View file

@ -1886,7 +1886,6 @@
"No replays found.": "No replays found.", "No replays found.": "No replays found.",
"Replay video available": "Replay video available", "Replay video available": "Replay video available",
"Check for Replays:": "Check for Replays", "Check for Replays:": "Check for Replays",
"A thumbnail is required. Please upload or provide an image URL above.": "A thumbnail is required. Please upload or provide an image URL above.",
"You can upload your own recording or select a replay when your stream is over": "You can upload your own recording or select a replay when your stream is over", "You can upload your own recording or select a replay when your stream is over": "You can upload your own recording or select a replay when your stream is over",
"This channel isn't staking enough LBRY Credits for inline image previews.": "This channel isn't staking enough LBRY Credits for inline image previews.", "This channel isn't staking enough LBRY Credits for inline image previews.": "This channel isn't staking enough LBRY Credits for inline image previews.",
"Fromage": "Fromage", "Fromage": "Fromage",
@ -1894,6 +1893,7 @@
"Tá Rolando": "Tá Rolando", "Tá Rolando": "Tá Rolando",
"Watch content and earn more Credits for each level unlocked! 10 views required for level 1 (Current Score: 0). Only up to 10 views per day count.": "Watch content and earn more Credits for each level unlocked! 10 views required for level 1 (Current Score: 0). Only up to 10 views per day count.", "Watch content and earn more Credits for each level unlocked! 10 views required for level 1 (Current Score: 0). Only up to 10 views per day count.": "Watch content and earn more Credits for each level unlocked! 10 views required for level 1 (Current Score: 0). Only up to 10 views per day count.",
"Follow your favorite creators and earn more Credits for each level unlocked! Follow 1 creators for level 1 (Current Score: 0).": "Follow your favorite creators and earn more Credits for each level unlocked! Follow 1 creators for level 1 (Current Score: 0).", "Follow your favorite creators and earn more Credits for each level unlocked! Follow 1 creators for level 1 (Current Score: 0).": "Follow your favorite creators and earn more Credits for each level unlocked! Follow 1 creators for level 1 (Current Score: 0).",
"Video/Audio": "Video/Audio", "Channel Not Found": "Channel Not Found",
"Probably because you didn't make it.": "Probably because you didn't make it.",
"--end--": "--end--" "--end--": "--end--"
} }

View file

@ -5,6 +5,7 @@ import ChannelThumbnail from 'component/channelThumbnail';
import { parseURI } from 'lbry-redux'; import { parseURI } from 'lbry-redux';
import ChannelBlockButton from 'component/channelBlockButton'; import ChannelBlockButton from 'component/channelBlockButton';
import ChannelMuteButton from 'component/channelMuteButton'; import ChannelMuteButton from 'component/channelMuteButton';
import SubscribeButton from 'component/subscribeButton';
type Props = { type Props = {
uri: string, uri: string,
@ -30,6 +31,7 @@ function AbandonedChannelPreview(props: Props) {
<div className="section__actions"> <div className="section__actions">
<ChannelBlockButton uri={uri} /> <ChannelBlockButton uri={uri} />
<ChannelMuteButton uri={uri} /> <ChannelMuteButton uri={uri} />
<SubscribeButton uri={uri} />
</div> </div>
</div> </div>
</div> </div>

View file

@ -15,13 +15,14 @@ type SubscriptionArgs = {
type Props = { type Props = {
permanentUrl: ?string, permanentUrl: ?string,
isSubscribed: boolean, isSubscribed: boolean,
doChannelSubscribe: SubscriptionArgs => void, doChannelSubscribe: (SubscriptionArgs) => void,
doChannelUnsubscribe: SubscriptionArgs => void, doChannelUnsubscribe: (SubscriptionArgs) => void,
showSnackBarOnSubscribe: boolean, showSnackBarOnSubscribe: boolean,
doToast: ({ message: string }) => void, doToast: ({ message: string }) => void,
shrinkOnMobile: boolean, shrinkOnMobile: boolean,
notificationsDisabled: boolean, notificationsDisabled: boolean,
user: ?User, user: ?User,
uri: string,
}; };
export default function SubscribeButton(props: Props) { export default function SubscribeButton(props: Props) {
@ -35,6 +36,7 @@ export default function SubscribeButton(props: Props) {
shrinkOnMobile = false, shrinkOnMobile = false,
notificationsDisabled, notificationsDisabled,
user, user,
uri,
} = props; } = props;
const buttonRef = useRef(); const buttonRef = useRef();
@ -43,6 +45,7 @@ export default function SubscribeButton(props: Props) {
isHovering = isMobile ? true : isHovering; isHovering = isMobile ? true : isHovering;
const uiNotificationsEnabled = user && user.experimental_ui; const uiNotificationsEnabled = user && user.experimental_ui;
const { channelName: rawChannelName } = parseURI(uri);
const { channelName } = parseURI(permanentUrl); const { channelName } = parseURI(permanentUrl);
const claimName = '@' + channelName; const claimName = '@' + channelName;
@ -55,6 +58,32 @@ export default function SubscribeButton(props: Props) {
const label = isMobile && shrinkOnMobile ? '' : unfollowOverride || subscriptionLabel; const label = isMobile && shrinkOnMobile ? '' : unfollowOverride || subscriptionLabel;
const titlePrefix = isSubscribed ? __('Unfollow this channel') : __('Follow this channel'); const titlePrefix = isSubscribed ? __('Unfollow this channel') : __('Follow this channel');
if (isSubscribed && !permanentUrl && rawChannelName) {
return (
<div className="button-group">
<Button
ref={buttonRef}
iconColor="red"
largestLabel={isMobile && shrinkOnMobile ? '' : subscriptionLabel}
icon={ICONS.UNSUBSCRIBE}
button={'alt'}
requiresAuth={IS_WEB}
label={label}
title={titlePrefix}
onClick={(e) => {
e.stopPropagation();
subscriptionHandler({
channelName: '@' + rawChannelName,
uri: uri,
notificationsDisabled: true,
});
}}
/>
</div>
);
}
return permanentUrl ? ( return permanentUrl ? (
<div className="button-group"> <div className="button-group">
<Button <Button
@ -66,7 +95,7 @@ export default function SubscribeButton(props: Props) {
requiresAuth={IS_WEB} requiresAuth={IS_WEB}
label={label} label={label}
title={titlePrefix} title={titlePrefix}
onClick={e => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
subscriptionHandler({ subscriptionHandler({

View file

@ -17,18 +17,18 @@ export default handleActions(
const newSubscriptions: Array<Subscription> = state.subscriptions.slice(); const newSubscriptions: Array<Subscription> = state.subscriptions.slice();
let newFollowing: Array<Following> = state.following.slice(); let newFollowing: Array<Following> = state.following.slice();
// prevent duplicates in the sidebar // prevent duplicates in the sidebar
if (!newSubscriptions.some(sub => sub.uri === newSubscription.uri)) { if (!newSubscriptions.some((sub) => sub.uri === newSubscription.uri)) {
// $FlowFixMe // $FlowFixMe
newSubscriptions.unshift(newSubscription); newSubscriptions.unshift(newSubscription);
} }
if (!newFollowing.some(sub => sub.uri === newSubscription.uri)) { if (!newFollowing.some((sub) => sub.uri === newSubscription.uri)) {
newFollowing.unshift({ newFollowing.unshift({
uri: newSubscription.uri, uri: newSubscription.uri,
notificationsDisabled: newSubscription.notificationsDisabled, notificationsDisabled: newSubscription.notificationsDisabled,
}); });
} else { } else {
newFollowing = newFollowing.map(following => { newFollowing = newFollowing.map((following) => {
if (following.uri === newSubscription.uri) { if (following.uri === newSubscription.uri) {
return { return {
uri: newSubscription.uri, uri: newSubscription.uri,
@ -50,10 +50,12 @@ export default handleActions(
const subscriptionToRemove: Subscription = action.data; const subscriptionToRemove: Subscription = action.data;
const newSubscriptions = state.subscriptions const newSubscriptions = state.subscriptions
.slice() .slice()
.filter(subscription => subscription.channelName !== subscriptionToRemove.channelName); .filter(
(subscription) => subscription.channelName.toLowerCase() !== subscriptionToRemove.channelName.toLowerCase()
);
const newFollowing = state.following const newFollowing = state.following
.slice() .slice()
.filter(subscription => subscription.uri !== subscriptionToRemove.uri); .filter((subscription) => subscription.uri !== subscriptionToRemove.uri);
return { return {
...state, ...state,
@ -95,7 +97,7 @@ export default handleActions(
if (!subscriptions) { if (!subscriptions) {
newSubscriptions = state.subscriptions; newSubscriptions = state.subscriptions;
} else { } else {
const parsedSubscriptions = subscriptions.map(uri => { const parsedSubscriptions = subscriptions.map((uri) => {
const { channelName } = parseURI(uri); const { channelName } = parseURI(uri);
return { return {

View file

@ -9,25 +9,25 @@ import {
import { swapKeyAndValue } from 'util/swap-json'; import { swapKeyAndValue } from 'util/swap-json';
// Returns the entire subscriptions state // Returns the entire subscriptions state
const selectState = state => state.subscriptions || {}; const selectState = (state) => state.subscriptions || {};
// Returns the list of channel uris a user is subscribed to // Returns the list of channel uris a user is subscribed to
export const selectSubscriptions = createSelector( export const selectSubscriptions = createSelector(
selectState, selectState,
state => state.subscriptions && state.subscriptions.sort((a, b) => a.channelName.localeCompare(b.channelName)) (state) => state.subscriptions && state.subscriptions.sort((a, b) => a.channelName.localeCompare(b.channelName))
); );
export const selectFollowing = createSelector(selectState, state => state.following && state.following); export const selectFollowing = createSelector(selectState, (state) => state.following && state.following);
// Fetching list of users subscriptions // Fetching list of users subscriptions
export const selectIsFetchingSubscriptions = createSelector(selectState, state => state.loading); export const selectIsFetchingSubscriptions = createSelector(selectState, (state) => state.loading);
// The current view mode on the subscriptions page // The current view mode on the subscriptions page
export const selectViewMode = createSelector(selectState, state => state.viewMode); export const selectViewMode = createSelector(selectState, (state) => state.viewMode);
// Suggested subscriptions from internal apis // Suggested subscriptions from internal apis
export const selectSuggested = createSelector(selectState, state => state.suggested); export const selectSuggested = createSelector(selectState, (state) => state.suggested);
export const selectIsFetchingSuggested = createSelector(selectState, state => state.loadingSuggested); export const selectIsFetchingSuggested = createSelector(selectState, (state) => state.loadingSuggested);
export const selectSuggestedChannels = createSelector( export const selectSuggestedChannels = createSelector(
selectSubscriptions, selectSubscriptions,
selectSuggested, selectSuggested,
@ -55,7 +55,7 @@ export const selectSuggestedChannels = createSelector(
// If a uri isn't already in the suggested object, add it // If a uri isn't already in the suggested object, add it
const suggestedChannels = { ...topSubscribedSuggestions }; const suggestedChannels = { ...topSubscribedSuggestions };
Object.keys(featuredSuggestions).forEach(uri => { Object.keys(featuredSuggestions).forEach((uri) => {
if (!suggestedChannels[uri]) { if (!suggestedChannels[uri]) {
const channelLabel = featuredSuggestions[uri]; const channelLabel = featuredSuggestions[uri];
suggestedChannels[uri] = channelLabel; suggestedChannels[uri] = channelLabel;
@ -73,15 +73,15 @@ export const selectSuggestedChannels = createSelector(
} }
}); });
return Object.keys(suggestedChannels).map(uri => ({ return Object.keys(suggestedChannels).map((uri) => ({
uri, uri,
label: suggestedChannels[uri], label: suggestedChannels[uri],
})); }));
} }
); );
export const selectFirstRunCompleted = createSelector(selectState, state => state.firstRunCompleted); export const selectFirstRunCompleted = createSelector(selectState, (state) => state.firstRunCompleted);
export const selectshowSuggestedSubs = createSelector(selectState, state => state.showSuggestedSubs); export const selectshowSuggestedSubs = createSelector(selectState, (state) => state.showSuggestedSubs);
// Fetching any claims that are a part of a users subscriptions // Fetching any claims that are a part of a users subscriptions
export const selectSubscriptionsBeingFetched = createSelector( export const selectSubscriptionsBeingFetched = createSelector(
@ -89,7 +89,7 @@ export const selectSubscriptionsBeingFetched = createSelector(
selectAllFetchingChannelClaims, selectAllFetchingChannelClaims,
(subscriptions, fetchingChannelClaims) => { (subscriptions, fetchingChannelClaims) => {
const fetchingSubscriptionMap = {}; const fetchingSubscriptionMap = {};
subscriptions.forEach(sub => { subscriptions.forEach((sub) => {
const isFetching = fetchingChannelClaims && fetchingChannelClaims[sub.uri]; const isFetching = fetchingChannelClaims && fetchingChannelClaims[sub.uri];
if (isFetching) { if (isFetching) {
fetchingSubscriptionMap[sub.uri] = true; fetchingSubscriptionMap[sub.uri] = true;
@ -102,17 +102,17 @@ export const selectSubscriptionsBeingFetched = createSelector(
// Returns true if a user is subscribed to the channel associated with the uri passed in // Returns true if a user is subscribed to the channel associated with the uri passed in
// Accepts content or channel uris // Accepts content or channel uris
export const makeSelectChannelInSubscriptions = uri => export const makeSelectChannelInSubscriptions = (uri) =>
createSelector(selectSubscriptions, subscriptions => subscriptions.some(sub => sub.uri === uri)); createSelector(selectSubscriptions, (subscriptions) => subscriptions.some((sub) => sub.uri === uri));
export const makeSelectIsSubscribed = uri => export const makeSelectIsSubscribed = (uri) =>
createSelector( createSelector(
selectSubscriptions, selectSubscriptions,
makeSelectChannelForClaimUri(uri, true), makeSelectChannelForClaimUri(uri, true),
makeSelectClaimForUri(uri), makeSelectClaimForUri(uri),
(subscriptions, channelUri, claim) => { (subscriptions, channelUri, claim) => {
if (channelUri) { if (channelUri) {
return subscriptions.some(sub => sub.uri === 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 // If we couldn't get a channel uri from the claim uri, the uri passed in might be a channel already
@ -123,21 +123,25 @@ export const makeSelectIsSubscribed = uri =>
if (isChannel && claim) { if (isChannel && claim) {
const uri = claim.permanent_url; const uri = claim.permanent_url;
return subscriptions.some(sub => sub.uri === uri); return subscriptions.some((sub) => sub.uri === uri);
}
if (isChannel && !claim) {
return subscriptions.some((sub) => sub.uri === uri);
} }
return false; return false;
} }
); );
export const makeSelectNotificationsDisabled = uri => export const makeSelectNotificationsDisabled = (uri) =>
createSelector( createSelector(
selectFollowing, selectFollowing,
makeSelectChannelForClaimUri(uri, true), makeSelectChannelForClaimUri(uri, true),
makeSelectClaimForUri(uri), makeSelectClaimForUri(uri),
(following, channelUri, claim) => { (following, channelUri, claim) => {
if (channelUri) { if (channelUri) {
return following.some(following => following.uri === channelUri && following.notificationsDisabled); 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 // If we couldn't get a channel uri from the claim uri, the uri passed in might be a channel already
@ -148,7 +152,7 @@ export const makeSelectNotificationsDisabled = uri =>
if (isChannel && claim) { if (isChannel && claim) {
const uri = claim.permanent_url; const uri = claim.permanent_url;
const disabled = following.some(sub => { const disabled = following.some((sub) => {
return sub.uri === uri && sub.notificationsDisabled === true; return sub.uri === uri && sub.notificationsDisabled === true;
}); });