2020-06-23 19:38:18 +02:00
|
|
|
// @flow
|
|
|
|
import { createSelector } from 'reselect';
|
2021-10-11 08:02:17 +02:00
|
|
|
import { createCachedSelector } from 're-reselect';
|
2022-05-24 15:14:20 +02:00
|
|
|
import { selectGeoBlockLists, selectMutedChannels } from 'redux/selectors/blocked';
|
2021-03-31 22:55:26 +02:00
|
|
|
import { selectShowMatureContent } from 'redux/selectors/settings';
|
2021-12-07 19:17:29 +01:00
|
|
|
import { selectMentionSearchResults, selectMentionQuery } from 'redux/selectors/search';
|
2022-05-24 15:14:20 +02:00
|
|
|
import { selectUserLocale } from 'redux/selectors/user';
|
2020-08-24 19:35:21 +02:00
|
|
|
import { selectBlacklistedOutpointMap, selectFilteredOutpointMap } from 'lbryinc';
|
2021-11-24 15:33:34 +01:00
|
|
|
import {
|
|
|
|
selectClaimsById,
|
|
|
|
selectMyClaimIdsRaw,
|
|
|
|
selectMyChannelClaimIds,
|
|
|
|
selectClaimIdForUri,
|
2021-12-02 17:49:13 +01:00
|
|
|
selectClaimIdsByUri,
|
2021-11-24 15:33:34 +01:00
|
|
|
} from 'redux/selectors/claims';
|
2021-12-06 18:28:36 +01:00
|
|
|
import { isClaimNsfw, getChannelFromClaim } from 'util/claim';
|
2021-12-02 17:49:13 +01:00
|
|
|
import { selectSubscriptionUris } from 'redux/selectors/subscriptions';
|
2022-02-02 13:45:16 +01:00
|
|
|
import { getCommentsListTitle } from 'util/comments';
|
2022-05-24 15:14:20 +02:00
|
|
|
import { getGeoRestrictionForClaim } from 'util/geoRestriction';
|
2020-06-23 19:38:18 +02:00
|
|
|
|
2022-03-17 05:55:40 +01:00
|
|
|
type State = { claims: any, comments: CommentsState, user: UserState };
|
2021-10-06 20:40:40 +02:00
|
|
|
|
2021-03-03 19:50:16 +01:00
|
|
|
const selectState = (state) => state.comments || {};
|
|
|
|
|
2021-10-11 05:22:05 +02:00
|
|
|
export const selectCommentsById = (state: State) => selectState(state).commentById || {};
|
2021-11-09 16:32:16 +01:00
|
|
|
export const selectCommentIdsByClaimId = (state: State) => selectState(state).byId;
|
2021-10-11 05:22:05 +02:00
|
|
|
export const selectIsFetchingComments = (state: State) => selectState(state).isLoading;
|
|
|
|
export const selectIsFetchingCommentsById = (state: State) => selectState(state).isLoadingById;
|
2022-03-11 05:11:01 +01:00
|
|
|
const selectTotalCommentsById = (state: State) => selectState(state).totalCommentsById;
|
2021-10-11 05:22:05 +02:00
|
|
|
export const selectIsFetchingReacts = (state: State) => selectState(state).isFetchingReacts;
|
2021-10-06 20:40:40 +02:00
|
|
|
|
|
|
|
export const selectMyReacts = (state: State) => state.comments.myReactsByCommentId;
|
|
|
|
export const selectMyReactsForComment = (state: State, commentIdChannelId: string) => {
|
|
|
|
// @commentIdChannelId: Format = 'commentId:MyChannelId'
|
|
|
|
return state.comments.myReactsByCommentId && state.comments.myReactsByCommentId[commentIdChannelId];
|
|
|
|
};
|
2022-03-03 15:20:09 +01:00
|
|
|
export const selectIsFetchingCommentsForParentId = (state: State, parentId: string) => {
|
2022-03-02 13:10:52 +01:00
|
|
|
return selectState(state).isLoadingByParentId[parentId];
|
|
|
|
};
|
2021-10-06 20:40:40 +02:00
|
|
|
|
|
|
|
export const selectOthersReacts = (state: State) => state.comments.othersReactsByCommentId;
|
|
|
|
export const selectOthersReactsForComment = (state: State, id: string) => {
|
|
|
|
return state.comments.othersReactsByCommentId && state.comments.othersReactsByCommentId[id];
|
|
|
|
};
|
2021-08-17 18:09:55 +02:00
|
|
|
|
2021-10-11 08:02:17 +02:00
|
|
|
// previously this used a mapping from claimId -> Array<Comments>
|
|
|
|
/* export const selectCommentsById = createSelector(
|
|
|
|
selectState,
|
|
|
|
state => state.byId || {}
|
|
|
|
); */
|
|
|
|
export const selectCommentsByUri = createSelector(selectState, (state) => {
|
|
|
|
const byUri = state.commentsByUri || {};
|
|
|
|
const comments = {};
|
|
|
|
Object.keys(byUri).forEach((uri) => {
|
|
|
|
const claimId = byUri[uri];
|
|
|
|
if (claimId === null) {
|
|
|
|
comments[uri] = null;
|
|
|
|
} else {
|
|
|
|
comments[uri] = claimId;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return comments;
|
|
|
|
});
|
|
|
|
|
2021-10-11 05:22:05 +02:00
|
|
|
export const selectPinnedCommentsById = (state: State) => selectState(state).pinnedCommentsById;
|
2021-10-11 08:02:17 +02:00
|
|
|
export const selectPinnedCommentsForUri = createCachedSelector(
|
2021-11-17 08:36:04 +01:00
|
|
|
selectClaimIdForUri,
|
2021-10-11 08:02:17 +02:00
|
|
|
selectCommentsById,
|
|
|
|
selectPinnedCommentsById,
|
|
|
|
(state, uri) => uri,
|
2021-11-17 08:36:04 +01:00
|
|
|
(claimId, byId, pinnedCommentsById, uri) => {
|
2021-10-11 08:02:17 +02:00
|
|
|
const pinnedCommentIds = pinnedCommentsById && pinnedCommentsById[claimId];
|
|
|
|
const pinnedComments = [];
|
2021-08-17 18:09:55 +02:00
|
|
|
|
2021-10-11 08:02:17 +02:00
|
|
|
if (pinnedCommentIds) {
|
|
|
|
pinnedCommentIds.forEach((commentId) => {
|
|
|
|
pinnedComments.push(byId[commentId]);
|
|
|
|
});
|
2021-08-17 18:09:55 +02:00
|
|
|
}
|
2021-10-11 08:02:17 +02:00
|
|
|
|
|
|
|
return pinnedComments;
|
|
|
|
}
|
2021-10-25 06:58:34 +02:00
|
|
|
)((state, uri) => String(uri));
|
2021-05-25 08:17:36 +02:00
|
|
|
|
2021-10-05 16:09:49 +02:00
|
|
|
export const selectModerationBlockList = createSelector(
|
|
|
|
(state) => selectState(state).moderationBlockList,
|
|
|
|
(moderationBlockList) => {
|
|
|
|
return moderationBlockList ? moderationBlockList.reverse() : [];
|
|
|
|
}
|
2021-03-03 19:50:16 +01:00
|
|
|
);
|
2021-05-25 08:17:36 +02:00
|
|
|
export const selectAdminBlockList = createSelector(selectState, (state) =>
|
|
|
|
state.adminBlockList ? state.adminBlockList.reverse() : []
|
|
|
|
);
|
|
|
|
export const selectModeratorBlockList = createSelector(selectState, (state) =>
|
|
|
|
state.moderatorBlockList ? state.moderatorBlockList.reverse() : []
|
|
|
|
);
|
|
|
|
|
2021-10-11 05:22:05 +02:00
|
|
|
export const selectPersonalTimeoutMap = (state: State) => selectState(state).personalTimeoutMap;
|
|
|
|
export const selectAdminTimeoutMap = (state: State) => selectState(state).adminTimeoutMap;
|
|
|
|
export const selectModeratorTimeoutMap = (state: State) => selectState(state).moderatorTimeoutMap;
|
|
|
|
export const selectModeratorBlockListDelegatorsMap = (state: State) =>
|
|
|
|
selectState(state).moderatorBlockListDelegatorsMap;
|
|
|
|
export const selectTogglingForDelegatorMap = (state: State) => selectState(state).togglingForDelegatorMap;
|
|
|
|
export const selectBlockingByUri = (state: State) => selectState(state).blockingByUri;
|
|
|
|
export const selectUnBlockingByUri = (state: State) => selectState(state).unBlockingByUri;
|
|
|
|
export const selectFetchingModerationBlockList = (state: State) => selectState(state).fetchingModerationBlockList;
|
|
|
|
export const selectModerationDelegatesById = (state: State) => selectState(state).moderationDelegatesById;
|
|
|
|
export const selectIsFetchingModerationDelegates = (state: State) => selectState(state).fetchingModerationDelegates;
|
|
|
|
export const selectModerationDelegatorsById = (state: State) => selectState(state).moderationDelegatorsById;
|
|
|
|
export const selectIsFetchingModerationDelegators = (state: State) => selectState(state).fetchingModerationDelegators;
|
2021-05-25 08:17:36 +02:00
|
|
|
|
|
|
|
export const selectHasAdminChannel = createSelector(selectState, (state) => {
|
|
|
|
const myChannelIds = Object.keys(state.moderationDelegatorsById);
|
|
|
|
for (let i = 0; i < myChannelIds.length; ++i) {
|
|
|
|
const id = myChannelIds[i];
|
|
|
|
if (state.moderationDelegatorsById[id] && state.moderationDelegatorsById[id].global) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/// Lint doesn't like this:
|
|
|
|
// return Object.values(state.moderationDelegatorsById).some((x) => x.global);
|
|
|
|
});
|
|
|
|
|
2020-06-23 19:38:18 +02:00
|
|
|
export const selectCommentsByClaimId = createSelector(selectState, selectCommentsById, (state, byId) => {
|
|
|
|
const byClaimId = state.byId || {};
|
|
|
|
const comments = {};
|
|
|
|
|
2020-08-24 19:35:21 +02:00
|
|
|
// replace every comment_id in the list with the actual comment object
|
|
|
|
Object.keys(byClaimId).forEach((claimId: string) => {
|
|
|
|
const commentIds = byClaimId[claimId];
|
|
|
|
|
|
|
|
comments[claimId] = Array(commentIds === null ? 0 : commentIds.length);
|
|
|
|
for (let i = 0; i < commentIds.length; i++) {
|
|
|
|
comments[claimId][i] = byId[commentIds[i]];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return comments;
|
|
|
|
});
|
|
|
|
|
2021-10-11 05:22:05 +02:00
|
|
|
export const selectSuperchatsByUri = (state: State) => selectState(state).superChatsByUri;
|
2021-04-23 21:59:48 +02:00
|
|
|
|
2021-10-11 08:02:17 +02:00
|
|
|
export const selectTopLevelCommentsByClaimId = createSelector(
|
|
|
|
(state) => selectState(state).topLevelCommentsById,
|
|
|
|
selectCommentsById,
|
|
|
|
(topLevelCommentsById, byId) => {
|
|
|
|
const byClaimId = topLevelCommentsById || {};
|
|
|
|
const comments = {};
|
2020-08-24 19:35:21 +02:00
|
|
|
|
2021-10-11 08:02:17 +02:00
|
|
|
// replace every comment_id in the list with the actual comment object
|
|
|
|
Object.keys(byClaimId).forEach((claimId) => {
|
|
|
|
const commentIds = byClaimId[claimId];
|
2020-06-23 19:38:18 +02:00
|
|
|
|
2021-10-11 08:02:17 +02:00
|
|
|
comments[claimId] = Array(commentIds === null ? 0 : commentIds.length);
|
|
|
|
for (let i = 0; i < commentIds.length; i++) {
|
|
|
|
comments[claimId][i] = byId[commentIds[i]];
|
|
|
|
}
|
|
|
|
});
|
2020-06-23 19:38:18 +02:00
|
|
|
|
2021-10-11 08:02:17 +02:00
|
|
|
return comments;
|
|
|
|
}
|
|
|
|
);
|
2020-06-23 19:38:18 +02:00
|
|
|
|
Re-design comment threads (#1489)
* Redesign threadline and fetching state
- threadline goes right below channel avatar, mimicking reddits implementation, has a increase effect on hover and is slimmer, creating more space for comments on screen
- fetching state now replaces show/hide button, also mimicking reddit, and now says that it is loading, instead of a blank spinner, and also improves space a bit
* Redesign comment threads
- Allow for infinite comment chains
- Can go back and forth between the pages
- Can go back to all comments or to the first comment in the chain
- Some other improvements, which include:
- add title on non-drawer comment sections (couldn't see amount of comments)
- fix Expandable component (would begin expanded and collapse after the effect runs, which looked bad and shifted the layout, now each comments greater than the set length begins collapsed)
- used constants for consistency
* Fix replying to last thread comment
* Fix buttons condition (only on fetched comment to avoid deleted case)
* Fix auto-scroll
* Bring back instant feedback for Show More replies
* Improve thread back links
- Now going back to all comments links the top-level comment for easier navigation
- Going back to ~ previous ~ now goes back into the chain instead of topmost level
* Clear timeouts due to unrelated issue
* Fix deep thread linked comment case and more scroll improvements
* More minor changes
* Flow
* Fix commentList tile style
* Fix long channel names overflowing on small screens
* More scroll changes
* Fix threadline
* Revert "Fix long channel names overflowing on small screens"
This reverts commit e4d2dc7da5861ed8136a60f3352e41a690cd4d33.
* Fix replies fetch
* Revert "Fix replies fetch"
This reverts commit ec70054675a604a7a5f3764ba07c36bf7b0f49c8.
* Cleanup and make smooth
* Always use linked comment on threads
* Cleanup
* Higlight thread comment
* Fix comment body styles
2022-05-16 12:22:13 +02:00
|
|
|
export const selectCommentForCommentId = createSelector(
|
|
|
|
(state, commentId) => commentId,
|
|
|
|
selectCommentsById,
|
|
|
|
(commentId, comments) => comments[commentId]
|
|
|
|
);
|
2020-08-24 19:35:21 +02:00
|
|
|
|
|
|
|
export const selectRepliesByParentId = createSelector(selectState, selectCommentsById, (state, byId) => {
|
|
|
|
const byParentId = state.repliesByParentId || {};
|
|
|
|
const comments = {};
|
|
|
|
|
|
|
|
// replace every comment_id in the list with the actual comment object
|
2021-03-03 19:50:16 +01:00
|
|
|
Object.keys(byParentId).forEach((id) => {
|
2020-08-24 19:35:21 +02:00
|
|
|
const commentIds = byParentId[id];
|
|
|
|
|
|
|
|
comments[id] = Array(commentIds === null ? 0 : commentIds.length);
|
|
|
|
for (let i = 0; i < commentIds.length; i++) {
|
|
|
|
comments[id][i] = byId[commentIds[i]];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return comments;
|
|
|
|
});
|
|
|
|
|
Re-design comment threads (#1489)
* Redesign threadline and fetching state
- threadline goes right below channel avatar, mimicking reddits implementation, has a increase effect on hover and is slimmer, creating more space for comments on screen
- fetching state now replaces show/hide button, also mimicking reddit, and now says that it is loading, instead of a blank spinner, and also improves space a bit
* Redesign comment threads
- Allow for infinite comment chains
- Can go back and forth between the pages
- Can go back to all comments or to the first comment in the chain
- Some other improvements, which include:
- add title on non-drawer comment sections (couldn't see amount of comments)
- fix Expandable component (would begin expanded and collapse after the effect runs, which looked bad and shifted the layout, now each comments greater than the set length begins collapsed)
- used constants for consistency
* Fix replying to last thread comment
* Fix buttons condition (only on fetched comment to avoid deleted case)
* Fix auto-scroll
* Bring back instant feedback for Show More replies
* Improve thread back links
- Now going back to all comments links the top-level comment for easier navigation
- Going back to ~ previous ~ now goes back into the chain instead of topmost level
* Clear timeouts due to unrelated issue
* Fix deep thread linked comment case and more scroll improvements
* More minor changes
* Flow
* Fix commentList tile style
* Fix long channel names overflowing on small screens
* More scroll changes
* Fix threadline
* Revert "Fix long channel names overflowing on small screens"
This reverts commit e4d2dc7da5861ed8136a60f3352e41a690cd4d33.
* Fix replies fetch
* Revert "Fix replies fetch"
This reverts commit ec70054675a604a7a5f3764ba07c36bf7b0f49c8.
* Cleanup and make smooth
* Always use linked comment on threads
* Cleanup
* Higlight thread comment
* Fix comment body styles
2022-05-16 12:22:13 +02:00
|
|
|
export const selectFetchedCommentAncestors = (state: State) => selectState(state).fetchedCommentAncestors;
|
|
|
|
|
|
|
|
export const selectCommentAncestorsForId = createSelector(
|
|
|
|
(state, commentId) => commentId,
|
|
|
|
selectFetchedCommentAncestors,
|
|
|
|
(commentId, fetchedAncestors) => fetchedAncestors && fetchedAncestors[commentId]
|
|
|
|
);
|
2021-07-15 16:43:28 +02:00
|
|
|
|
2021-11-09 16:32:16 +01:00
|
|
|
export const selectCommentIdsForUri = (state: State, uri: string) => {
|
|
|
|
const claimId = selectClaimIdForUri(state, uri);
|
|
|
|
const commentIdsByClaimId = selectCommentIdsByClaimId(state);
|
|
|
|
return commentIdsByClaimId[claimId];
|
|
|
|
};
|
2020-09-29 16:10:23 +02:00
|
|
|
|
2021-10-11 08:02:17 +02:00
|
|
|
const filterCommentsDepOnList = {
|
|
|
|
claimsById: selectClaimsById,
|
2021-11-04 03:10:40 +01:00
|
|
|
myClaimIds: selectMyClaimIdsRaw,
|
2021-11-24 15:33:34 +01:00
|
|
|
myChannelClaimIds: selectMyChannelClaimIds,
|
2021-10-11 08:02:17 +02:00
|
|
|
mutedChannels: selectMutedChannels,
|
|
|
|
personalBlockList: selectModerationBlockList,
|
|
|
|
blacklistedMap: selectBlacklistedOutpointMap,
|
|
|
|
filteredMap: selectFilteredOutpointMap,
|
|
|
|
showMatureContent: selectShowMatureContent,
|
2022-05-24 15:14:20 +02:00
|
|
|
geoBlockList: selectGeoBlockLists,
|
|
|
|
locale: selectUserLocale,
|
2021-10-11 08:02:17 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
const filterCommentsPropKeys = Object.keys(filterCommentsDepOnList);
|
|
|
|
|
2021-10-11 05:22:05 +02:00
|
|
|
export const selectPendingCommentReacts = (state: State) => selectState(state).pendingCommentReactions;
|
|
|
|
export const selectSettingsByChannelId = (state: State) => selectState(state).settingsByChannelId;
|
|
|
|
export const selectFetchingCreatorSettings = (state: State) => selectState(state).fetchingSettings;
|
|
|
|
export const selectFetchingBlockedWords = (state: State) => selectState(state).fetchingBlockedWords;
|
2021-04-20 10:40:53 +02:00
|
|
|
|
2021-10-11 08:02:17 +02:00
|
|
|
export const selectCommentsForUri = createCachedSelector(
|
|
|
|
(state, uri) => uri,
|
|
|
|
selectCommentsByClaimId,
|
2021-11-17 08:36:04 +01:00
|
|
|
selectClaimIdForUri,
|
2021-10-11 08:02:17 +02:00
|
|
|
...Object.values(filterCommentsDepOnList),
|
2021-11-17 08:36:04 +01:00
|
|
|
(uri, byClaimId, claimId, ...filterInputs) => {
|
2021-10-11 08:02:17 +02:00
|
|
|
const comments = byClaimId && byClaimId[claimId];
|
|
|
|
return filterComments(comments, claimId, filterInputs);
|
|
|
|
}
|
2021-10-25 06:58:34 +02:00
|
|
|
)((state, uri) => String(uri));
|
2021-10-11 08:02:17 +02:00
|
|
|
|
|
|
|
export const selectTopLevelCommentsForUri = createCachedSelector(
|
|
|
|
(state, uri) => uri,
|
|
|
|
(state, uri, maxCount) => maxCount,
|
|
|
|
selectTopLevelCommentsByClaimId,
|
2021-11-17 08:36:04 +01:00
|
|
|
selectClaimIdForUri,
|
2021-10-11 08:02:17 +02:00
|
|
|
...Object.values(filterCommentsDepOnList),
|
2021-11-17 08:36:04 +01:00
|
|
|
(uri, maxCount = -1, byClaimId, claimId, ...filterInputs) => {
|
2021-10-11 08:02:17 +02:00
|
|
|
const comments = byClaimId && byClaimId[claimId];
|
2021-11-17 09:05:50 +01:00
|
|
|
if (comments) {
|
|
|
|
return filterComments(maxCount > 0 ? comments.slice(0, maxCount) : comments, claimId, filterInputs);
|
|
|
|
} else {
|
|
|
|
return [];
|
|
|
|
}
|
2021-10-11 08:02:17 +02:00
|
|
|
}
|
2021-10-25 06:58:34 +02:00
|
|
|
)((state, uri, maxCount = -1) => `${String(uri)}:${maxCount}`);
|
2020-08-24 19:35:21 +02:00
|
|
|
|
2021-07-15 16:43:28 +02:00
|
|
|
export const makeSelectTopLevelTotalPagesForUri = (uri: string) =>
|
|
|
|
createSelector(selectState, selectCommentsByUri, (state, byUri) => {
|
|
|
|
const claimId = byUri[uri];
|
|
|
|
return state.topLevelTotalPagesById[claimId] || 0;
|
|
|
|
});
|
|
|
|
|
2021-10-11 08:02:17 +02:00
|
|
|
export const selectRepliesForParentId = createCachedSelector(
|
|
|
|
(state, id) => id,
|
|
|
|
(state) => selectState(state).repliesByParentId,
|
|
|
|
selectCommentsById,
|
|
|
|
...Object.values(filterCommentsDepOnList),
|
|
|
|
(id, repliesByParentId, commentsById, ...filterInputs) => {
|
|
|
|
// const claimId = byUri[uri]; // just parentId (id)
|
|
|
|
const replyIdsForParent = repliesByParentId[id] || [];
|
2022-01-26 15:25:43 +01:00
|
|
|
if (!replyIdsForParent.length) return [];
|
2021-10-11 08:02:17 +02:00
|
|
|
|
|
|
|
const comments = [];
|
|
|
|
replyIdsForParent.forEach((cid) => {
|
|
|
|
comments.push(commentsById[cid]);
|
|
|
|
});
|
|
|
|
// const comments = byParentId && byParentId[id];
|
|
|
|
|
|
|
|
return filterComments(comments, undefined, filterInputs);
|
|
|
|
}
|
2021-10-25 06:58:34 +02:00
|
|
|
)((state, id: string) => String(id));
|
2021-08-04 15:40:16 +02:00
|
|
|
|
Run the extra app-side comment filter only if the claim is not ours.
## Preamble
- The app-side uses a cached blocklist, so when a Timeout expires, it doesn't know that the ban has been lifted.
- Meanwhile, we are doing extra comment filtering using this blocklist (so that we don't see comments that we have blocked, regardless of whose claim we are viewing).
## Issue
In a livestream, if a new message from an ex-offender comes in after their ban has been lifted, we do get the websocket message but it's being filtered out locally as mentioned above. So, the msg ended up being visible for everyone except the owner.
## Fix (band aid)
- Don't run the extra filter if the claim we are viewing is ours -- commentron would have filtered it for us anyways, and is the right logic to use even before this Timeout feature is introduced.
- For the case of Timeout, this only serves as a band-aid until Commentron Issue 80 is available for us to detect the ban has been lifted. This is because it doesn't handle the case where I am a viewer and I decided to timeout someone for a few minutes. Because I am not the owner of the claim, the offender will continue to be blocked due to the same issue mentioned above.
2021-09-03 09:43:01 +02:00
|
|
|
/**
|
2021-10-11 08:02:17 +02:00
|
|
|
* filterComments
|
Run the extra app-side comment filter only if the claim is not ours.
## Preamble
- The app-side uses a cached blocklist, so when a Timeout expires, it doesn't know that the ban has been lifted.
- Meanwhile, we are doing extra comment filtering using this blocklist (so that we don't see comments that we have blocked, regardless of whose claim we are viewing).
## Issue
In a livestream, if a new message from an ex-offender comes in after their ban has been lifted, we do get the websocket message but it's being filtered out locally as mentioned above. So, the msg ended up being visible for everyone except the owner.
## Fix (band aid)
- Don't run the extra filter if the claim we are viewing is ours -- commentron would have filtered it for us anyways, and is the right logic to use even before this Timeout feature is introduced.
- For the case of Timeout, this only serves as a band-aid until Commentron Issue 80 is available for us to detect the ban has been lifted. This is because it doesn't handle the case where I am a viewer and I decided to timeout someone for a few minutes. Because I am not the owner of the claim, the offender will continue to be blocked due to the same issue mentioned above.
2021-09-03 09:43:01 +02:00
|
|
|
*
|
|
|
|
* @param comments List of comments to filter.
|
|
|
|
* @param claimId The claim that `comments` reside in.
|
2021-11-04 03:10:40 +01:00
|
|
|
* @param filterInputs Values returned by filterCommentsDepOnList.
|
Run the extra app-side comment filter only if the claim is not ours.
## Preamble
- The app-side uses a cached blocklist, so when a Timeout expires, it doesn't know that the ban has been lifted.
- Meanwhile, we are doing extra comment filtering using this blocklist (so that we don't see comments that we have blocked, regardless of whose claim we are viewing).
## Issue
In a livestream, if a new message from an ex-offender comes in after their ban has been lifted, we do get the websocket message but it's being filtered out locally as mentioned above. So, the msg ended up being visible for everyone except the owner.
## Fix (band aid)
- Don't run the extra filter if the claim we are viewing is ours -- commentron would have filtered it for us anyways, and is the right logic to use even before this Timeout feature is introduced.
- For the case of Timeout, this only serves as a band-aid until Commentron Issue 80 is available for us to detect the ban has been lifted. This is because it doesn't handle the case where I am a viewer and I decided to timeout someone for a few minutes. Because I am not the owner of the claim, the offender will continue to be blocked due to the same issue mentioned above.
2021-09-03 09:43:01 +02:00
|
|
|
*/
|
2021-10-11 08:02:17 +02:00
|
|
|
const filterComments = (comments: Array<Comment>, claimId?: string, filterInputs: any) => {
|
2021-12-06 18:28:36 +01:00
|
|
|
const filterProps = filterInputs.reduce((acc, cur, i) => {
|
2021-10-11 08:02:17 +02:00
|
|
|
acc[filterCommentsPropKeys[i]] = cur;
|
|
|
|
return acc;
|
|
|
|
}, {});
|
|
|
|
|
|
|
|
const {
|
|
|
|
claimsById,
|
2021-11-04 03:10:40 +01:00
|
|
|
myClaimIds,
|
2021-11-24 15:33:34 +01:00
|
|
|
myChannelClaimIds,
|
2021-10-11 08:02:17 +02:00
|
|
|
mutedChannels,
|
|
|
|
personalBlockList,
|
|
|
|
blacklistedMap,
|
|
|
|
filteredMap,
|
|
|
|
showMatureContent,
|
2022-05-24 15:14:20 +02:00
|
|
|
geoBlockList,
|
|
|
|
locale,
|
2021-10-11 08:02:17 +02:00
|
|
|
} = filterProps;
|
|
|
|
|
|
|
|
return comments
|
|
|
|
? comments.filter((comment) => {
|
|
|
|
if (!comment) {
|
|
|
|
// It may have been recently deleted after being blocked
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const channelClaim = claimsById[comment.channel_id];
|
|
|
|
|
|
|
|
// Return comment if `channelClaim` doesn't exist so the component knows to resolve the author
|
|
|
|
if (channelClaim) {
|
2021-11-24 15:33:34 +01:00
|
|
|
if ((myClaimIds && myClaimIds.size > 0) || (myChannelClaimIds && myChannelClaimIds.length > 0)) {
|
|
|
|
const claimIsMine =
|
|
|
|
channelClaim.is_my_output ||
|
|
|
|
myChannelClaimIds.includes(channelClaim.claim_id) ||
|
|
|
|
myClaimIds.includes(channelClaim.claim_id);
|
|
|
|
// TODO: I believe 'myClaimIds' does not include channels, so it seems wasteful to include it here? ^
|
2021-10-11 08:02:17 +02:00
|
|
|
if (claimIsMine) {
|
|
|
|
return true;
|
2020-09-11 19:51:31 +02:00
|
|
|
}
|
2021-10-11 08:02:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const outpoint = `${channelClaim.txid}:${channelClaim.nout}`;
|
|
|
|
if (blacklistedMap[outpoint] || filteredMap[outpoint]) {
|
|
|
|
return false;
|
|
|
|
}
|
2020-09-11 19:51:31 +02:00
|
|
|
|
2021-10-11 08:02:17 +02:00
|
|
|
if (!showMatureContent) {
|
|
|
|
const claimIsMature = isClaimNsfw(channelClaim);
|
|
|
|
if (claimIsMature) {
|
|
|
|
return false;
|
2020-07-15 20:07:07 +02:00
|
|
|
}
|
2021-10-11 08:02:17 +02:00
|
|
|
}
|
|
|
|
}
|
2020-07-15 20:07:07 +02:00
|
|
|
|
2021-10-11 08:02:17 +02:00
|
|
|
if (claimId) {
|
2021-11-04 03:10:40 +01:00
|
|
|
const claimIdIsMine = myClaimIds && myClaimIds.size > 0 && myClaimIds.includes(claimId);
|
2021-10-11 08:02:17 +02:00
|
|
|
if (!claimIdIsMine) {
|
|
|
|
if (personalBlockList.includes(comment.channel_url)) {
|
|
|
|
return false;
|
Run the extra app-side comment filter only if the claim is not ours.
## Preamble
- The app-side uses a cached blocklist, so when a Timeout expires, it doesn't know that the ban has been lifted.
- Meanwhile, we are doing extra comment filtering using this blocklist (so that we don't see comments that we have blocked, regardless of whose claim we are viewing).
## Issue
In a livestream, if a new message from an ex-offender comes in after their ban has been lifted, we do get the websocket message but it's being filtered out locally as mentioned above. So, the msg ended up being visible for everyone except the owner.
## Fix (band aid)
- Don't run the extra filter if the claim we are viewing is ours -- commentron would have filtered it for us anyways, and is the right logic to use even before this Timeout feature is introduced.
- For the case of Timeout, this only serves as a band-aid until Commentron Issue 80 is available for us to detect the ban has been lifted. This is because it doesn't handle the case where I am a viewer and I decided to timeout someone for a few minutes. Because I am not the owner of the claim, the offender will continue to be blocked due to the same issue mentioned above.
2021-09-03 09:43:01 +02:00
|
|
|
}
|
2021-10-11 08:02:17 +02:00
|
|
|
}
|
|
|
|
}
|
Run the extra app-side comment filter only if the claim is not ours.
## Preamble
- The app-side uses a cached blocklist, so when a Timeout expires, it doesn't know that the ban has been lifted.
- Meanwhile, we are doing extra comment filtering using this blocklist (so that we don't see comments that we have blocked, regardless of whose claim we are viewing).
## Issue
In a livestream, if a new message from an ex-offender comes in after their ban has been lifted, we do get the websocket message but it's being filtered out locally as mentioned above. So, the msg ended up being visible for everyone except the owner.
## Fix (band aid)
- Don't run the extra filter if the claim we are viewing is ours -- commentron would have filtered it for us anyways, and is the right logic to use even before this Timeout feature is introduced.
- For the case of Timeout, this only serves as a band-aid until Commentron Issue 80 is available for us to detect the ban has been lifted. This is because it doesn't handle the case where I am a viewer and I decided to timeout someone for a few minutes. Because I am not the owner of the claim, the offender will continue to be blocked due to the same issue mentioned above.
2021-09-03 09:43:01 +02:00
|
|
|
|
2022-05-24 15:14:20 +02:00
|
|
|
if (channelClaim) {
|
|
|
|
const geoRestriction: ?GeoRestriction = getGeoRestrictionForClaim(channelClaim, locale, geoBlockList);
|
|
|
|
if (geoRestriction) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-11 08:02:17 +02:00
|
|
|
return !mutedChannels.includes(comment.channel_url);
|
|
|
|
})
|
|
|
|
: [];
|
|
|
|
};
|
2020-09-30 20:46:17 +02:00
|
|
|
|
2021-08-04 17:01:31 +02:00
|
|
|
export const makeSelectTotalReplyPagesForParentId = (parentId: string) =>
|
2021-07-15 16:43:28 +02:00
|
|
|
createSelector(selectState, (state) => {
|
2021-08-04 17:01:31 +02:00
|
|
|
return state.repliesTotalPagesByParentId[parentId] || 0;
|
2021-07-15 16:43:28 +02:00
|
|
|
});
|
|
|
|
|
2022-03-11 05:11:01 +01:00
|
|
|
export const selectTotalCommentsCountForUri = (state: State, uri: string) => {
|
|
|
|
const commentIdsByUri = selectCommentsByUri(state);
|
|
|
|
const totalCommentsById = selectTotalCommentsById(state);
|
|
|
|
const claimId = commentIdsByUri[uri];
|
|
|
|
return totalCommentsById[claimId] || 0;
|
|
|
|
};
|
2021-03-03 19:50:16 +01:00
|
|
|
|
2022-03-11 05:11:01 +01:00
|
|
|
export const selectCommentsListTitleForUri = (state: State, uri: string) => {
|
|
|
|
const totalComments = selectTotalCommentsCountForUri(state, uri);
|
|
|
|
return getCommentsListTitle(totalComments);
|
|
|
|
};
|
2022-02-02 13:45:16 +01:00
|
|
|
|
2021-05-25 08:17:36 +02:00
|
|
|
// Personal list
|
2021-03-03 19:50:16 +01:00
|
|
|
export const makeSelectChannelIsBlocked = (uri: string) =>
|
|
|
|
createSelector(selectModerationBlockList, (blockedChannelUris) => {
|
|
|
|
if (!blockedChannelUris || !blockedChannelUris) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return blockedChannelUris.includes(uri);
|
|
|
|
});
|
|
|
|
|
2021-05-25 08:17:36 +02:00
|
|
|
export const makeSelectChannelIsAdminBlocked = (uri: string) =>
|
|
|
|
createSelector(selectAdminBlockList, (list) => {
|
|
|
|
return list ? list.includes(uri) : false;
|
|
|
|
});
|
|
|
|
|
|
|
|
export const makeSelectChannelIsModeratorBlocked = (uri: string) =>
|
|
|
|
createSelector(selectModeratorBlockList, (list) => {
|
|
|
|
return list ? list.includes(uri) : false;
|
|
|
|
});
|
|
|
|
|
|
|
|
export const makeSelectChannelIsModeratorBlockedForCreator = (uri: string, creatorUri: string) =>
|
|
|
|
createSelector(selectModeratorBlockList, selectModeratorBlockListDelegatorsMap, (blockList, delegatorsMap) => {
|
|
|
|
if (!blockList) return false;
|
|
|
|
return blockList.includes(uri) && delegatorsMap[uri] && delegatorsMap[uri].includes(creatorUri);
|
|
|
|
});
|
|
|
|
|
|
|
|
export const makeSelectIsTogglingForDelegator = (uri: string, creatorUri: string) =>
|
|
|
|
createSelector(selectTogglingForDelegatorMap, (togglingForDelegatorMap) => {
|
|
|
|
return togglingForDelegatorMap[uri] && togglingForDelegatorMap[uri].includes(creatorUri);
|
|
|
|
});
|
|
|
|
|
2021-03-03 19:50:16 +01:00
|
|
|
export const makeSelectUriIsBlockingOrUnBlocking = (uri: string) =>
|
|
|
|
createSelector(selectBlockingByUri, selectUnBlockingByUri, (blockingByUri, unBlockingByUri) => {
|
|
|
|
return blockingByUri[uri] || unBlockingByUri[uri];
|
|
|
|
});
|
2021-04-23 21:59:48 +02:00
|
|
|
|
2021-11-04 05:46:01 +01:00
|
|
|
export const selectSuperChatDataForUri = (state: State, uri: string) => {
|
|
|
|
const byUri = selectSuperchatsByUri(state);
|
|
|
|
return byUri[uri];
|
|
|
|
};
|
2021-04-23 21:59:48 +02:00
|
|
|
|
2021-11-04 05:46:01 +01:00
|
|
|
export const selectSuperChatsForUri = (state: State, uri: string) => {
|
|
|
|
const superChatData = selectSuperChatDataForUri(state, uri);
|
|
|
|
return superChatData ? superChatData.comments : undefined;
|
|
|
|
};
|
2021-04-23 21:59:48 +02:00
|
|
|
|
2021-11-04 05:46:01 +01:00
|
|
|
export const selectSuperChatTotalAmountForUri = (state: State, uri: string) => {
|
|
|
|
const superChatData = selectSuperChatDataForUri(state, uri);
|
|
|
|
return superChatData ? superChatData.totalAmount : 0;
|
|
|
|
};
|
2021-12-02 17:49:13 +01:00
|
|
|
|
|
|
|
export const selectChannelMentionData = createCachedSelector(
|
2021-12-06 18:28:36 +01:00
|
|
|
(state, uri) => uri,
|
2021-12-02 17:49:13 +01:00
|
|
|
selectClaimIdsByUri,
|
|
|
|
selectClaimsById,
|
|
|
|
selectTopLevelCommentsForUri,
|
|
|
|
selectSubscriptionUris,
|
2021-12-07 15:51:55 +01:00
|
|
|
selectMentionSearchResults,
|
2021-12-07 19:17:29 +01:00
|
|
|
selectMentionQuery,
|
|
|
|
(uri, claimIdsByUri, claimsById, topLevelComments, subscriptionUris, searchUris, query) => {
|
2021-12-06 18:28:36 +01:00
|
|
|
let canonicalCreatorUri;
|
2021-12-02 17:49:13 +01:00
|
|
|
const commentorUris = [];
|
|
|
|
const canonicalCommentors = [];
|
|
|
|
const canonicalSubscriptions = [];
|
2021-12-06 20:39:39 +01:00
|
|
|
const canonicalSearch = [];
|
2021-12-02 17:49:13 +01:00
|
|
|
|
2021-12-06 18:28:36 +01:00
|
|
|
if (uri) {
|
|
|
|
const claimId = claimIdsByUri[uri];
|
|
|
|
const claim = claimsById[claimId];
|
|
|
|
const channelFromClaim = claim && getChannelFromClaim(claim);
|
|
|
|
canonicalCreatorUri = channelFromClaim && channelFromClaim.canonical_url;
|
|
|
|
|
|
|
|
topLevelComments.forEach(({ channel_url: uri }) => {
|
|
|
|
// Check: if there are duplicate commentors
|
|
|
|
if (!commentorUris.includes(uri)) {
|
|
|
|
// Update: commentorUris
|
|
|
|
commentorUris.push(uri);
|
|
|
|
|
|
|
|
// Update: canonicalCommentors
|
|
|
|
const claimId = claimIdsByUri[uri];
|
|
|
|
const claim = claimsById[claimId];
|
|
|
|
if (claim && claim.canonical_url) {
|
|
|
|
canonicalCommentors.push(claim.canonical_url);
|
|
|
|
}
|
2021-12-02 17:49:13 +01:00
|
|
|
}
|
2021-12-06 18:28:36 +01:00
|
|
|
});
|
|
|
|
}
|
2021-12-02 17:49:13 +01:00
|
|
|
|
|
|
|
subscriptionUris.forEach((uri) => {
|
|
|
|
// Update: canonicalSubscriptions
|
|
|
|
const claimId = claimIdsByUri[uri];
|
|
|
|
const claim = claimsById[claimId];
|
|
|
|
if (claim && claim.canonical_url) {
|
|
|
|
canonicalSubscriptions.push(claim.canonical_url);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2021-12-07 15:51:55 +01:00
|
|
|
let hasNewResolvedResults = false;
|
2021-12-06 20:39:39 +01:00
|
|
|
if (searchUris && searchUris.length > 0) {
|
|
|
|
searchUris.forEach((uri) => {
|
|
|
|
// Update: canonicalSubscriptions
|
|
|
|
const claimId = claimIdsByUri[uri];
|
|
|
|
const claim = claimsById[claimId];
|
|
|
|
if (claim && claim.canonical_url) {
|
|
|
|
canonicalSearch.push(claim.canonical_url);
|
|
|
|
}
|
|
|
|
});
|
2021-12-07 15:51:55 +01:00
|
|
|
hasNewResolvedResults = canonicalSearch.length > 0;
|
2021-12-06 20:39:39 +01:00
|
|
|
}
|
|
|
|
|
2021-12-07 15:51:55 +01:00
|
|
|
return {
|
|
|
|
canonicalCommentors,
|
|
|
|
canonicalCreatorUri,
|
2021-12-07 19:17:29 +01:00
|
|
|
canonicalSearch,
|
2021-12-07 15:51:55 +01:00
|
|
|
canonicalSubscriptions,
|
|
|
|
commentorUris,
|
|
|
|
hasNewResolvedResults,
|
2021-12-07 19:17:29 +01:00
|
|
|
query,
|
2021-12-07 15:51:55 +01:00
|
|
|
};
|
2021-12-02 17:49:13 +01:00
|
|
|
}
|
|
|
|
)((state, uri, maxCount) => `${String(uri)}:${maxCount}`);
|
2022-04-28 09:08:26 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the list of your channel IDs that have commented on the given claim.
|
|
|
|
*
|
|
|
|
* @param state
|
|
|
|
* @param claimId
|
2022-05-06 07:14:40 +02:00
|
|
|
* @returns {null | undefined | Array<string>} 'undefined' = "not fetched for this ID"; 'null' = "no claim";
|
2022-04-28 09:08:26 +02:00
|
|
|
*/
|
|
|
|
export const selectMyCommentedChannelIdsForId = (state: State, claimId: string) => {
|
|
|
|
return claimId ? selectState(state).myCommentedChannelIdsById[claimId] : null;
|
|
|
|
};
|