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';
|
2021-03-03 19:50:16 +01:00
|
|
|
import { selectMutedChannels } from 'redux/selectors/blocked';
|
2021-03-31 22:55:26 +02:00
|
|
|
import { selectShowMatureContent } from 'redux/selectors/settings';
|
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-10-17 10:36:14 +02:00
|
|
|
import { isClaimNsfw } from 'util/claim';
|
2021-12-02 17:49:13 +01:00
|
|
|
import { selectSubscriptionUris } from 'redux/selectors/subscriptions';
|
2020-06-23 19:38:18 +02:00
|
|
|
|
2021-11-09 16:32:16 +01:00
|
|
|
type State = { claims: any, comments: CommentsState };
|
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;
|
|
|
|
export const selectIsFetchingCommentsByParentId = (state: State) => selectState(state).isLoadingByParentId;
|
|
|
|
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];
|
|
|
|
};
|
|
|
|
|
|
|
|
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
|
|
|
|
2020-08-24 19:35:21 +02:00
|
|
|
export const makeSelectCommentForCommentId = (commentId: string) =>
|
2021-03-03 19:50:16 +01:00
|
|
|
createSelector(selectCommentsById, (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;
|
|
|
|
});
|
|
|
|
|
2021-10-11 05:22:05 +02:00
|
|
|
export const selectLinkedCommentAncestors = (state: State) => selectState(state).linkedCommentAncestors;
|
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,
|
|
|
|
};
|
|
|
|
|
|
|
|
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] || [];
|
|
|
|
if (!replyIdsForParent.length) return null;
|
|
|
|
|
|
|
|
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) => {
|
|
|
|
const filterProps = filterInputs.reduce(function (acc, cur, i) {
|
|
|
|
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,
|
|
|
|
} = 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
|
|
|
|
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
|
|
|
});
|
|
|
|
|
2020-09-30 20:46:17 +02:00
|
|
|
export const makeSelectTotalCommentsCountForUri = (uri: string) =>
|
2021-07-15 16:43:28 +02:00
|
|
|
createSelector(selectState, selectCommentsByUri, (state, byUri) => {
|
|
|
|
const claimId = byUri[uri];
|
|
|
|
return state.totalCommentsById[claimId] || 0;
|
2020-09-30 20:46:17 +02:00
|
|
|
});
|
2021-03-03 19:50: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(
|
|
|
|
selectClaimIdsByUri,
|
|
|
|
selectClaimsById,
|
|
|
|
selectTopLevelCommentsForUri,
|
|
|
|
selectSubscriptionUris,
|
|
|
|
(claimIdsByUri, claimsById, topLevelComments, subscriptionUris) => {
|
|
|
|
const commentorUris = [];
|
|
|
|
const canonicalCommentors = [];
|
|
|
|
const canonicalSubscriptions = [];
|
|
|
|
|
|
|
|
topLevelComments.forEach((comment) => {
|
|
|
|
const uri = comment.channel_url;
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
subscriptionUris.forEach((uri) => {
|
|
|
|
// Update: canonicalSubscriptions
|
|
|
|
const claimId = claimIdsByUri[uri];
|
|
|
|
const claim = claimsById[claimId];
|
|
|
|
if (claim && claim.canonical_url) {
|
|
|
|
canonicalSubscriptions.push(claim.canonical_url);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return {
|
|
|
|
topLevelComments,
|
|
|
|
commentorUris,
|
|
|
|
canonicalCommentors,
|
|
|
|
canonicalSubscriptions,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
)((state, uri, maxCount) => `${String(uri)}:${maxCount}`);
|