2020-06-23 19:38:18 +02:00
// @flow
import * as ACTIONS from 'constants/action_types' ;
2020-09-29 16:10:23 +02:00
import * as REACTION _TYPES from 'constants/reactions' ;
2021-06-19 11:52:17 +02:00
import * as PAGES from 'constants/pages' ;
2021-07-15 16:43:28 +02:00
import { SORT _BY , BLOCK _LEVEL } from 'constants/comment' ;
2021-07-15 22:22:44 +02:00
import {
Lbry ,
parseURI ,
buildURI ,
selectClaimsById ,
selectClaimsByUri ,
selectMyChannelClaims ,
isURIEqual ,
} from 'lbry-redux' ;
2020-10-20 15:29:49 +02:00
import { doToast , doSeeNotifications } from 'redux/actions/notifications' ;
2020-09-29 20:45:28 +02:00
import {
makeSelectMyReactionsForComment ,
makeSelectOthersReactionsForComment ,
2020-09-30 17:59:05 +02:00
selectPendingCommentReacts ,
2021-03-03 19:50:16 +01:00
selectModerationBlockList ,
2021-05-25 08:17:36 +02:00
selectModerationDelegatorsById ,
2020-09-29 20:45:28 +02:00
} from 'redux/selectors/comments' ;
2020-10-20 15:29:49 +02:00
import { makeSelectNotificationForCommentId } from 'redux/selectors/notifications' ;
2021-02-09 17:05:56 +01:00
import { selectActiveChannelClaim } from 'redux/selectors/app' ;
2021-02-11 06:12:41 +01:00
import { toHex } from 'util/hex' ;
import Comments from 'comments' ;
2021-06-24 16:33:11 +02:00
import { selectPrefsReady } from 'redux/selectors/sync' ;
import { doAlertWaitingForSync } from 'redux/actions/app' ;
2020-06-23 19:38:18 +02:00
2021-07-15 16:43:28 +02:00
const isDev = process . env . NODE _ENV !== 'production' ;
2021-07-25 14:52:45 +02:00
const FETCH _API _FAILED _TO _FETCH = 'Failed to fetch' ;
2021-07-15 16:43:28 +02:00
2021-08-10 04:20:55 +02:00
const COMMENTRON _MSG _REMAP = {
// <-- Commentron msg --> : <-- App msg -->
'channel is blocked by publisher' : 'Unable to comment. This channel has blocked you.' ,
'channel is not allowed to post comments' : 'Unable to comment. Your channel has been blocked by an admin.' ,
'comments are disabled by the creator' : 'Unable to comment. The content owner has disabled comments.' ,
'duplicate comment!' : 'Please do not spam.' ,
} ;
const COMMENTRON _REGEX _MAP = {
// <-- App msg --> : <-- Regex of Commentron msg -->
'Your user name "%1%" is too close to the creator\'s user name "%2%" and may cause confusion. Please use another identity.' : /^your user name (.*) is too close to the creator's user name (.*) and may cause confusion. Please use another identity.$/ ,
'Slow mode is on. Please wait up to %1% seconds before commenting again.' : /^Slow mode is on. Please wait at most (.*) seconds before commenting again.$/ ,
'The comment contains contents that are blocked by %1%.' : /^the comment contents are blocked by (.*)$/ ,
} ;
2021-07-15 16:43:28 +02:00
function devToast ( dispatch , msg ) {
if ( isDev ) {
console . error ( msg ) ; // eslint-disable-line
dispatch ( doToast ( { isError : true , message : ` DEV: ${ msg } ` } ) ) ;
}
}
export function doCommentList (
uri : string ,
parentId : string ,
page : number = 1 ,
pageSize : number = 99999 ,
sortBy : number = SORT _BY . NEWEST
) {
2020-11-16 20:09:00 +01:00
return ( dispatch : Dispatch , getState : GetState ) => {
2020-06-23 19:38:18 +02:00
const state = getState ( ) ;
const claim = selectClaimsByUri ( state ) [ uri ] ;
const claimId = claim ? claim . claim _id : null ;
2021-02-11 06:12:41 +01:00
if ( ! claimId ) {
dispatch ( {
type : ACTIONS . COMMENT _LIST _FAILED ,
data : 'unable to find claim for uri' ,
} ) ;
return ;
}
2020-06-23 19:38:18 +02:00
dispatch ( {
type : ACTIONS . COMMENT _LIST _STARTED ,
2021-07-15 16:43:28 +02:00
data : {
parentId ,
} ,
2020-06-23 19:38:18 +02:00
} ) ;
2021-02-11 06:12:41 +01:00
2021-06-03 07:57:50 +02:00
// Adding 'channel_id' and 'channel_name' enables "CreatorSettings > commentsEnabled".
const authorChannelClaim = claim . value _type === 'channel' ? claim : claim . signing _channel ;
2021-02-11 06:12:41 +01:00
return Comments . comment _list ( {
2020-06-23 19:38:18 +02:00
page ,
2021-02-11 06:12:41 +01:00
claim _id : claimId ,
2020-06-23 19:38:18 +02:00
page _size : pageSize ,
2021-07-15 16:43:28 +02:00
parent _id : parentId || undefined ,
top _level : ! parentId ,
2021-06-03 07:57:50 +02:00
channel _id : authorChannelClaim ? authorChannelClaim . claim _id : undefined ,
channel _name : authorChannelClaim ? authorChannelClaim . name : undefined ,
2021-07-15 16:43:28 +02:00
sort _by : sortBy ,
2020-06-23 19:38:18 +02:00
} )
. then ( ( result : CommentListResponse ) => {
2021-07-15 16:43:28 +02:00
const { items : comments , total _items , total _filtered _items , total _pages } = result ;
2020-06-23 19:38:18 +02:00
dispatch ( {
type : ACTIONS . COMMENT _LIST _COMPLETED ,
data : {
comments ,
2021-07-15 16:43:28 +02:00
parentId ,
totalItems : total _items ,
totalFilteredItems : total _filtered _items ,
totalPages : total _pages ,
2020-06-23 19:38:18 +02:00
claimId : claimId ,
2021-06-03 07:57:50 +02:00
authorClaimId : authorChannelClaim ? authorChannelClaim . claim _id : undefined ,
2020-06-23 19:38:18 +02:00
uri : uri ,
} ,
} ) ;
2021-07-15 16:43:28 +02:00
2020-10-20 05:20:38 +02:00
return result ;
2020-06-23 19:38:18 +02:00
} )
2021-03-03 19:50:16 +01:00
. catch ( ( error ) => {
2021-07-25 14:52:45 +02:00
switch ( error . message ) {
case 'comments are disabled by the creator' :
dispatch ( {
type : ACTIONS . COMMENT _LIST _COMPLETED ,
data : {
authorClaimId : authorChannelClaim ? authorChannelClaim . claim _id : undefined ,
disabled : true ,
} ,
} ) ;
break ;
case FETCH _API _FAILED _TO _FETCH :
dispatch (
doToast ( {
isError : true ,
message : Comments . isCustomServer
? _ _ ( 'Failed to fetch comments. Verify custom server settings.' )
: _ _ ( 'Failed to fetch comments.' ) ,
} )
) ;
dispatch ( { type : ACTIONS . COMMENT _LIST _FAILED , data : error } ) ;
break ;
default :
dispatch ( doToast ( { isError : true , message : ` ${ error . message } ` } ) ) ;
dispatch ( { type : ACTIONS . COMMENT _LIST _FAILED , data : error } ) ;
2021-06-03 07:57:50 +02:00
}
2020-06-23 19:38:18 +02:00
} ) ;
} ;
}
2021-07-15 16:43:28 +02:00
export function doCommentById ( commentId : string , toastIfNotFound : boolean = true ) {
return ( dispatch : Dispatch , getState : GetState ) => {
return Comments . comment _by _id ( { comment _id : commentId , with _ancestors : true } )
. then ( ( result : CommentByIdResponse ) => {
const { item , items , ancestors } = result ;
dispatch ( {
type : ACTIONS . COMMENT _BY _ID _COMPLETED ,
data : {
comment : item || items , // Requested a change to rename it to 'item'. This covers both.
ancestors : ancestors ,
} ,
} ) ;
return result ;
} )
. catch ( ( error ) => {
if ( error . message === 'sql: no rows in result set' && toastIfNotFound ) {
dispatch (
doToast ( {
isError : true ,
message : _ _ ( 'The requested comment is no longer available.' ) ,
} )
) ;
} else {
devToast ( dispatch , error . message ) ;
}
} ) ;
} ;
}
export function doCommentReset ( uri : string ) {
return ( dispatch : Dispatch , getState : GetState ) => {
const state = getState ( ) ;
const claim = selectClaimsByUri ( state ) [ uri ] ;
const claimId = claim ? claim . claim _id : null ;
if ( ! claimId ) {
dispatch ( {
type : ACTIONS . COMMENT _LIST _FAILED ,
data : 'unable to find claim for uri' ,
} ) ;
return ;
}
dispatch ( {
type : ACTIONS . COMMENT _LIST _RESET ,
data : {
claimId ,
} ,
} ) ;
} ;
}
2021-04-23 21:59:48 +02:00
export function doSuperChatList ( uri : string ) {
return ( dispatch : Dispatch , getState : GetState ) => {
const state = getState ( ) ;
const claim = selectClaimsByUri ( state ) [ uri ] ;
const claimId = claim ? claim . claim _id : null ;
if ( ! claimId ) {
console . error ( 'No claimId found for uri: ' , uri ) ; //eslint-disable-line
return ;
}
dispatch ( {
type : ACTIONS . COMMENT _SUPER _CHAT _LIST _STARTED ,
} ) ;
return Comments . super _list ( {
claim _id : claimId ,
} )
2021-07-15 16:43:28 +02:00
. then ( ( result : SuperListResponse ) => {
2021-04-23 21:59:48 +02:00
const { items : comments , total _amount : totalAmount } = result ;
dispatch ( {
type : ACTIONS . COMMENT _SUPER _CHAT _LIST _COMPLETED ,
data : {
comments ,
totalAmount ,
uri : uri ,
} ,
} ) ;
} )
. catch ( ( error ) => {
dispatch ( {
type : ACTIONS . COMMENT _SUPER _CHAT _LIST _FAILED ,
data : error ,
} ) ;
} ) ;
} ;
}
2021-07-15 16:43:28 +02:00
export function doCommentReactList ( commentIds : Array < string > ) {
2021-07-15 05:24:37 +02:00
return async ( dispatch : Dispatch , getState : GetState ) => {
2020-09-29 16:10:23 +02:00
const state = getState ( ) ;
2021-02-09 17:05:56 +01:00
const activeChannelClaim = selectActiveChannelClaim ( state ) ;
2020-09-30 17:59:05 +02:00
2020-09-29 16:10:23 +02:00
dispatch ( {
type : ACTIONS . COMMENT _REACTION _LIST _STARTED ,
} ) ;
2021-02-09 17:05:56 +01:00
2021-07-15 05:24:37 +02:00
const params : ReactionListParams = {
2020-09-29 16:10:23 +02:00
comment _ids : commentIds . join ( ',' ) ,
2020-09-30 17:59:05 +02:00
} ;
2021-02-09 17:05:56 +01:00
if ( activeChannelClaim ) {
2021-07-15 05:24:37 +02:00
const signatureData = await channelSignName ( activeChannelClaim . claim _id , activeChannelClaim . name ) ;
if ( ! signatureData ) {
return dispatch ( doToast ( { isError : true , message : _ _ ( 'Unable to verify your channel. Please try again.' ) } ) ) ;
}
params . channel _name = activeChannelClaim . name ;
params . channel _id = activeChannelClaim . claim _id ;
params . signature = signatureData . signature ;
params . signing _ts = signatureData . signing _ts ;
2020-09-30 17:59:05 +02:00
}
2020-10-06 21:35:13 +02:00
2021-07-15 05:24:37 +02:00
return Comments . reaction _list ( params )
. then ( ( result : ReactionListResponse ) => {
2020-09-29 16:10:23 +02:00
const { my _reactions : myReactions , others _reactions : othersReactions } = result ;
dispatch ( {
type : ACTIONS . COMMENT _REACTION _LIST _COMPLETED ,
data : {
2021-07-20 08:55:26 +02:00
myReactions ,
2020-09-29 16:10:23 +02:00
othersReactions ,
2021-07-15 16:43:28 +02:00
channelId : activeChannelClaim ? activeChannelClaim . claim _id : undefined ,
2021-07-20 08:55:26 +02:00
commentIds ,
2020-09-29 16:10:23 +02:00
} ,
} ) ;
} )
2021-03-03 19:50:16 +01:00
. catch ( ( error ) => {
2020-09-29 16:10:23 +02:00
dispatch ( {
type : ACTIONS . COMMENT _REACTION _LIST _FAILED ,
2021-07-18 04:54:01 +02:00
data : error ,
2020-09-29 16:10:23 +02:00
} ) ;
} ) ;
} ;
}
export function doCommentReact ( commentId : string , type : string ) {
2021-07-15 05:24:37 +02:00
return async ( dispatch : Dispatch , getState : GetState ) => {
2020-09-29 16:10:23 +02:00
const state = getState ( ) ;
2021-02-09 17:05:56 +01:00
const activeChannelClaim = selectActiveChannelClaim ( state ) ;
2020-09-30 17:59:05 +02:00
const pendingReacts = selectPendingCommentReacts ( state ) ;
2020-10-20 15:29:49 +02:00
const notification = makeSelectNotificationForCommentId ( commentId ) ( state ) ;
2021-02-09 17:05:56 +01:00
if ( ! activeChannelClaim ) {
console . error ( 'Unable to react to comment. No activeChannel is set.' ) ; // eslint-disable-line
return ;
}
2020-10-20 15:29:49 +02:00
if ( notification && ! notification . is _seen ) {
dispatch ( doSeeNotifications ( [ notification . id ] ) ) ;
}
2021-02-09 17:05:56 +01:00
2020-09-30 17:59:05 +02:00
const exclusiveTypes = {
[ REACTION _TYPES . LIKE ] : REACTION _TYPES . DISLIKE ,
[ REACTION _TYPES . DISLIKE ] : REACTION _TYPES . LIKE ,
} ;
2021-02-09 17:05:56 +01:00
2020-10-02 02:25:30 +02:00
if ( pendingReacts . includes ( commentId + exclusiveTypes [ type ] ) || pendingReacts . includes ( commentId + type ) ) {
2020-09-30 17:59:05 +02:00
// ignore dislikes during likes, for example
return ;
}
2021-02-09 17:05:56 +01:00
2021-07-15 16:43:28 +02:00
const reactKey = ` ${ commentId } : ${ activeChannelClaim . claim _id } ` ;
const myReacts = makeSelectMyReactionsForComment ( reactKey ) ( state ) ;
const othersReacts = makeSelectOthersReactionsForComment ( reactKey ) ( state ) ;
2021-07-15 05:24:37 +02:00
const signatureData = await channelSignName ( activeChannelClaim . claim _id , activeChannelClaim . name ) ;
if ( ! signatureData ) {
return dispatch ( doToast ( { isError : true , message : _ _ ( 'Unable to verify your channel. Please try again.' ) } ) ) ;
}
const params : ReactionReactParams = {
2020-09-29 16:10:23 +02:00
comment _ids : commentId ,
2021-02-09 17:05:56 +01:00
channel _name : activeChannelClaim . name ,
channel _id : activeChannelClaim . claim _id ,
2021-07-15 05:24:37 +02:00
signature : signatureData . signature ,
signing _ts : signatureData . signing _ts ,
type : type ,
2020-09-29 16:10:23 +02:00
} ;
2021-02-09 17:05:56 +01:00
2020-09-29 16:10:23 +02:00
if ( myReacts . includes ( type ) ) {
params [ 'remove' ] = true ;
2020-09-29 20:45:28 +02:00
myReacts . splice ( myReacts . indexOf ( type ) , 1 ) ;
} else {
myReacts . push ( type ) ;
if ( Object . keys ( exclusiveTypes ) . includes ( type ) ) {
params [ 'clear_types' ] = exclusiveTypes [ type ] ;
if ( myReacts . indexOf ( exclusiveTypes [ type ] ) !== - 1 ) {
myReacts . splice ( myReacts . indexOf ( exclusiveTypes [ type ] ) , 1 ) ;
}
}
2020-09-29 16:10:23 +02:00
}
2021-07-15 05:24:37 +02:00
2020-09-29 20:45:28 +02:00
dispatch ( {
type : ACTIONS . COMMENT _REACT _STARTED ,
2020-09-30 17:59:05 +02:00
data : commentId + type ,
2020-09-29 20:45:28 +02:00
} ) ;
2020-10-02 02:25:30 +02:00
2020-09-29 20:45:28 +02:00
// simulate api return shape: ['like'] -> { 'like': 1 }
const myReactsObj = myReacts . reduce ( ( acc , el ) => {
acc [ el ] = 1 ;
return acc ;
} , { } ) ;
2020-09-29 16:10:23 +02:00
2020-10-02 02:25:30 +02:00
dispatch ( {
type : ACTIONS . COMMENT _REACTION _LIST _COMPLETED ,
data : {
2021-07-15 16:43:28 +02:00
myReactions : { [ reactKey ] : myReactsObj } ,
othersReactions : { [ reactKey ] : othersReacts } ,
2020-10-02 02:25:30 +02:00
} ,
} ) ;
2021-07-15 05:24:37 +02:00
Comments . reaction _react ( params )
. then ( ( result : ReactionReactResponse ) => {
2020-09-29 16:10:23 +02:00
dispatch ( {
type : ACTIONS . COMMENT _REACT _COMPLETED ,
2020-09-30 17:59:05 +02:00
data : commentId + type ,
2020-09-29 20:45:28 +02:00
} ) ;
2020-09-29 16:10:23 +02:00
} )
2021-03-03 19:50:16 +01:00
. catch ( ( error ) => {
2020-09-29 16:10:23 +02:00
dispatch ( {
type : ACTIONS . COMMENT _REACT _FAILED ,
2020-09-30 17:59:05 +02:00
data : commentId + type ,
2020-09-29 16:10:23 +02:00
} ) ;
2020-10-02 02:25:30 +02:00
const myRevertedReactsObj = myReacts
2021-03-03 19:50:16 +01:00
. filter ( ( el ) => el !== type )
2020-10-02 02:25:30 +02:00
. reduce ( ( acc , el ) => {
acc [ el ] = 1 ;
return acc ;
} , { } ) ;
dispatch ( {
type : ACTIONS . COMMENT _REACTION _LIST _COMPLETED ,
data : {
myReactions : { [ commentId ] : myRevertedReactsObj } ,
othersReactions : { [ commentId ] : othersReacts } ,
} ,
} ) ;
2020-09-29 16:10:23 +02:00
} ) ;
} ;
}
2021-07-06 22:28:29 +02:00
/ * *
*
* @ param comment
* @ param claim _id - File claim id
* @ param parent _id - What is this ?
* @ param uri
* @ param livestream
* @ param { string } [ txid ] Optional transaction id
* @ param { string } [ payment _intent _id ] Optional transaction id
* @ param { string } [ environment ] Optional environment for Stripe ( test | live )
* @ returns { ( function ( Dispatch , GetState ) : Promise < undefined | void | * > ) | * }
* /
2021-03-16 19:37:19 +01:00
export function doCommentCreate (
comment : string = '' ,
claim _id : string = '' ,
parent _id ? : string ,
uri : string ,
2021-04-23 21:59:48 +02:00
livestream ? : boolean = false ,
2021-07-06 22:28:29 +02:00
txid ? : string ,
payment _intent _id ? : string ,
2021-07-20 08:55:26 +02:00
environment ? : string
2021-03-16 19:37:19 +01:00
) {
2021-04-23 21:59:48 +02:00
return async ( dispatch : Dispatch , getState : GetState ) => {
2020-06-23 19:38:18 +02:00
const state = getState ( ) ;
2021-07-06 22:28:29 +02:00
// get active channel that will receive comment and optional tip
2021-02-09 17:05:56 +01:00
const activeChannelClaim = selectActiveChannelClaim ( state ) ;
if ( ! activeChannelClaim ) {
console . error ( 'Unable to create comment. No activeChannel is set.' ) ; // eslint-disable-line
return ;
}
2020-06-23 19:38:18 +02:00
dispatch ( {
type : ACTIONS . COMMENT _CREATE _STARTED ,
} ) ;
2021-04-23 21:59:48 +02:00
let signatureData ;
if ( activeChannelClaim ) {
try {
signatureData = await Lbry . channel _sign ( {
channel _id : activeChannelClaim . claim _id ,
hexdata : toHex ( comment ) ,
} ) ;
} catch ( e ) { }
}
2021-07-06 22:28:29 +02:00
// send a notification
2020-10-20 15:29:49 +02:00
if ( parent _id ) {
const notification = makeSelectNotificationForCommentId ( parent _id ) ( state ) ;
if ( notification && ! notification . is _seen ) {
dispatch ( doSeeNotifications ( [ notification . id ] ) ) ;
}
}
2021-04-23 21:59:48 +02:00
if ( ! signatureData ) {
return dispatch ( doToast ( { isError : true , message : _ _ ( 'Unable to verify your channel. Please try again.' ) } ) ) ;
}
2021-07-06 22:28:29 +02:00
// Comments is a function which helps make calls to the backend
// these params passed in POST call.
2021-04-23 21:59:48 +02:00
return Comments . comment _create ( {
2020-06-23 19:38:18 +02:00
comment : comment ,
claim _id : claim _id ,
2021-02-09 17:05:56 +01:00
channel _id : activeChannelClaim . claim _id ,
2021-04-23 21:59:48 +02:00
channel _name : activeChannelClaim . name ,
2020-06-23 19:38:18 +02:00
parent _id : parent _id ,
2021-04-23 21:59:48 +02:00
signature : signatureData . signature ,
signing _ts : signatureData . signing _ts ,
2021-07-06 22:28:29 +02:00
... ( txid ? { support _tx _id : txid } : { } ) , // add transaction id if it exists
... ( payment _intent _id ? { payment _intent _id } : { } ) , // add payment_intent_id if it exists
... ( environment ? { environment } : { } ) , // add environment for stripe if it exists
2020-06-23 19:38:18 +02:00
} )
. then ( ( result : CommentCreateResponse ) => {
dispatch ( {
type : ACTIONS . COMMENT _CREATE _COMPLETED ,
data : {
2020-09-09 20:53:31 +02:00
uri ,
2021-03-16 19:37:19 +01:00
livestream ,
2020-06-23 19:38:18 +02:00
comment : result ,
claimId : claim _id ,
} ,
} ) ;
2020-09-30 02:11:48 +02:00
return result ;
2020-06-23 19:38:18 +02:00
} )
2021-03-03 19:50:16 +01:00
. catch ( ( error ) => {
2021-08-10 04:20:55 +02:00
dispatch ( { type : ACTIONS . COMMENT _CREATE _FAILED , data : error } ) ;
let toastMessage ;
2021-03-03 19:50:16 +01:00
2021-08-10 04:20:55 +02:00
for ( const commentronMsg in COMMENTRON _MSG _REMAP ) {
if ( error . message === commentronMsg ) {
toastMessage = _ _ ( COMMENTRON _MSG _REMAP [ commentronMsg ] ) ;
break ;
}
2021-03-03 19:50:16 +01:00
}
2021-08-10 04:20:55 +02:00
if ( ! toastMessage ) {
for ( const i18nStr in COMMENTRON _REGEX _MAP ) {
const regex = COMMENTRON _REGEX _MAP [ i18nStr ] ;
const match = error . message . match ( regex ) ;
if ( match ) {
const subs = { } ;
for ( let i = 1 ; i < match . length ; ++ i ) {
subs [ ` ${ i } ` ] = match [ i ] ;
2021-07-03 17:56:41 +02:00
}
2021-08-10 04:20:55 +02:00
toastMessage = _ _ ( i18nStr , subs ) ;
2021-07-03 17:56:41 +02:00
break ;
2021-08-10 04:20:55 +02:00
}
2021-04-22 15:36:40 +02:00
}
}
2021-08-10 04:20:55 +02:00
if ( ! toastMessage ) {
// Fallback to commentron original message. It will be in English
// only and most likely not capitalized correctly.
toastMessage = error . message ;
}
2021-04-23 21:59:48 +02:00
2021-08-10 04:20:55 +02:00
dispatch ( doToast ( { message : toastMessage , isError : true } ) ) ;
2021-04-23 21:59:48 +02:00
return Promise . reject ( error ) ;
2020-06-23 19:38:18 +02:00
} ) ;
} ;
}
2021-07-15 16:43:28 +02:00
export function doCommentPin ( commentId : string , claimId : string , remove : boolean ) {
2021-07-15 05:24:37 +02:00
return async ( dispatch : Dispatch , getState : GetState ) => {
2020-10-20 05:20:38 +02:00
const state = getState ( ) ;
2021-02-09 17:05:56 +01:00
const activeChannel = selectActiveChannelClaim ( state ) ;
if ( ! activeChannel ) {
console . error ( 'Unable to pin comment. No activeChannel is set.' ) ; // eslint-disable-line
return ;
}
2020-10-20 05:20:38 +02:00
2021-07-15 05:24:37 +02:00
const signedCommentId = await channelSignData ( activeChannel . claim _id , commentId ) ;
if ( ! signedCommentId ) {
return dispatch ( doToast ( { isError : true , message : _ _ ( 'Unable to verify your channel. Please try again.' ) } ) ) ;
}
2020-10-20 05:20:38 +02:00
dispatch ( {
type : ACTIONS . COMMENT _PIN _STARTED ,
} ) ;
2021-07-15 05:24:37 +02:00
const params : CommentPinParams = {
2021-02-09 17:05:56 +01:00
comment _id : commentId ,
channel _id : activeChannel . claim _id ,
2021-07-15 05:24:37 +02:00
channel _name : activeChannel . name ,
remove : remove ,
signature : signedCommentId . signature ,
signing _ts : signedCommentId . signing _ts ,
} ;
return Comments . comment _pin ( params )
2020-10-20 05:20:38 +02:00
. then ( ( result : CommentPinResponse ) => {
dispatch ( {
type : ACTIONS . COMMENT _PIN _COMPLETED ,
2021-07-15 16:43:28 +02:00
data : {
pinnedComment : result . items ,
claimId ,
unpin : remove ,
} ,
2020-10-20 05:20:38 +02:00
} ) ;
} )
2021-03-03 19:50:16 +01:00
. catch ( ( error ) => {
2020-10-20 05:20:38 +02:00
dispatch ( {
type : ACTIONS . COMMENT _PIN _FAILED ,
data : error ,
} ) ;
dispatch (
doToast ( {
message : 'Unable to pin this comment, please try again later.' ,
isError : true ,
} )
) ;
} ) ;
} ;
}
2021-02-11 06:12:41 +01:00
export function doCommentAbandon ( commentId : string , creatorChannelUri ? : string ) {
return async ( dispatch : Dispatch , getState : GetState ) => {
const state = getState ( ) ;
const claim = creatorChannelUri ? selectClaimsByUri ( state ) [ creatorChannelUri ] : undefined ;
const creatorChannelId = claim ? claim . claim _id : null ;
const creatorChannelName = claim ? claim . name : null ;
const activeChannelClaim = selectActiveChannelClaim ( state ) ;
2020-06-23 19:38:18 +02:00
dispatch ( {
type : ACTIONS . COMMENT _ABANDON _STARTED ,
} ) ;
2021-02-11 06:12:41 +01:00
let commentIdSignature ;
if ( activeChannelClaim ) {
try {
commentIdSignature = await Lbry . channel _sign ( {
channel _id : activeChannelClaim . claim _id ,
hexdata : toHex ( commentId ) ,
} ) ;
} catch ( e ) { }
}
return Comments . comment _abandon ( {
comment _id : commentId ,
... ( creatorChannelId ? { creator _channel _id : creatorChannelId } : { } ) ,
... ( creatorChannelName ? { creator _channel _name : creatorChannelName } : { } ) ,
... ( commentIdSignature || { } ) ,
2020-06-23 19:38:18 +02:00
} )
. then ( ( result : CommentAbandonResponse ) => {
// Comment may not be deleted if the signing channel can't be signed.
// This will happen if the channel was recently created or abandoned.
if ( result . abandoned ) {
dispatch ( {
type : ACTIONS . COMMENT _ABANDON _COMPLETED ,
data : {
2021-02-11 06:12:41 +01:00
comment _id : commentId ,
2020-06-23 19:38:18 +02:00
} ,
} ) ;
} else {
dispatch ( {
type : ACTIONS . COMMENT _ABANDON _FAILED ,
} ) ;
dispatch (
doToast ( {
message : 'Your channel is still being setup, try again in a few moments.' ,
isError : true ,
} )
) ;
}
} )
2021-03-03 19:50:16 +01:00
. catch ( ( error ) => {
2020-06-23 19:38:18 +02:00
dispatch ( {
type : ACTIONS . COMMENT _ABANDON _FAILED ,
data : error ,
} ) ;
2021-02-11 06:12:41 +01:00
2020-06-23 19:38:18 +02:00
dispatch (
doToast ( {
message : 'Unable to delete this comment, please try again later.' ,
isError : true ,
} )
) ;
} ) ;
} ;
}
export function doCommentUpdate ( comment _id : string , comment : string ) {
// if they provided an empty string, they must have wanted to abandon
if ( comment === '' ) {
return doCommentAbandon ( comment _id ) ;
} else {
2021-07-15 05:24:37 +02:00
return async ( dispatch : Dispatch , getState : GetState ) => {
const state = getState ( ) ;
const activeChannelClaim = selectActiveChannelClaim ( state ) ;
if ( ! activeChannelClaim ) {
return dispatch ( doToast ( { isError : true , message : _ _ ( 'No active channel selected.' ) } ) ) ;
}
const signedComment = await channelSignData ( activeChannelClaim . claim _id , comment ) ;
if ( ! signedComment ) {
return dispatch ( doToast ( { isError : true , message : _ _ ( 'Unable to verify your channel. Please try again.' ) } ) ) ;
}
2020-06-23 19:38:18 +02:00
dispatch ( {
type : ACTIONS . COMMENT _UPDATE _STARTED ,
} ) ;
2021-07-15 05:24:37 +02:00
return Comments . comment _edit ( {
2020-06-23 19:38:18 +02:00
comment _id : comment _id ,
comment : comment ,
2021-07-15 05:24:37 +02:00
signature : signedComment . signature ,
signing _ts : signedComment . signing _ts ,
2020-06-23 19:38:18 +02:00
} )
2021-07-15 05:24:37 +02:00
. then ( ( result : CommentEditResponse ) => {
2020-06-23 19:38:18 +02:00
if ( result != null ) {
dispatch ( {
type : ACTIONS . COMMENT _UPDATE _COMPLETED ,
data : {
comment : result ,
} ,
} ) ;
} else {
// the result will return null
dispatch ( {
type : ACTIONS . COMMENT _UPDATE _FAILED ,
} ) ;
dispatch (
doToast ( {
message : 'Your channel is still being setup, try again in a few moments.' ,
isError : true ,
} )
) ;
}
} )
2021-03-03 19:50:16 +01:00
. catch ( ( error ) => {
2020-06-23 19:38:18 +02:00
dispatch ( {
type : ACTIONS . COMMENT _UPDATE _FAILED ,
data : error ,
} ) ;
dispatch (
doToast ( {
message : 'Unable to edit this comment, please try again later.' ,
isError : true ,
} )
) ;
} ) ;
} ;
}
}
2021-02-11 06:12:41 +01:00
2021-05-28 03:06:07 +02:00
async function channelSignName ( channelClaimId : string , channelName : string ) {
let signedObject ;
try {
signedObject = await Lbry . channel _sign ( {
channel _id : channelClaimId ,
hexdata : toHex ( channelName ) ,
} ) ;
signedObject [ 'claim_id' ] = channelClaimId ;
signedObject [ 'name' ] = channelName ;
} catch ( e ) { }
return signedObject ;
}
2021-07-15 05:24:37 +02:00
async function channelSignData ( channelClaimId : string , data : string ) {
let signedObject ;
try {
signedObject = await Lbry . channel _sign ( {
channel _id : channelClaimId ,
hexdata : toHex ( data ) ,
} ) ;
} catch ( e ) { }
return signedObject ;
}
2021-02-11 06:12:41 +01:00
// Hides a users comments from all creator's claims and prevent them from commenting in the future
2021-05-25 08:17:36 +02:00
function doCommentModToggleBlock (
unblock : boolean ,
commenterUri : string ,
creatorId : string ,
blockerIds : Array < string > , // [] = use all my channels
blockLevel : string ,
showLink : boolean = false
) {
2021-02-11 06:12:41 +01:00
return async ( dispatch : Dispatch , getState : GetState ) => {
const state = getState ( ) ;
2021-06-24 16:33:11 +02:00
const ready = selectPrefsReady ( state ) ;
2021-05-25 08:17:36 +02:00
let blockerChannelClaims = selectMyChannelClaims ( state ) ;
2021-06-24 16:33:11 +02:00
if ( ! ready ) {
return dispatch ( doAlertWaitingForSync ( ) ) ;
}
if ( ! blockerChannelClaims ) {
return dispatch (
doToast ( {
message : _ _ ( 'Create a channel to change this setting.' ) ,
isError : false ,
} )
) ;
}
2021-05-25 08:17:36 +02:00
if ( blockerIds . length === 0 ) {
// Specific blockers not provided, so find one based on block-level.
switch ( blockLevel ) {
case BLOCK _LEVEL . MODERATOR :
{
// Find the first channel that is a moderator for 'creatorId'.
const delegatorsById = selectModerationDelegatorsById ( state ) ;
blockerChannelClaims = [
blockerChannelClaims . find ( ( x ) => {
const delegatorDataForId = delegatorsById [ x . claim _id ] ;
return delegatorDataForId && Object . values ( delegatorDataForId . delegators ) . includes ( creatorId ) ;
} ) ,
] ;
}
break ;
case BLOCK _LEVEL . ADMIN :
{
// Find the first admin channel and use that.
const delegatorsById = selectModerationDelegatorsById ( state ) ;
blockerChannelClaims = [
blockerChannelClaims . find ( ( x ) => delegatorsById [ x . claim _id ] && delegatorsById [ x . claim _id ] . global ) ,
] ;
}
break ;
}
} else {
blockerChannelClaims = blockerChannelClaims . filter ( ( x ) => blockerIds . includes ( x . claim _id ) ) ;
}
2021-06-19 04:33:32 +02:00
const { channelName , channelClaimId } = parseURI ( commenterUri ) ;
2021-02-11 06:12:41 +01:00
2021-05-25 08:17:36 +02:00
const creatorClaim = selectClaimsById ( state ) [ creatorId ] ;
if ( creatorId && ! creatorClaim ) {
console . error ( "Can't find creator claim" ) ; // eslint-disable-line
return ;
}
2021-03-03 19:50:16 +01:00
dispatch ( {
type : unblock ? ACTIONS . COMMENT _MODERATION _UN _BLOCK _STARTED : ACTIONS . COMMENT _MODERATION _BLOCK _STARTED ,
data : {
2021-05-25 08:17:36 +02:00
blockedUri : commenterUri ,
creatorUri : creatorClaim ? creatorClaim . permanent _url : undefined ,
blockLevel : blockLevel ,
2021-03-03 19:50:16 +01:00
} ,
} ) ;
2021-02-11 06:12:41 +01:00
2021-06-19 04:33:32 +02:00
const commenterIdForAction = channelClaimId ;
const commenterNameForAction = channelName ;
2021-03-03 19:50:16 +01:00
let channelSignatures = [ ] ;
2021-02-11 06:12:41 +01:00
2021-03-03 19:50:16 +01:00
const sharedModBlockParams = unblock
? {
2021-05-25 08:17:36 +02:00
un _blocked _channel _id : commenterIdForAction ,
un _blocked _channel _name : commenterNameForAction ,
}
2021-03-03 19:50:16 +01:00
: {
2021-05-25 08:17:36 +02:00
blocked _channel _id : commenterIdForAction ,
blocked _channel _name : commenterNameForAction ,
} ;
2021-03-03 19:50:16 +01:00
const commentAction = unblock ? Comments . moderation _unblock : Comments . moderation _block ;
2021-05-25 08:17:36 +02:00
return Promise . all ( blockerChannelClaims . map ( ( x ) => channelSignName ( x . claim _id , x . name ) ) )
2021-05-28 03:06:07 +02:00
. then ( ( response ) => {
channelSignatures = response ;
// $FlowFixMe
return Promise . allSettled (
channelSignatures
. filter ( ( x ) => x !== undefined && x !== null )
. map ( ( signatureData ) =>
commentAction ( {
2021-05-25 08:17:36 +02:00
// $FlowFixMe
2021-05-28 03:06:07 +02:00
mod _channel _id : signatureData . claim _id ,
2021-05-25 08:17:36 +02:00
// $FlowFixMe
2021-05-28 03:06:07 +02:00
mod _channel _name : signatureData . name ,
2021-05-25 08:17:36 +02:00
// $FlowFixMe
2021-05-28 03:06:07 +02:00
signature : signatureData . signature ,
2021-05-25 08:17:36 +02:00
// $FlowFixMe
2021-05-28 03:06:07 +02:00
signing _ts : signatureData . signing _ts ,
2021-05-25 08:17:36 +02:00
creator _channel _id : creatorClaim ? creatorClaim . claim _id : undefined ,
creator _channel _name : creatorClaim ? creatorClaim . name : undefined ,
block _all : unblock ? undefined : blockLevel === BLOCK _LEVEL . ADMIN ,
global _un _block : unblock ? blockLevel === BLOCK _LEVEL . ADMIN : undefined ,
2021-05-28 03:06:07 +02:00
... sharedModBlockParams ,
} )
)
)
2021-05-25 08:17:36 +02:00
. then ( ( response ) => {
const failures = [ ] ;
response . forEach ( ( res , index ) => {
if ( res . status === 'rejected' ) {
// TODO: This should be error codes
if ( res . reason . message !== 'validation is disallowed for non controlling channels' ) {
// $FlowFixMe
failures . push ( channelSignatures [ index ] . name + ': ' + res . reason . message ) ;
}
}
} ) ;
if ( failures . length !== 0 ) {
dispatch ( doToast ( { message : failures . join ( ) , isError : true } ) ) ;
dispatch ( {
type : unblock ? ACTIONS . COMMENT _MODERATION _UN _BLOCK _FAILED : ACTIONS . COMMENT _MODERATION _BLOCK _FAILED ,
data : {
blockedUri : commenterUri ,
creatorUri : creatorClaim ? creatorClaim . permanent _url : undefined ,
blockLevel : blockLevel ,
} ,
} ) ;
return ;
}
2021-05-28 03:06:07 +02:00
dispatch ( {
type : unblock ? ACTIONS . COMMENT _MODERATION _UN _BLOCK _COMPLETE : ACTIONS . COMMENT _MODERATION _BLOCK _COMPLETE ,
2021-05-25 08:17:36 +02:00
data : {
blockedUri : commenterUri ,
creatorUri : creatorClaim ? creatorClaim . permanent _url : undefined ,
blockLevel : blockLevel ,
} ,
2021-05-28 03:06:07 +02:00
} ) ;
2021-05-25 08:17:36 +02:00
dispatch (
doToast ( {
message : unblock
? _ _ ( 'Channel unblocked!' )
: _ _ ( 'Channel "%channel%" blocked.' , { channel : commenterNameForAction } ) ,
linkText : _ _ ( showLink ? 'See All' : '' ) ,
linkTarget : '/settings/block_and_mute' ,
} )
) ;
2021-05-28 03:06:07 +02:00
} )
. catch ( ( ) => {
dispatch ( {
type : unblock ? ACTIONS . COMMENT _MODERATION _UN _BLOCK _FAILED : ACTIONS . COMMENT _MODERATION _BLOCK _FAILED ,
2021-05-25 08:17:36 +02:00
data : {
blockedUri : commenterUri ,
creatorUri : creatorClaim ? creatorClaim . permanent _url : undefined ,
blockLevel : blockLevel ,
} ,
2021-05-28 03:06:07 +02:00
} ) ;
} ) ;
2021-03-03 19:50:16 +01:00
} )
. catch ( ( ) => {
dispatch ( {
type : unblock ? ACTIONS . COMMENT _MODERATION _UN _BLOCK _FAILED : ACTIONS . COMMENT _MODERATION _BLOCK _FAILED ,
2021-05-25 08:17:36 +02:00
data : {
blockedUri : commenterUri ,
creatorUri : creatorClaim ? creatorClaim . permanent _url : undefined ,
blockLevel : blockLevel ,
} ,
2021-03-03 19:50:16 +01:00
} ) ;
} ) ;
} ;
}
2021-05-25 08:17:36 +02:00
/ * *
* Blocks the commenter for all channels that I own .
*
* @ param commenterUri
* @ param showLink
* @ returns { function ( Dispatch ) : * }
* /
export function doCommentModBlock ( commenterUri : string , showLink : boolean = true ) {
return ( dispatch : Dispatch ) => {
return dispatch ( doCommentModToggleBlock ( false , commenterUri , '' , [ ] , BLOCK _LEVEL . SELF , showLink ) ) ;
} ;
}
/ * *
* Blocks the commenter using the given channel that has Global privileges .
*
* @ param commenterUri
* @ param blockerId
* @ returns { function ( Dispatch ) : * }
* /
export function doCommentModBlockAsAdmin ( commenterUri : string , blockerId : string ) {
return ( dispatch : Dispatch ) => {
return dispatch ( doCommentModToggleBlock ( false , commenterUri , '' , blockerId ? [ blockerId ] : [ ] , BLOCK _LEVEL . ADMIN ) ) ;
} ;
}
/ * *
* Blocks the commenter using the given channel that has been granted
* moderation rights by the creator .
*
* @ param commenterUri
* @ param creatorId
* @ param blockerId
* @ returns { function ( Dispatch ) : * }
* /
export function doCommentModBlockAsModerator ( commenterUri : string , creatorId : string , blockerId : string ) {
2021-03-03 19:50:16 +01:00
return ( dispatch : Dispatch ) => {
2021-05-25 08:17:36 +02:00
return dispatch (
doCommentModToggleBlock ( false , commenterUri , creatorId , blockerId ? [ blockerId ] : [ ] , BLOCK _LEVEL . MODERATOR )
) ;
2021-03-03 19:50:16 +01:00
} ;
}
2021-05-25 08:17:36 +02:00
/ * *
* Unblocks the commenter for all channels that I own .
*
* @ param commenterUri
* @ param showLink
* @ returns { function ( Dispatch ) : * }
* /
export function doCommentModUnBlock ( commenterUri : string , showLink : boolean = true ) {
2021-03-03 19:50:16 +01:00
return ( dispatch : Dispatch ) => {
2021-05-25 08:17:36 +02:00
return dispatch ( doCommentModToggleBlock ( true , commenterUri , '' , [ ] , BLOCK _LEVEL . SELF , showLink ) ) ;
} ;
}
/ * *
* Unblocks the commenter using the given channel that has Global privileges .
*
* @ param commenterUri
* @ param blockerId
* @ returns { function ( Dispatch ) : * }
* /
export function doCommentModUnBlockAsAdmin ( commenterUri : string , blockerId : string ) {
return ( dispatch : Dispatch ) => {
return dispatch ( doCommentModToggleBlock ( true , commenterUri , '' , blockerId ? [ blockerId ] : [ ] , BLOCK _LEVEL . ADMIN ) ) ;
} ;
}
/ * *
* Unblocks the commenter using the given channel that has been granted
* moderation rights by the creator .
*
* @ param commenterUri
* @ param creatorId
* @ param blockerId
* @ returns { function ( Dispatch ) : * }
* /
export function doCommentModUnBlockAsModerator ( commenterUri : string , creatorId : string , blockerId : string ) {
return ( dispatch : Dispatch ) => {
return dispatch (
doCommentModToggleBlock ( true , commenterUri , creatorId , blockerId ? [ blockerId ] : [ ] , BLOCK _LEVEL . MODERATOR )
) ;
2021-03-03 19:50:16 +01:00
} ;
}
export function doFetchModBlockedList ( ) {
return async ( dispatch : Dispatch , getState : GetState ) => {
const state = getState ( ) ;
const myChannels = selectMyChannelClaims ( state ) ;
dispatch ( {
type : ACTIONS . COMMENT _MODERATION _BLOCK _LIST _STARTED ,
2021-02-11 06:12:41 +01:00
} ) ;
2021-03-03 19:50:16 +01:00
let channelSignatures = [ ] ;
2021-05-28 03:06:07 +02:00
return Promise . all ( myChannels . map ( ( channel ) => channelSignName ( channel . claim _id , channel . name ) ) )
. then ( ( response ) => {
channelSignatures = response ;
// $FlowFixMe
return Promise . allSettled (
channelSignatures
. filter ( ( x ) => x !== undefined && x !== null )
. map ( ( signatureData ) =>
Comments . moderation _block _list ( {
mod _channel _id : signatureData . claim _id ,
mod _channel _name : signatureData . name ,
signature : signatureData . signature ,
signing _ts : signatureData . signing _ts ,
} )
)
)
. then ( ( res ) => {
2021-05-25 08:17:36 +02:00
let personalBlockList = [ ] ;
let adminBlockList = [ ] ;
let moderatorBlockList = [ ] ;
let moderatorBlockListDelegatorsMap = { } ;
const blockListsPerChannel = res . map ( ( r ) => r . value ) ;
blockListsPerChannel
2021-05-28 03:06:07 +02:00
. sort ( ( a , b ) => {
return 1 ;
} )
2021-05-25 08:17:36 +02:00
. forEach ( ( channelBlockLists ) => {
const storeList = ( fetchedList , blockedList , blockedByMap ) => {
if ( fetchedList ) {
fetchedList . forEach ( ( blockedChannel ) => {
if ( blockedChannel . blocked _channel _name ) {
const channelUri = buildURI ( {
channelName : blockedChannel . blocked _channel _name ,
claimId : blockedChannel . blocked _channel _id ,
} ) ;
2021-07-15 22:22:44 +02:00
if ( ! blockedList . find ( ( blockedChannel ) => isURIEqual ( blockedChannel . channelUri , channelUri ) ) ) {
2021-05-25 08:17:36 +02:00
blockedList . push ( { channelUri , blockedAt : blockedChannel . blocked _at } ) ;
}
if ( blockedByMap !== undefined ) {
const blockedByChannelUri = buildURI ( {
channelName : blockedChannel . blocked _by _channel _name ,
claimId : blockedChannel . blocked _by _channel _id ,
} ) ;
if ( blockedByMap [ channelUri ] ) {
if ( ! blockedByMap [ channelUri ] . includes ( blockedByChannelUri ) ) {
blockedByMap [ channelUri ] . push ( blockedByChannelUri ) ;
}
} else {
blockedByMap [ channelUri ] = [ blockedByChannelUri ] ;
}
}
2021-05-28 03:06:07 +02:00
}
2021-05-25 08:17:36 +02:00
} ) ;
}
} ;
const blocked _channels = channelBlockLists && channelBlockLists . blocked _channels ;
const globally _blocked _channels = channelBlockLists && channelBlockLists . globally _blocked _channels ;
const delegated _blocked _channels = channelBlockLists && channelBlockLists . delegated _blocked _channels ;
storeList ( blocked _channels , personalBlockList ) ;
storeList ( globally _blocked _channels , adminBlockList ) ;
storeList ( delegated _blocked _channels , moderatorBlockList , moderatorBlockListDelegatorsMap ) ;
2021-03-03 19:50:16 +01:00
} ) ;
2021-05-28 03:06:07 +02:00
dispatch ( {
type : ACTIONS . COMMENT _MODERATION _BLOCK _LIST _COMPLETED ,
data : {
2021-05-25 08:17:36 +02:00
personalBlockList :
personalBlockList . length > 0
? personalBlockList
2021-05-28 03:06:07 +02:00
. sort ( ( a , b ) => new Date ( a . blockedAt ) - new Date ( b . blockedAt ) )
. map ( ( blockedChannel ) => blockedChannel . channelUri )
: null ,
2021-05-25 08:17:36 +02:00
adminBlockList :
adminBlockList . length > 0
? adminBlockList
. sort ( ( a , b ) => new Date ( a . blockedAt ) - new Date ( b . blockedAt ) )
. map ( ( blockedChannel ) => blockedChannel . channelUri )
: null ,
moderatorBlockList :
moderatorBlockList . length > 0
? moderatorBlockList
. sort ( ( a , b ) => new Date ( a . blockedAt ) - new Date ( b . blockedAt ) )
. map ( ( blockedChannel ) => blockedChannel . channelUri )
: null ,
moderatorBlockListDelegatorsMap : moderatorBlockListDelegatorsMap ,
2021-05-28 03:06:07 +02:00
} ,
} ) ;
} )
. catch ( ( ) => {
dispatch ( {
type : ACTIONS . COMMENT _MODERATION _BLOCK _LIST _FAILED ,
} ) ;
} ) ;
2021-03-03 19:50:16 +01:00
} )
. catch ( ( ) => {
dispatch ( {
type : ACTIONS . COMMENT _MODERATION _BLOCK _LIST _FAILED ,
} ) ;
} ) ;
2021-02-11 06:12:41 +01:00
} ;
}
2021-03-03 19:50:16 +01:00
export const doUpdateBlockListForPublishedChannel = ( channelClaim : ChannelClaim ) => {
return async ( dispatch : Dispatch , getState : GetState ) => {
const state = getState ( ) ;
const blockedUris = selectModerationBlockList ( state ) ;
let channelSignature : ? {
signature : string ,
signing _ts : string ,
} ;
try {
channelSignature = await Lbry . channel _sign ( {
channel _id : channelClaim . claim _id ,
hexdata : toHex ( channelClaim . name ) ,
} ) ;
} catch ( e ) { }
if ( ! channelSignature ) {
return ;
}
return Promise . all (
blockedUris . map ( ( uri ) => {
const { channelName , channelClaimId } = parseURI ( uri ) ;
return Comments . moderation _block ( {
mod _channel _id : channelClaim . claim _id ,
mod _channel _name : channelClaim . name ,
// $FlowFixMe
signature : channelSignature . signature ,
// $FlowFixMe
signing _ts : channelSignature . signing _ts ,
blocked _channel _id : channelClaimId ,
blocked _channel _name : channelName ,
} ) ;
} )
) ;
} ;
} ;
2021-04-20 10:40:53 +02:00
2021-05-25 08:17:36 +02:00
export function doCommentModAddDelegate (
modChannelId : string ,
modChannelName : string ,
2021-06-19 11:52:17 +02:00
creatorChannelClaim : ChannelClaim ,
showToast : boolean = false
2021-05-25 08:17:36 +02:00
) {
return async ( dispatch : Dispatch , getState : GetState ) => {
let signature : ? {
signature : string ,
signing _ts : string ,
} ;
try {
signature = await Lbry . channel _sign ( {
channel _id : creatorChannelClaim . claim _id ,
hexdata : toHex ( creatorChannelClaim . name ) ,
} ) ;
} catch ( e ) { }
if ( ! signature ) {
return ;
}
return Comments . moderation _add _delegate ( {
mod _channel _id : modChannelId ,
mod _channel _name : modChannelName ,
creator _channel _id : creatorChannelClaim . claim _id ,
creator _channel _name : creatorChannelClaim . name ,
signature : signature . signature ,
signing _ts : signature . signing _ts ,
2021-06-19 11:52:17 +02:00
} )
. then ( ( ) => {
if ( showToast ) {
dispatch (
doToast ( {
message : _ _ ( 'Added %user% as moderator for %myChannel%' , {
user : modChannelName ,
myChannel : creatorChannelClaim . name ,
} ) ,
linkText : _ _ ( 'Manage' ) ,
linkTarget : ` / ${ PAGES . SETTINGS _CREATOR } ` ,
} )
) ;
}
} )
. catch ( ( err ) => {
dispatch (
doToast ( {
message : err . message ,
isError : true ,
} )
) ;
} ) ;
2021-05-25 08:17:36 +02:00
} ;
}
export function doCommentModRemoveDelegate (
modChannelId : string ,
modChannelName : string ,
creatorChannelClaim : ChannelClaim
) {
return async ( dispatch : Dispatch , getState : GetState ) => {
let signature : ? {
signature : string ,
signing _ts : string ,
} ;
try {
signature = await Lbry . channel _sign ( {
channel _id : creatorChannelClaim . claim _id ,
hexdata : toHex ( creatorChannelClaim . name ) ,
} ) ;
} catch ( e ) { }
if ( ! signature ) {
return ;
}
return Comments . moderation _remove _delegate ( {
mod _channel _id : modChannelId ,
mod _channel _name : modChannelName ,
creator _channel _id : creatorChannelClaim . claim _id ,
creator _channel _name : creatorChannelClaim . name ,
signature : signature . signature ,
signing _ts : signature . signing _ts ,
} ) . catch ( ( err ) => {
dispatch (
doToast ( {
message : err . message ,
isError : true ,
} )
) ;
} ) ;
} ;
}
export function doCommentModListDelegates ( channelClaim : ChannelClaim ) {
return async ( dispatch : Dispatch , getState : GetState ) => {
dispatch ( {
type : ACTIONS . COMMENT _FETCH _MODERATION _DELEGATES _STARTED ,
} ) ;
let signature : ? {
signature : string ,
signing _ts : string ,
} ;
try {
signature = await Lbry . channel _sign ( {
channel _id : channelClaim . claim _id ,
hexdata : toHex ( channelClaim . name ) ,
} ) ;
} catch ( e ) { }
if ( ! signature ) {
dispatch ( {
type : ACTIONS . COMMENT _FETCH _MODERATION _DELEGATES _FAILED ,
} ) ;
return ;
}
return Comments . moderation _list _delegates ( {
creator _channel _id : channelClaim . claim _id ,
creator _channel _name : channelClaim . name ,
signature : signature . signature ,
signing _ts : signature . signing _ts ,
} )
. then ( ( response ) => {
dispatch ( {
type : ACTIONS . COMMENT _FETCH _MODERATION _DELEGATES _COMPLETED ,
data : {
id : channelClaim . claim _id ,
delegates : response . Delegates ,
} ,
} ) ;
} )
. catch ( ( err ) => {
dispatch ( {
type : ACTIONS . COMMENT _FETCH _MODERATION _DELEGATES _FAILED ,
} ) ;
} ) ;
} ;
}
export function doFetchCommentModAmIList ( channelClaim : ChannelClaim ) {
return async ( dispatch : Dispatch , getState : GetState ) => {
const state = getState ( ) ;
const myChannels = selectMyChannelClaims ( state ) ;
dispatch ( {
type : ACTIONS . COMMENT _MODERATION _AM _I _LIST _STARTED ,
} ) ;
let channelSignatures = [ ] ;
return Promise . all ( myChannels . map ( ( channel ) => channelSignName ( channel . claim _id , channel . name ) ) )
. then ( ( response ) => {
channelSignatures = response ;
// $FlowFixMe
return Promise . allSettled (
channelSignatures
. filter ( ( x ) => x !== undefined && x !== null )
. map ( ( signatureData ) =>
Comments . moderation _am _i ( {
channel _name : signatureData . name ,
channel _id : signatureData . claim _id ,
signature : signatureData . signature ,
signing _ts : signatureData . signing _ts ,
} )
)
)
. then ( ( res ) => {
const delegatorsById = { } ;
channelSignatures . forEach ( ( chanSig , index ) => {
if ( chanSig && res [ index ] ) {
const value = res [ index ] . value ;
delegatorsById [ chanSig . claim _id ] = {
global : value ? value . type === 'Global' : false ,
delegators : value && value . authorized _channels ? value . authorized _channels : { } ,
} ;
}
} ) ;
dispatch ( {
type : ACTIONS . COMMENT _MODERATION _AM _I _LIST _COMPLETED ,
data : delegatorsById ,
} ) ;
} )
. catch ( ( err ) => {
dispatch ( {
type : ACTIONS . COMMENT _MODERATION _AM _I _LIST _FAILED ,
} ) ;
} ) ;
} )
. catch ( ( ) => {
dispatch ( {
type : ACTIONS . COMMENT _MODERATION _AM _I _LIST _FAILED ,
} ) ;
} ) ;
} ;
}
2021-04-20 10:40:53 +02:00
export const doFetchCreatorSettings = ( channelClaimIds : Array < string > = [ ] ) => {
return async ( dispatch : Dispatch , getState : GetState ) => {
const state = getState ( ) ;
const myChannels = selectMyChannelClaims ( state ) ;
dispatch ( {
type : ACTIONS . COMMENT _FETCH _SETTINGS _STARTED ,
} ) ;
let channelSignatures = [ ] ;
if ( myChannels ) {
for ( const channelClaim of myChannels ) {
if ( channelClaimIds . length !== 0 && ! channelClaimIds . includes ( channelClaim . claim _id ) ) {
continue ;
}
try {
const channelSignature = await Lbry . channel _sign ( {
channel _id : channelClaim . claim _id ,
hexdata : toHex ( channelClaim . name ) ,
} ) ;
channelSignatures . push ( { ... channelSignature , claim _id : channelClaim . claim _id , name : channelClaim . name } ) ;
} catch ( e ) { }
}
}
return Promise . all (
channelSignatures . map ( ( signatureData ) =>
Comments . setting _list ( {
channel _name : signatureData . name ,
channel _id : signatureData . claim _id ,
signature : signatureData . signature ,
signing _ts : signatureData . signing _ts ,
} )
)
)
. then ( ( settings ) => {
const settingsByChannelId = { } ;
for ( let i = 0 ; i < channelSignatures . length ; ++ i ) {
const channelId = channelSignatures [ i ] . claim _id ;
settingsByChannelId [ channelId ] = settings [ i ] ;
2021-07-23 09:45:04 +02:00
if ( settings [ i ] . words ) {
settingsByChannelId [ channelId ] . words = settings [ i ] . words . split ( ',' ) ;
}
2021-04-20 10:40:53 +02:00
delete settingsByChannelId [ channelId ] . channel _name ;
delete settingsByChannelId [ channelId ] . channel _id ;
delete settingsByChannelId [ channelId ] . signature ;
delete settingsByChannelId [ channelId ] . signing _ts ;
}
dispatch ( {
type : ACTIONS . COMMENT _FETCH _SETTINGS _COMPLETED ,
data : settingsByChannelId ,
} ) ;
} )
2021-05-25 04:58:42 +02:00
. catch ( ( err ) => {
// TODO: Use error codes when available.
// TODO: The "validation is disallowed" thing ideally should just be a
// success case that returns a null setting, instead of an error.
// As we are using 'Promise.all', if one channel fails, everyone
// fails. This forces us to remove the batch functionality of this
// function. However, since this "validation is disallowed" thing
// is potentially a temporary one to handle spammers, I retained
// the batch functionality for now.
if ( err . message === 'validation is disallowed for non controlling channels' ) {
const settingsByChannelId = { } ;
for ( let i = 0 ; i < channelSignatures . length ; ++ i ) {
const channelId = channelSignatures [ i ] . claim _id ;
// 'undefined' means "fetching or have not fetched";
// 'null' means "feature not available for this channel";
settingsByChannelId [ channelId ] = null ;
}
dispatch ( {
type : ACTIONS . COMMENT _FETCH _SETTINGS _COMPLETED ,
data : settingsByChannelId ,
} ) ;
return ;
}
2021-04-20 10:40:53 +02:00
dispatch ( {
type : ACTIONS . COMMENT _FETCH _SETTINGS _FAILED ,
} ) ;
} ) ;
} ;
} ;
/ * *
* Updates creator settings , except for 'Words' , which will be handled by
* 'doCommentWords, doCommentBlockWords, etc.'
*
* @ param channelClaim
* @ param settings
* @ returns { function ( Dispatch , GetState ) : Promise < R > | Promise < unknown > | * }
* /
export const doUpdateCreatorSettings = ( channelClaim : ChannelClaim , settings : PerChannelSettings ) => {
return async ( dispatch : Dispatch , getState : GetState ) => {
let channelSignature : ? {
signature : string ,
signing _ts : string ,
} ;
try {
channelSignature = await Lbry . channel _sign ( {
channel _id : channelClaim . claim _id ,
hexdata : toHex ( channelClaim . name ) ,
} ) ;
} catch ( e ) { }
if ( ! channelSignature ) {
return ;
}
return Comments . setting _update ( {
channel _name : channelClaim . name ,
channel _id : channelClaim . claim _id ,
signature : channelSignature . signature ,
signing _ts : channelSignature . signing _ts ,
... settings ,
} ) . catch ( ( err ) => {
dispatch (
doToast ( {
message : err . message ,
isError : true ,
} )
) ;
} ) ;
} ;
} ;
export const doCommentWords = ( channelClaim : ChannelClaim , words : Array < string > , isUnblock : boolean ) => {
return async ( dispatch : Dispatch , getState : GetState ) => {
let channelSignature : ? {
signature : string ,
signing _ts : string ,
} ;
try {
channelSignature = await Lbry . channel _sign ( {
channel _id : channelClaim . claim _id ,
hexdata : toHex ( channelClaim . name ) ,
} ) ;
} catch ( e ) { }
if ( ! channelSignature ) {
return ;
}
const cmd = isUnblock ? Comments . setting _unblock _word : Comments . setting _block _word ;
return cmd ( {
channel _name : channelClaim . name ,
channel _id : channelClaim . claim _id ,
words : words . join ( ',' ) ,
signature : channelSignature . signature ,
signing _ts : channelSignature . signing _ts ,
} ) . catch ( ( err ) => {
dispatch (
doToast ( {
message : err . message ,
isError : true ,
} )
) ;
} ) ;
} ;
} ;
export const doCommentBlockWords = ( channelClaim : ChannelClaim , words : Array < string > ) => {
return ( dispatch : Dispatch ) => {
return dispatch ( doCommentWords ( channelClaim , words , false ) ) ;
} ;
} ;
export const doCommentUnblockWords = ( channelClaim : ChannelClaim , words : Array < string > ) => {
return ( dispatch : Dispatch ) => {
return dispatch ( doCommentWords ( channelClaim , words , true ) ) ;
} ;
} ;
export const doFetchBlockedWords = ( ) => {
return async ( dispatch : Dispatch , getState : GetState ) => {
const state = getState ( ) ;
const myChannels = selectMyChannelClaims ( state ) ;
dispatch ( {
type : ACTIONS . COMMENT _FETCH _BLOCKED _WORDS _STARTED ,
} ) ;
let channelSignatures = [ ] ;
if ( myChannels ) {
for ( const channelClaim of myChannels ) {
try {
const channelSignature = await Lbry . channel _sign ( {
channel _id : channelClaim . claim _id ,
hexdata : toHex ( channelClaim . name ) ,
} ) ;
channelSignatures . push ( { ... channelSignature , claim _id : channelClaim . claim _id , name : channelClaim . name } ) ;
} catch ( e ) { }
}
}
return Promise . all (
channelSignatures . map ( ( signatureData ) =>
Comments . setting _list _blocked _words ( {
channel _name : signatureData . name ,
channel _id : signatureData . claim _id ,
signature : signatureData . signature ,
signing _ts : signatureData . signing _ts ,
} )
)
)
. then ( ( blockedWords ) => {
const blockedWordsByChannelId = { } ;
for ( let i = 0 ; i < channelSignatures . length ; ++ i ) {
const claim _id = channelSignatures [ i ] . claim _id ;
blockedWordsByChannelId [ claim _id ] = blockedWords [ i ] . word _list ;
}
dispatch ( {
type : ACTIONS . COMMENT _FETCH _BLOCKED _WORDS _COMPLETED ,
data : blockedWordsByChannelId ,
} ) ;
} )
. catch ( ( ) => {
dispatch ( {
type : ACTIONS . COMMENT _FETCH _BLOCKED _WORDS _FAILED ,
} ) ;
} ) ;
} ;
} ;