Compare commits
20 commits
master
...
collection
Author | SHA1 | Date | |
---|---|---|---|
|
5cd9e7601c | ||
|
97cd2dbd6a | ||
|
6d160b3d14 | ||
|
e4fc01fc72 | ||
|
d71b9a351c | ||
|
dc19ecb77e | ||
|
dd95916281 | ||
|
9e802f47c4 | ||
|
ff16a0c8a4 | ||
|
6228a2dce1 | ||
|
09699be25c | ||
|
25acc1289c | ||
|
d2d9b037f0 | ||
|
bcd8fd1005 | ||
|
d4fba29fc5 | ||
|
79519a3ea1 | ||
|
15eebee694 | ||
|
a1dce5581c | ||
|
211266cdd8 | ||
|
f9f53af06e |
19 changed files with 3028 additions and 67 deletions
1300
dist/bundle.es.js
vendored
1300
dist/bundle.es.js
vendored
File diff suppressed because it is too large
Load diff
80
dist/flow-typed/Claim.js
vendored
80
dist/flow-typed/Claim.js
vendored
|
@ -1,11 +1,15 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
declare type Claim = StreamClaim | ChannelClaim;
|
declare type Claim = StreamClaim | ChannelClaim | CollectionClaim;
|
||||||
|
|
||||||
declare type ChannelClaim = GenericClaim & {
|
declare type ChannelClaim = GenericClaim & {
|
||||||
value: ChannelMetadata,
|
value: ChannelMetadata,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
declare type CollectionClaim = GenericClaim & {
|
||||||
|
value: CollectionMetadata,
|
||||||
|
};
|
||||||
|
|
||||||
declare type StreamClaim = GenericClaim & {
|
declare type StreamClaim = GenericClaim & {
|
||||||
value: StreamMetadata,
|
value: StreamMetadata,
|
||||||
};
|
};
|
||||||
|
@ -30,7 +34,7 @@ declare type GenericClaim = {
|
||||||
short_url: string, // permanent_url with short id, no channel
|
short_url: string, // permanent_url with short id, no channel
|
||||||
txid: string, // unique tx id
|
txid: string, // unique tx id
|
||||||
type: 'claim' | 'update' | 'support',
|
type: 'claim' | 'update' | 'support',
|
||||||
value_type: 'stream' | 'channel',
|
value_type: 'stream' | 'channel' | 'collection',
|
||||||
signing_channel?: ChannelClaim,
|
signing_channel?: ChannelClaim,
|
||||||
reposted_claim?: GenericClaim,
|
reposted_claim?: GenericClaim,
|
||||||
repost_channel_url?: string,
|
repost_channel_url?: string,
|
||||||
|
@ -74,6 +78,10 @@ declare type ChannelMetadata = GenericMetadata & {
|
||||||
featured?: Array<string>,
|
featured?: Array<string>,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
declare type CollectionMetadata = GenericMetadata & {
|
||||||
|
claims: Array<string>,
|
||||||
|
}
|
||||||
|
|
||||||
declare type StreamMetadata = GenericMetadata & {
|
declare type StreamMetadata = GenericMetadata & {
|
||||||
license?: string, // License "title" ex: Creative Commons, Custom copyright
|
license?: string, // License "title" ex: Creative Commons, Custom copyright
|
||||||
license_url?: string, // Link to full license
|
license_url?: string, // Link to full license
|
||||||
|
@ -136,3 +144,71 @@ declare type PurchaseReceipt = {
|
||||||
txid: string,
|
txid: string,
|
||||||
type: 'purchase',
|
type: 'purchase',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
declare type ClaimActionResolveInfo = {
|
||||||
|
[string]: {
|
||||||
|
stream: ?StreamClaim,
|
||||||
|
channel: ?ChannelClaim,
|
||||||
|
claimsInChannel: ?number,
|
||||||
|
collection: ?CollectionClaim,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type ChannelUpdateParams = {
|
||||||
|
claim_id: string,
|
||||||
|
bid?: string,
|
||||||
|
title?: string,
|
||||||
|
cover_url?: string,
|
||||||
|
thumbnail_url?: string,
|
||||||
|
description?: string,
|
||||||
|
website_url?: string,
|
||||||
|
email?: string,
|
||||||
|
tags?: Array<string>,
|
||||||
|
replace?: boolean,
|
||||||
|
languages?: Array<string>,
|
||||||
|
locations?: Array<string>,
|
||||||
|
blocking?: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type ChannelPublishParams = {
|
||||||
|
name: string,
|
||||||
|
bid: string,
|
||||||
|
blocking?: true,
|
||||||
|
title?: string,
|
||||||
|
cover_url?: string,
|
||||||
|
thumbnail_url?: string,
|
||||||
|
description?: string,
|
||||||
|
website_url?: string,
|
||||||
|
email?: string,
|
||||||
|
tags?: Array<string>,
|
||||||
|
languages?: Array<string>,
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type CollectionUpdateParams = {
|
||||||
|
claim_id: string,
|
||||||
|
claim_ids?: Array<string>,
|
||||||
|
bid?: string,
|
||||||
|
title?: string,
|
||||||
|
cover_url?: string,
|
||||||
|
thumbnail_url?: string,
|
||||||
|
description?: string,
|
||||||
|
website_url?: string,
|
||||||
|
email?: string,
|
||||||
|
tags?: Array<string>,
|
||||||
|
replace?: boolean,
|
||||||
|
languages?: Array<string>,
|
||||||
|
locations?: Array<string>,
|
||||||
|
blocking?: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type CollectionPublishParams = {
|
||||||
|
name: string,
|
||||||
|
bid: string,
|
||||||
|
claim_ids: Array<string>,
|
||||||
|
blocking?: true,
|
||||||
|
title?: string,
|
||||||
|
thumbnail_url?: string,
|
||||||
|
description?: string,
|
||||||
|
tags?: Array<string>,
|
||||||
|
languages?: Array<string>,
|
||||||
|
}
|
||||||
|
|
34
dist/flow-typed/Collections.js
vendored
Normal file
34
dist/flow-typed/Collections.js
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
declare type Collection = {
|
||||||
|
id: string,
|
||||||
|
items: Array<?string>,
|
||||||
|
name: string,
|
||||||
|
type: string,
|
||||||
|
updatedAt: number,
|
||||||
|
totalItems?: number,
|
||||||
|
sourceId?: string, // if copied, claimId of original collection
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CollectionState = {
|
||||||
|
unpublished: CollectionGroup,
|
||||||
|
resolved: CollectionGroup,
|
||||||
|
pending: CollectionGroup,
|
||||||
|
edited: CollectionGroup,
|
||||||
|
builtin: CollectionGroup,
|
||||||
|
saved: Array<string>,
|
||||||
|
isResolvingCollectionById: { [string]: boolean },
|
||||||
|
error?: string | null,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CollectionGroup = {
|
||||||
|
[string]: Collection,
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type CollectionEditParams = {
|
||||||
|
claims?: Array<Claim>,
|
||||||
|
remove?: boolean,
|
||||||
|
claimIds?: Array<string>,
|
||||||
|
replace?: boolean,
|
||||||
|
order?: { from: number, to: number },
|
||||||
|
type?: string,
|
||||||
|
name?: string,
|
||||||
|
}
|
35
dist/flow-typed/Lbry.js
vendored
35
dist/flow-typed/Lbry.js
vendored
|
@ -170,6 +170,37 @@ declare type ChannelSignResponse = {
|
||||||
signing_ts: string,
|
signing_ts: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
declare type CollectionCreateResponse = {
|
||||||
|
outputs: Array<Claim>,
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
total_items: number,
|
||||||
|
total_pages: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type CollectionListResponse = {
|
||||||
|
items: Array<Claim>,
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
total_items: number,
|
||||||
|
total_pages: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CollectionResolveResponse = {
|
||||||
|
items: Array<Claim>,
|
||||||
|
total_items: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CollectionResolveOptions = {
|
||||||
|
claim_id: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CollectionListOptions = {
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
resolve?: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
declare type FileListResponse = {
|
declare type FileListResponse = {
|
||||||
items: Array<FileListItem>,
|
items: Array<FileListItem>,
|
||||||
page: number,
|
page: number,
|
||||||
|
@ -288,6 +319,10 @@ declare type LbryTypes = {
|
||||||
support_abandon: (params: {}) => Promise<SupportAbandonResponse>,
|
support_abandon: (params: {}) => Promise<SupportAbandonResponse>,
|
||||||
stream_repost: (params: StreamRepostOptions) => Promise<StreamRepostResponse>,
|
stream_repost: (params: StreamRepostOptions) => Promise<StreamRepostResponse>,
|
||||||
purchase_list: (params: PurchaseListOptions) => Promise<PurchaseListResponse>,
|
purchase_list: (params: PurchaseListOptions) => Promise<PurchaseListResponse>,
|
||||||
|
collection_resolve: (params: CollectionResolveOptions) => Promise<CollectionResolveResponse>,
|
||||||
|
collection_list: (params: CollectionListOptions) => Promise<CollectionListResponse>,
|
||||||
|
collection_create: (params: {}) => Promise<CollectionCreateResponse>,
|
||||||
|
collection_update: (params: {}) => Promise<CollectionCreateResponse>,
|
||||||
|
|
||||||
// File fetching and manipulation
|
// File fetching and manipulation
|
||||||
file_list: (params: {}) => Promise<FileListResponse>,
|
file_list: (params: {}) => Promise<FileListResponse>,
|
||||||
|
|
80
flow-typed/Claim.js
vendored
80
flow-typed/Claim.js
vendored
|
@ -1,11 +1,15 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
declare type Claim = StreamClaim | ChannelClaim;
|
declare type Claim = StreamClaim | ChannelClaim | CollectionClaim;
|
||||||
|
|
||||||
declare type ChannelClaim = GenericClaim & {
|
declare type ChannelClaim = GenericClaim & {
|
||||||
value: ChannelMetadata,
|
value: ChannelMetadata,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
declare type CollectionClaim = GenericClaim & {
|
||||||
|
value: CollectionMetadata,
|
||||||
|
};
|
||||||
|
|
||||||
declare type StreamClaim = GenericClaim & {
|
declare type StreamClaim = GenericClaim & {
|
||||||
value: StreamMetadata,
|
value: StreamMetadata,
|
||||||
};
|
};
|
||||||
|
@ -30,7 +34,7 @@ declare type GenericClaim = {
|
||||||
short_url: string, // permanent_url with short id, no channel
|
short_url: string, // permanent_url with short id, no channel
|
||||||
txid: string, // unique tx id
|
txid: string, // unique tx id
|
||||||
type: 'claim' | 'update' | 'support',
|
type: 'claim' | 'update' | 'support',
|
||||||
value_type: 'stream' | 'channel',
|
value_type: 'stream' | 'channel' | 'collection',
|
||||||
signing_channel?: ChannelClaim,
|
signing_channel?: ChannelClaim,
|
||||||
reposted_claim?: GenericClaim,
|
reposted_claim?: GenericClaim,
|
||||||
repost_channel_url?: string,
|
repost_channel_url?: string,
|
||||||
|
@ -74,6 +78,10 @@ declare type ChannelMetadata = GenericMetadata & {
|
||||||
featured?: Array<string>,
|
featured?: Array<string>,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
declare type CollectionMetadata = GenericMetadata & {
|
||||||
|
claims: Array<string>,
|
||||||
|
}
|
||||||
|
|
||||||
declare type StreamMetadata = GenericMetadata & {
|
declare type StreamMetadata = GenericMetadata & {
|
||||||
license?: string, // License "title" ex: Creative Commons, Custom copyright
|
license?: string, // License "title" ex: Creative Commons, Custom copyright
|
||||||
license_url?: string, // Link to full license
|
license_url?: string, // Link to full license
|
||||||
|
@ -136,3 +144,71 @@ declare type PurchaseReceipt = {
|
||||||
txid: string,
|
txid: string,
|
||||||
type: 'purchase',
|
type: 'purchase',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
declare type ClaimActionResolveInfo = {
|
||||||
|
[string]: {
|
||||||
|
stream: ?StreamClaim,
|
||||||
|
channel: ?ChannelClaim,
|
||||||
|
claimsInChannel: ?number,
|
||||||
|
collection: ?CollectionClaim,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type ChannelUpdateParams = {
|
||||||
|
claim_id: string,
|
||||||
|
bid?: string,
|
||||||
|
title?: string,
|
||||||
|
cover_url?: string,
|
||||||
|
thumbnail_url?: string,
|
||||||
|
description?: string,
|
||||||
|
website_url?: string,
|
||||||
|
email?: string,
|
||||||
|
tags?: Array<string>,
|
||||||
|
replace?: boolean,
|
||||||
|
languages?: Array<string>,
|
||||||
|
locations?: Array<string>,
|
||||||
|
blocking?: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type ChannelPublishParams = {
|
||||||
|
name: string,
|
||||||
|
bid: string,
|
||||||
|
blocking?: true,
|
||||||
|
title?: string,
|
||||||
|
cover_url?: string,
|
||||||
|
thumbnail_url?: string,
|
||||||
|
description?: string,
|
||||||
|
website_url?: string,
|
||||||
|
email?: string,
|
||||||
|
tags?: Array<string>,
|
||||||
|
languages?: Array<string>,
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type CollectionUpdateParams = {
|
||||||
|
claim_id: string,
|
||||||
|
claim_ids?: Array<string>,
|
||||||
|
bid?: string,
|
||||||
|
title?: string,
|
||||||
|
cover_url?: string,
|
||||||
|
thumbnail_url?: string,
|
||||||
|
description?: string,
|
||||||
|
website_url?: string,
|
||||||
|
email?: string,
|
||||||
|
tags?: Array<string>,
|
||||||
|
replace?: boolean,
|
||||||
|
languages?: Array<string>,
|
||||||
|
locations?: Array<string>,
|
||||||
|
blocking?: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type CollectionPublishParams = {
|
||||||
|
name: string,
|
||||||
|
bid: string,
|
||||||
|
claim_ids: Array<string>,
|
||||||
|
blocking?: true,
|
||||||
|
title?: string,
|
||||||
|
thumbnail_url?: string,
|
||||||
|
description?: string,
|
||||||
|
tags?: Array<string>,
|
||||||
|
languages?: Array<string>,
|
||||||
|
}
|
||||||
|
|
34
flow-typed/Collections.js
vendored
Normal file
34
flow-typed/Collections.js
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
declare type Collection = {
|
||||||
|
id: string,
|
||||||
|
items: Array<?string>,
|
||||||
|
name: string,
|
||||||
|
type: string,
|
||||||
|
updatedAt: number,
|
||||||
|
totalItems?: number,
|
||||||
|
sourceId?: string, // if copied, claimId of original collection
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CollectionState = {
|
||||||
|
unpublished: CollectionGroup,
|
||||||
|
resolved: CollectionGroup,
|
||||||
|
pending: CollectionGroup,
|
||||||
|
edited: CollectionGroup,
|
||||||
|
builtin: CollectionGroup,
|
||||||
|
saved: Array<string>,
|
||||||
|
isResolvingCollectionById: { [string]: boolean },
|
||||||
|
error?: string | null,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CollectionGroup = {
|
||||||
|
[string]: Collection,
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type CollectionEditParams = {
|
||||||
|
claims?: Array<Claim>,
|
||||||
|
remove?: boolean,
|
||||||
|
claimIds?: Array<string>,
|
||||||
|
replace?: boolean,
|
||||||
|
order?: { from: number, to: number },
|
||||||
|
type?: string,
|
||||||
|
name?: string,
|
||||||
|
}
|
35
flow-typed/Lbry.js
vendored
35
flow-typed/Lbry.js
vendored
|
@ -170,6 +170,37 @@ declare type ChannelSignResponse = {
|
||||||
signing_ts: string,
|
signing_ts: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
declare type CollectionCreateResponse = {
|
||||||
|
outputs: Array<Claim>,
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
total_items: number,
|
||||||
|
total_pages: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type CollectionListResponse = {
|
||||||
|
items: Array<Claim>,
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
total_items: number,
|
||||||
|
total_pages: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CollectionResolveResponse = {
|
||||||
|
items: Array<Claim>,
|
||||||
|
total_items: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CollectionResolveOptions = {
|
||||||
|
claim_id: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type CollectionListOptions = {
|
||||||
|
page: number,
|
||||||
|
page_size: number,
|
||||||
|
resolve?: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
declare type FileListResponse = {
|
declare type FileListResponse = {
|
||||||
items: Array<FileListItem>,
|
items: Array<FileListItem>,
|
||||||
page: number,
|
page: number,
|
||||||
|
@ -288,6 +319,10 @@ declare type LbryTypes = {
|
||||||
support_abandon: (params: {}) => Promise<SupportAbandonResponse>,
|
support_abandon: (params: {}) => Promise<SupportAbandonResponse>,
|
||||||
stream_repost: (params: StreamRepostOptions) => Promise<StreamRepostResponse>,
|
stream_repost: (params: StreamRepostOptions) => Promise<StreamRepostResponse>,
|
||||||
purchase_list: (params: PurchaseListOptions) => Promise<PurchaseListResponse>,
|
purchase_list: (params: PurchaseListOptions) => Promise<PurchaseListResponse>,
|
||||||
|
collection_resolve: (params: CollectionResolveOptions) => Promise<CollectionResolveResponse>,
|
||||||
|
collection_list: (params: CollectionListOptions) => Promise<CollectionListResponse>,
|
||||||
|
collection_create: (params: {}) => Promise<CollectionCreateResponse>,
|
||||||
|
collection_update: (params: {}) => Promise<CollectionCreateResponse>,
|
||||||
|
|
||||||
// File fetching and manipulation
|
// File fetching and manipulation
|
||||||
file_list: (params: {}) => Promise<FileListResponse>,
|
file_list: (params: {}) => Promise<FileListResponse>,
|
||||||
|
|
|
@ -102,6 +102,9 @@ export const ABANDON_CLAIM_SUCCEEDED = 'ABANDON_CLAIM_SUCCEEDED';
|
||||||
export const FETCH_CHANNEL_LIST_STARTED = 'FETCH_CHANNEL_LIST_STARTED';
|
export const FETCH_CHANNEL_LIST_STARTED = 'FETCH_CHANNEL_LIST_STARTED';
|
||||||
export const FETCH_CHANNEL_LIST_COMPLETED = 'FETCH_CHANNEL_LIST_COMPLETED';
|
export const FETCH_CHANNEL_LIST_COMPLETED = 'FETCH_CHANNEL_LIST_COMPLETED';
|
||||||
export const FETCH_CHANNEL_LIST_FAILED = 'FETCH_CHANNEL_LIST_FAILED';
|
export const FETCH_CHANNEL_LIST_FAILED = 'FETCH_CHANNEL_LIST_FAILED';
|
||||||
|
export const FETCH_COLLECTION_LIST_STARTED = 'FETCH_COLLECTION_LIST_STARTED';
|
||||||
|
export const FETCH_COLLECTION_LIST_COMPLETED = 'FETCH_COLLECTION_LIST_COMPLETED';
|
||||||
|
export const FETCH_COLLECTION_LIST_FAILED = 'FETCH_COLLECTION_LIST_FAILED';
|
||||||
export const CREATE_CHANNEL_STARTED = 'CREATE_CHANNEL_STARTED';
|
export const CREATE_CHANNEL_STARTED = 'CREATE_CHANNEL_STARTED';
|
||||||
export const CREATE_CHANNEL_COMPLETED = 'CREATE_CHANNEL_COMPLETED';
|
export const CREATE_CHANNEL_COMPLETED = 'CREATE_CHANNEL_COMPLETED';
|
||||||
export const CREATE_CHANNEL_FAILED = 'CREATE_CHANNEL_FAILED';
|
export const CREATE_CHANNEL_FAILED = 'CREATE_CHANNEL_FAILED';
|
||||||
|
@ -111,6 +114,7 @@ export const UPDATE_CHANNEL_FAILED = 'UPDATE_CHANNEL_FAILED';
|
||||||
export const IMPORT_CHANNEL_STARTED = 'IMPORT_CHANNEL_STARTED';
|
export const IMPORT_CHANNEL_STARTED = 'IMPORT_CHANNEL_STARTED';
|
||||||
export const IMPORT_CHANNEL_COMPLETED = 'IMPORT_CHANNEL_COMPLETED';
|
export const IMPORT_CHANNEL_COMPLETED = 'IMPORT_CHANNEL_COMPLETED';
|
||||||
export const IMPORT_CHANNEL_FAILED = 'IMPORT_CHANNEL_FAILED';
|
export const IMPORT_CHANNEL_FAILED = 'IMPORT_CHANNEL_FAILED';
|
||||||
|
export const CLEAR_CHANNEL_ERRORS = 'CLEAR_CHANNEL_ERRORS';
|
||||||
export const PUBLISH_STARTED = 'PUBLISH_STARTED';
|
export const PUBLISH_STARTED = 'PUBLISH_STARTED';
|
||||||
export const PUBLISH_COMPLETED = 'PUBLISH_COMPLETED';
|
export const PUBLISH_COMPLETED = 'PUBLISH_COMPLETED';
|
||||||
export const PUBLISH_FAILED = 'PUBLISH_FAILED';
|
export const PUBLISH_FAILED = 'PUBLISH_FAILED';
|
||||||
|
@ -129,7 +133,6 @@ export const CLAIM_REPOST_STARTED = 'CLAIM_REPOST_STARTED';
|
||||||
export const CLAIM_REPOST_COMPLETED = 'CLAIM_REPOST_COMPLETED';
|
export const CLAIM_REPOST_COMPLETED = 'CLAIM_REPOST_COMPLETED';
|
||||||
export const CLAIM_REPOST_FAILED = 'CLAIM_REPOST_FAILED';
|
export const CLAIM_REPOST_FAILED = 'CLAIM_REPOST_FAILED';
|
||||||
export const CLEAR_REPOST_ERROR = 'CLEAR_REPOST_ERROR';
|
export const CLEAR_REPOST_ERROR = 'CLEAR_REPOST_ERROR';
|
||||||
export const CLEAR_CHANNEL_ERRORS = 'CLEAR_CHANNEL_ERRORS';
|
|
||||||
export const CHECK_PUBLISH_NAME_STARTED = 'CHECK_PUBLISH_NAME_STARTED';
|
export const CHECK_PUBLISH_NAME_STARTED = 'CHECK_PUBLISH_NAME_STARTED';
|
||||||
export const CHECK_PUBLISH_NAME_COMPLETED = 'CHECK_PUBLISH_NAME_COMPLETED';
|
export const CHECK_PUBLISH_NAME_COMPLETED = 'CHECK_PUBLISH_NAME_COMPLETED';
|
||||||
export const UPDATE_PENDING_CLAIMS = 'UPDATE_PENDING_CLAIMS';
|
export const UPDATE_PENDING_CLAIMS = 'UPDATE_PENDING_CLAIMS';
|
||||||
|
@ -142,6 +145,27 @@ export const PURCHASE_LIST_STARTED = 'PURCHASE_LIST_STARTED';
|
||||||
export const PURCHASE_LIST_COMPLETED = 'PURCHASE_LIST_COMPLETED';
|
export const PURCHASE_LIST_COMPLETED = 'PURCHASE_LIST_COMPLETED';
|
||||||
export const PURCHASE_LIST_FAILED = 'PURCHASE_LIST_FAILED';
|
export const PURCHASE_LIST_FAILED = 'PURCHASE_LIST_FAILED';
|
||||||
|
|
||||||
|
export const COLLECTION_PUBLISH_STARTED = 'COLLECTION_PUBLISH_STARTED';
|
||||||
|
export const COLLECTION_PUBLISH_COMPLETED = 'COLLECTION_PUBLISH_COMPLETED';
|
||||||
|
export const COLLECTION_PUBLISH_FAILED = 'COLLECTION_PUBLISH_FAILED';
|
||||||
|
export const COLLECTION_PUBLISH_UPDATE_STARTED = 'COLLECTION_PUBLISH_UPDATE_STARTED';
|
||||||
|
export const COLLECTION_PUBLISH_UPDATE_COMPLETED = 'COLLECTION_PUBLISH_UPDATE_COMPLETED';
|
||||||
|
export const COLLECTION_PUBLISH_UPDATE_FAILED = 'COLLECTION_PUBLISH_UPDATE_FAILED';
|
||||||
|
export const COLLECTION_PUBLISH_ABANDON_STARTED = 'COLLECTION_PUBLISH_ABANDON_STARTED';
|
||||||
|
export const COLLECTION_PUBLISH_ABANDON_COMPLETED = 'COLLECTION_PUBLISH_ABANDON_COMPLETED';
|
||||||
|
export const COLLECTION_PUBLISH_ABANDON_FAILED = 'COLLECTION_PUBLISH_ABANDON_FAILED';
|
||||||
|
export const CLEAR_COLLECTION_ERRORS = 'CLEAR_COLLECTION_ERRORS';
|
||||||
|
export const COLLECTION_ITEMS_RESOLVE_STARTED = 'COLLECTION_ITEMS_RESOLVE_STARTED';
|
||||||
|
export const COLLECTION_ITEMS_RESOLVE_COMPLETED = 'COLLECTION_ITEMS_RESOLVE_COMPLETED';
|
||||||
|
export const COLLECTION_ITEMS_RESOLVE_FAILED = 'COLLECTION_ITEMS_RESOLVE_FAILED';
|
||||||
|
export const COLLECTION_NEW = 'COLLECTION_NEW';
|
||||||
|
export const COLLECTION_DELETE = 'COLLECTION_DELETE';
|
||||||
|
export const COLLECTION_PENDING = 'COLLECTION_PENDING';
|
||||||
|
export const COLLECTION_EDIT = 'COLLECTION_EDIT';
|
||||||
|
export const COLLECTION_COPY = 'COLLECTION_COPY';
|
||||||
|
export const COLLECTION_SAVE = 'COLLECTION_SAVE';
|
||||||
|
export const COLLECTION_ERROR = 'COLLECTION_ERROR';
|
||||||
|
|
||||||
// Comments
|
// Comments
|
||||||
export const COMMENT_LIST_STARTED = 'COMMENT_LIST_STARTED';
|
export const COMMENT_LIST_STARTED = 'COMMENT_LIST_STARTED';
|
||||||
export const COMMENT_LIST_COMPLETED = 'COMMENT_LIST_COMPLETED';
|
export const COMMENT_LIST_COMPLETED = 'COMMENT_LIST_COMPLETED';
|
||||||
|
|
10
src/constants/collections.js
Normal file
10
src/constants/collections.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
export const COLLECTION_ID = 'lid';
|
||||||
|
export const COLLECTION_INDEX = 'linx';
|
||||||
|
|
||||||
|
export const COL_TYPE_PLAYLIST = 'playlist';
|
||||||
|
export const COL_TYPE_CHANNELS = 'channelList';
|
||||||
|
|
||||||
|
export const WATCH_LATER_ID = 'watchlater';
|
||||||
|
export const FAVORITES_ID = 'favorites';
|
||||||
|
export const FAVORITE_CHANNELS_ID = 'favoriteChannels';
|
||||||
|
export const BUILTIN_LISTS = [WATCH_LATER_ID, FAVORITES_ID, FAVORITE_CHANNELS_ID];
|
45
src/index.js
45
src/index.js
|
@ -12,6 +12,7 @@ import * as TXO_LIST from 'constants/txo_list';
|
||||||
import * as SPEECH_URLS from 'constants/speech_urls';
|
import * as SPEECH_URLS from 'constants/speech_urls';
|
||||||
import * as DAEMON_SETTINGS from 'constants/daemon_settings';
|
import * as DAEMON_SETTINGS from 'constants/daemon_settings';
|
||||||
import * as SHARED_PREFERENCES from 'constants/shared_preferences';
|
import * as SHARED_PREFERENCES from 'constants/shared_preferences';
|
||||||
|
import * as COLLECTIONS_CONSTS from 'constants/collections';
|
||||||
import { DEFAULT_KNOWN_TAGS, DEFAULT_FOLLOWED_TAGS, MATURE_TAGS } from 'constants/tags';
|
import { DEFAULT_KNOWN_TAGS, DEFAULT_FOLLOWED_TAGS, MATURE_TAGS } from 'constants/tags';
|
||||||
import Lbry, { apiCall } from 'lbry';
|
import Lbry, { apiCall } from 'lbry';
|
||||||
import LbryFirst from 'lbry-first';
|
import LbryFirst from 'lbry-first';
|
||||||
|
@ -35,6 +36,7 @@ export {
|
||||||
MATURE_TAGS,
|
MATURE_TAGS,
|
||||||
SPEECH_URLS,
|
SPEECH_URLS,
|
||||||
SHARED_PREFERENCES,
|
SHARED_PREFERENCES,
|
||||||
|
COLLECTIONS_CONSTS,
|
||||||
};
|
};
|
||||||
|
|
||||||
// common
|
// common
|
||||||
|
@ -57,6 +59,13 @@ export { buildSharedStateMiddleware } from 'redux/middleware/shared-state';
|
||||||
|
|
||||||
// actions
|
// actions
|
||||||
export { doToast, doDismissToast, doError, doDismissError } from 'redux/actions/notifications';
|
export { doToast, doDismissToast, doError, doDismissError } from 'redux/actions/notifications';
|
||||||
|
export {
|
||||||
|
doLocalCollectionCreate,
|
||||||
|
doFetchItemsInCollection,
|
||||||
|
doFetchItemsInCollections,
|
||||||
|
doCollectionEdit,
|
||||||
|
doCollectionDelete,
|
||||||
|
} from 'redux/actions/collections';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
doFetchClaimsByChannel,
|
doFetchClaimsByChannel,
|
||||||
|
@ -66,6 +75,7 @@ export {
|
||||||
doResolveUris,
|
doResolveUris,
|
||||||
doResolveUri,
|
doResolveUri,
|
||||||
doFetchChannelListMine,
|
doFetchChannelListMine,
|
||||||
|
doFetchCollectionListMine,
|
||||||
doCreateChannel,
|
doCreateChannel,
|
||||||
doUpdateChannel,
|
doUpdateChannel,
|
||||||
doClaimSearch,
|
doClaimSearch,
|
||||||
|
@ -76,6 +86,8 @@ export {
|
||||||
doCheckPublishNameAvailability,
|
doCheckPublishNameAvailability,
|
||||||
doPurchaseList,
|
doPurchaseList,
|
||||||
doCheckPendingClaims,
|
doCheckPendingClaims,
|
||||||
|
doCollectionPublish,
|
||||||
|
doCollectionPublishUpdate,
|
||||||
} from 'redux/actions/claims';
|
} from 'redux/actions/claims';
|
||||||
|
|
||||||
export { doClearPurchasedUriSuccess, doPurchaseUri, doFileGet } from 'redux/actions/file';
|
export { doClearPurchasedUriSuccess, doPurchaseUri, doFileGet } from 'redux/actions/file';
|
||||||
|
@ -140,11 +152,37 @@ export { fileInfoReducer } from 'redux/reducers/file_info';
|
||||||
export { notificationsReducer } from 'redux/reducers/notifications';
|
export { notificationsReducer } from 'redux/reducers/notifications';
|
||||||
export { publishReducer } from 'redux/reducers/publish';
|
export { publishReducer } from 'redux/reducers/publish';
|
||||||
export { walletReducer } from 'redux/reducers/wallet';
|
export { walletReducer } from 'redux/reducers/wallet';
|
||||||
|
export { collectionsReducer } from 'redux/reducers/collections';
|
||||||
|
|
||||||
// selectors
|
// selectors
|
||||||
export { makeSelectContentPositionForUri } from 'redux/selectors/content';
|
export { makeSelectContentPositionForUri } from 'redux/selectors/content';
|
||||||
|
|
||||||
export { selectToast, selectError } from 'redux/selectors/notifications';
|
export { selectToast, selectError } from 'redux/selectors/notifications';
|
||||||
|
export {
|
||||||
|
selectSavedCollectionIds,
|
||||||
|
selectBuiltinCollections,
|
||||||
|
selectResolvedCollections,
|
||||||
|
selectMyUnpublishedCollections,
|
||||||
|
selectMyEditedCollections,
|
||||||
|
selectMyPublishedCollections,
|
||||||
|
selectMyPublishedMixedCollections,
|
||||||
|
selectMyPublishedPlaylistCollections,
|
||||||
|
makeSelectEditedCollectionForId,
|
||||||
|
makeSelectPendingCollectionForId,
|
||||||
|
makeSelectPublishedCollectionForId,
|
||||||
|
makeSelectCollectionIsMine,
|
||||||
|
makeSelectMyPublishedCollectionForId,
|
||||||
|
makeSelectUnpublishedCollectionForId,
|
||||||
|
makeSelectCollectionForId,
|
||||||
|
makeSelectUrlsForCollectionId,
|
||||||
|
makeSelectClaimIdsForCollectionId,
|
||||||
|
makeSelectNameForCollectionId,
|
||||||
|
makeSelectCountForCollectionId,
|
||||||
|
makeSelectIsResolvingCollectionForId,
|
||||||
|
makeSelectIndexForUrlInCollection,
|
||||||
|
makeSelectNextUrlForCollectionAndUrl,
|
||||||
|
makeSelectCollectionForIdHasClaimUrl,
|
||||||
|
} from 'redux/selectors/collections';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
makeSelectClaimForUri,
|
makeSelectClaimForUri,
|
||||||
|
@ -209,6 +247,8 @@ export {
|
||||||
selectAllMyClaimsByOutpoint,
|
selectAllMyClaimsByOutpoint,
|
||||||
selectMyClaimsOutpoints,
|
selectMyClaimsOutpoints,
|
||||||
selectFetchingMyChannels,
|
selectFetchingMyChannels,
|
||||||
|
selectFetchingMyCollections,
|
||||||
|
selectMyCollectionIds,
|
||||||
selectMyChannelClaims,
|
selectMyChannelClaims,
|
||||||
selectResolvingUris,
|
selectResolvingUris,
|
||||||
selectPlayingUri,
|
selectPlayingUri,
|
||||||
|
@ -237,6 +277,11 @@ export {
|
||||||
selectFetchingMyPurchasesError,
|
selectFetchingMyPurchasesError,
|
||||||
selectMyPurchasesCount,
|
selectMyPurchasesCount,
|
||||||
selectPurchaseUriSuccess,
|
selectPurchaseUriSuccess,
|
||||||
|
makeSelectClaimIdForUri,
|
||||||
|
selectUpdatingCollection,
|
||||||
|
selectUpdateCollectionError,
|
||||||
|
selectCreatingCollection,
|
||||||
|
selectCreateCollectionError,
|
||||||
} from 'redux/selectors/claims';
|
} from 'redux/selectors/claims';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|
|
@ -90,6 +90,10 @@ const Lbry: LbryTypes = {
|
||||||
support_create: params => daemonCallWithResult('support_create', params),
|
support_create: params => daemonCallWithResult('support_create', params),
|
||||||
support_list: params => daemonCallWithResult('support_list', params),
|
support_list: params => daemonCallWithResult('support_list', params),
|
||||||
stream_repost: params => daemonCallWithResult('stream_repost', params),
|
stream_repost: params => daemonCallWithResult('stream_repost', params),
|
||||||
|
collection_resolve: params => daemonCallWithResult('collection_resolve', params),
|
||||||
|
collection_list: params => daemonCallWithResult('collection_list', params),
|
||||||
|
collection_create: params => daemonCallWithResult('collection_create', params),
|
||||||
|
collection_update: params => daemonCallWithResult('collection_update', params),
|
||||||
|
|
||||||
// File fetching and manipulation
|
// File fetching and manipulation
|
||||||
file_list: (params = {}) => daemonCallWithResult('file_list', params),
|
file_list: (params = {}) => daemonCallWithResult('file_list', params),
|
||||||
|
|
|
@ -10,16 +10,22 @@ import {
|
||||||
selectClaimsByUri,
|
selectClaimsByUri,
|
||||||
selectMyChannelClaims,
|
selectMyChannelClaims,
|
||||||
selectPendingIds,
|
selectPendingIds,
|
||||||
selectClaimsById,
|
|
||||||
} from 'redux/selectors/claims';
|
} from 'redux/selectors/claims';
|
||||||
|
|
||||||
import { doFetchTxoPage } from 'redux/actions/wallet';
|
import { doFetchTxoPage } from 'redux/actions/wallet';
|
||||||
import { selectSupportsByOutpoint } from 'redux/selectors/wallet';
|
import { selectSupportsByOutpoint } from 'redux/selectors/wallet';
|
||||||
import { creditsToString } from 'util/format-credits';
|
import { creditsToString } from 'util/format-credits';
|
||||||
import { batchActions } from 'util/batch-actions';
|
import { batchActions } from 'util/batch-actions';
|
||||||
import { createNormalizedClaimSearchKey } from 'util/claim';
|
import { createNormalizedClaimSearchKey } from 'util/claim';
|
||||||
import { PAGE_SIZE } from 'constants/claim';
|
import { PAGE_SIZE } from 'constants/claim';
|
||||||
|
import {
|
||||||
type ResolveEntries = Array<[string, GenericClaim]>;
|
selectPendingCollections,
|
||||||
|
} from 'redux/selectors/collections';
|
||||||
|
import {
|
||||||
|
doFetchItemsInCollection,
|
||||||
|
doFetchItemsInCollections,
|
||||||
|
doCollectionDelete,
|
||||||
|
} from 'redux/actions/collections';
|
||||||
|
|
||||||
export function doResolveUris(
|
export function doResolveUris(
|
||||||
uris: Array<string>,
|
uris: Array<string>,
|
||||||
|
@ -61,9 +67,12 @@ export function doResolveUris(
|
||||||
stream: ?StreamClaim,
|
stream: ?StreamClaim,
|
||||||
channel: ?ChannelClaim,
|
channel: ?ChannelClaim,
|
||||||
claimsInChannel: ?number,
|
claimsInChannel: ?number,
|
||||||
|
collection: ?CollectionClaim,
|
||||||
},
|
},
|
||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
|
const collectionIds: Array<string> = [];
|
||||||
|
|
||||||
return Lbry.resolve({ urls: urisToResolve, ...options }).then(
|
return Lbry.resolve({ urls: urisToResolve, ...options }).then(
|
||||||
async(result: ResolveResponse) => {
|
async(result: ResolveResponse) => {
|
||||||
let repostedResults = {};
|
let repostedResults = {};
|
||||||
|
@ -80,6 +89,7 @@ export function doResolveUris(
|
||||||
// https://github.com/facebook/flow/issues/2221
|
// https://github.com/facebook/flow/issues/2221
|
||||||
if (uriResolveInfo) {
|
if (uriResolveInfo) {
|
||||||
if (uriResolveInfo.error) {
|
if (uriResolveInfo.error) {
|
||||||
|
// $FlowFixMe
|
||||||
resolveInfo[uri] = { ...fallbackResolveInfo };
|
resolveInfo[uri] = { ...fallbackResolveInfo };
|
||||||
} else {
|
} else {
|
||||||
if (checkReposts) {
|
if (checkReposts) {
|
||||||
|
@ -96,6 +106,10 @@ export function doResolveUris(
|
||||||
result.channel = uriResolveInfo;
|
result.channel = uriResolveInfo;
|
||||||
// $FlowFixMe
|
// $FlowFixMe
|
||||||
result.claimsInChannel = uriResolveInfo.meta.claims_in_channel;
|
result.claimsInChannel = uriResolveInfo.meta.claims_in_channel;
|
||||||
|
} else if (uriResolveInfo.value_type === 'collection') {
|
||||||
|
result.collection = uriResolveInfo;
|
||||||
|
// $FlowFixMe
|
||||||
|
collectionIds.push(uriResolveInfo.claim_id);
|
||||||
} else {
|
} else {
|
||||||
result.stream = uriResolveInfo;
|
result.stream = uriResolveInfo;
|
||||||
if (uriResolveInfo.signing_channel) {
|
if (uriResolveInfo.signing_channel) {
|
||||||
|
@ -127,6 +141,11 @@ export function doResolveUris(
|
||||||
type: ACTIONS.RESOLVE_URIS_COMPLETED,
|
type: ACTIONS.RESOLVE_URIS_COMPLETED,
|
||||||
data: { resolveInfo },
|
data: { resolveInfo },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (collectionIds.length) {
|
||||||
|
dispatch(doFetchItemsInCollections({ collectionIds: collectionIds, pageSize: 5 }));
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -399,7 +418,7 @@ export function doCreateChannel(name: string, amount: number, optionalParams: an
|
||||||
description?: string,
|
description?: string,
|
||||||
website_url?: string,
|
website_url?: string,
|
||||||
email?: string,
|
email?: string,
|
||||||
tags?: Array<string>,
|
tags?: Array<Tag>,
|
||||||
languages?: Array<string>,
|
languages?: Array<string>,
|
||||||
} = {
|
} = {
|
||||||
name,
|
name,
|
||||||
|
@ -493,7 +512,6 @@ export function doUpdateChannel(params: any, cb: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// we'll need to remove these once we add locations/channels to channel page edit/create options
|
// we'll need to remove these once we add locations/channels to channel page edit/create options
|
||||||
|
|
||||||
if (channelClaim && channelClaim.value && channelClaim.value.locations) {
|
if (channelClaim && channelClaim.value && channelClaim.value.locations) {
|
||||||
updateParams.locations = channelClaim.value.locations;
|
updateParams.locations = channelClaim.value.locations;
|
||||||
}
|
}
|
||||||
|
@ -531,7 +549,7 @@ export function doImportChannel(certificate: string) {
|
||||||
});
|
});
|
||||||
|
|
||||||
return Lbry.channel_import({ channel_data: certificate })
|
return Lbry.channel_import({ channel_data: certificate })
|
||||||
.then((result: string) => {
|
.then(() => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.IMPORT_CHANNEL_COMPLETED,
|
type: ACTIONS.IMPORT_CHANNEL_COMPLETED,
|
||||||
});
|
});
|
||||||
|
@ -573,11 +591,45 @@ export function doFetchChannelListMine(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function doFetchCollectionListMine(page: number = 1, pageSize: number = 99999) {
|
||||||
|
return (dispatch: Dispatch) => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.FETCH_COLLECTION_LIST_STARTED,
|
||||||
|
});
|
||||||
|
|
||||||
|
const callback = (response: CollectionListResponse) => {
|
||||||
|
const { items } = response;
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.FETCH_COLLECTION_LIST_COMPLETED,
|
||||||
|
data: { claims: items },
|
||||||
|
});
|
||||||
|
dispatch(
|
||||||
|
doFetchItemsInCollections({
|
||||||
|
collectionIds: items.map(claim => claim.claim_id),
|
||||||
|
page_size: 5,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const failure = error => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.FETCH_COLLECTION_LIST_FAILED,
|
||||||
|
data: error,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Lbry.collection_list({ page, page_size: pageSize, resolve_claims: 1, resolve: true }).then(
|
||||||
|
callback,
|
||||||
|
failure
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function doClaimSearch(
|
export function doClaimSearch(
|
||||||
options: {
|
options: {
|
||||||
page_size: number,
|
page_size: number,
|
||||||
page: number,
|
page: number,
|
||||||
no_totals: boolean,
|
no_totals?: boolean,
|
||||||
any_tags?: Array<string>,
|
any_tags?: Array<string>,
|
||||||
claim_ids?: Array<string>,
|
claim_ids?: Array<string>,
|
||||||
channel_ids?: Array<string>,
|
channel_ids?: Array<string>,
|
||||||
|
@ -618,7 +670,7 @@ export function doClaimSearch(
|
||||||
pageSize: options.page_size,
|
pageSize: options.page_size,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return true;
|
return resolveInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
const failure = err => {
|
const failure = err => {
|
||||||
|
@ -638,8 +690,7 @@ export function doClaimSearch(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doRepost(options: StreamRepostOptions) {
|
export function doRepost(options: StreamRepostOptions) {
|
||||||
return (dispatch: Dispatch) => {
|
return (dispatch: Dispatch): Promise<any> => {
|
||||||
// $FlowFixMe
|
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.CLAIM_REPOST_STARTED,
|
type: ACTIONS.CLAIM_REPOST_STARTED,
|
||||||
|
@ -679,6 +730,189 @@ export function doRepost(options: StreamRepostOptions) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function doCollectionPublish(
|
||||||
|
options: {
|
||||||
|
name: string,
|
||||||
|
bid: string,
|
||||||
|
blocking: true,
|
||||||
|
title?: string,
|
||||||
|
channel_id?: string,
|
||||||
|
thumbnail_url?: string,
|
||||||
|
description?: string,
|
||||||
|
tags?: Array<Tag>,
|
||||||
|
languages?: Array<string>,
|
||||||
|
claims: Array<string>,
|
||||||
|
},
|
||||||
|
localId: string
|
||||||
|
) {
|
||||||
|
return (dispatch: Dispatch): Promise<any> => {
|
||||||
|
// $FlowFixMe
|
||||||
|
|
||||||
|
const params: {
|
||||||
|
name: string,
|
||||||
|
bid: string,
|
||||||
|
channel_id?: string,
|
||||||
|
blocking?: true,
|
||||||
|
title?: string,
|
||||||
|
thumbnail_url?: string,
|
||||||
|
description?: string,
|
||||||
|
tags?: Array<string>,
|
||||||
|
languages?: Array<string>,
|
||||||
|
claims: Array<string>,
|
||||||
|
} = {
|
||||||
|
name: options.name,
|
||||||
|
bid: creditsToString(options.bid),
|
||||||
|
title: options.title,
|
||||||
|
thumbnail_url: options.thumbnail_url,
|
||||||
|
description: options.description,
|
||||||
|
tags: [],
|
||||||
|
languages: options.languages || [],
|
||||||
|
locations: [],
|
||||||
|
blocking: true,
|
||||||
|
claims: options.claims,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (options.tags) {
|
||||||
|
params['tags'] = options.tags.map(tag => tag.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.channel_id) {
|
||||||
|
params['channel_id'] = options.channel_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise(resolve => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COLLECTION_PUBLISH_STARTED,
|
||||||
|
});
|
||||||
|
|
||||||
|
function success(response) {
|
||||||
|
const collectionClaim = response.outputs[0];
|
||||||
|
dispatch(
|
||||||
|
batchActions(
|
||||||
|
{
|
||||||
|
type: ACTIONS.COLLECTION_PUBLISH_COMPLETED,
|
||||||
|
data: { claimId: collectionClaim.claim_id },
|
||||||
|
},
|
||||||
|
// move unpublished collection to pending collection with new publish id
|
||||||
|
// recent publish won't resolve this second. handle it in checkPending
|
||||||
|
{
|
||||||
|
type: ACTIONS.UPDATE_PENDING_CLAIMS,
|
||||||
|
data: {
|
||||||
|
claims: [collectionClaim],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COLLECTION_PENDING,
|
||||||
|
data: { localId: localId, claimId: collectionClaim.claim_id },
|
||||||
|
});
|
||||||
|
dispatch(doCheckPendingClaims());
|
||||||
|
dispatch(doFetchCollectionListMine(1, 10));
|
||||||
|
return resolve(collectionClaim);
|
||||||
|
}
|
||||||
|
|
||||||
|
function failure(error) {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COLLECTION_PUBLISH_FAILED,
|
||||||
|
data: {
|
||||||
|
error: error.message,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Lbry.collection_create(params).then(success, failure);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doCollectionPublishUpdate(options: {
|
||||||
|
bid?: string,
|
||||||
|
blocking?: true,
|
||||||
|
title?: string,
|
||||||
|
thumbnail_url?: string,
|
||||||
|
description?: string,
|
||||||
|
claim_id: string,
|
||||||
|
tags?: Array<Tag>,
|
||||||
|
languages?: Array<string>,
|
||||||
|
claims?: Array<string>,
|
||||||
|
}) {
|
||||||
|
return (dispatch: Dispatch): Promise<any> => {
|
||||||
|
// TODO: implement one click update
|
||||||
|
|
||||||
|
const updateParams: {
|
||||||
|
bid?: string,
|
||||||
|
blocking?: true,
|
||||||
|
title?: string,
|
||||||
|
thumbnail_url?: string,
|
||||||
|
description?: string,
|
||||||
|
claim_id: string,
|
||||||
|
tags?: Array<string>,
|
||||||
|
languages?: Array<string>,
|
||||||
|
claims?: Array<string>,
|
||||||
|
clear_claims: boolean,
|
||||||
|
} = {
|
||||||
|
bid: creditsToString(options.bid),
|
||||||
|
title: options.title,
|
||||||
|
thumbnail_url: options.thumbnail_url,
|
||||||
|
description: options.description,
|
||||||
|
tags: [],
|
||||||
|
languages: options.languages || [],
|
||||||
|
locations: [],
|
||||||
|
blocking: true,
|
||||||
|
claim_id: options.claim_id,
|
||||||
|
clear_claims: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (options.tags) {
|
||||||
|
updateParams['tags'] = options.tags.map(tag => tag.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.claims) {
|
||||||
|
updateParams['claims'] = options.claims;
|
||||||
|
}
|
||||||
|
return new Promise(resolve => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COLLECTION_PUBLISH_UPDATE_STARTED,
|
||||||
|
});
|
||||||
|
|
||||||
|
function success(response) {
|
||||||
|
const collectionClaim = response.outputs[0];
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COLLECTION_PUBLISH_UPDATE_COMPLETED,
|
||||||
|
data: {
|
||||||
|
collectionClaim,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COLLECTION_PENDING,
|
||||||
|
data: { claimId: collectionClaim.claim_id },
|
||||||
|
});
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.UPDATE_PENDING_CLAIMS,
|
||||||
|
data: {
|
||||||
|
claims: [collectionClaim],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
dispatch(doCheckPendingClaims());
|
||||||
|
dispatch(doFetchCollectionListMine(1, 10));
|
||||||
|
return resolve(collectionClaim);
|
||||||
|
}
|
||||||
|
|
||||||
|
function failure(error) {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COLLECTION_PUBLISH_UPDATE_FAILED,
|
||||||
|
data: {
|
||||||
|
error: error.message,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Lbry.collection_update(updateParams).then(success, failure);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function doCheckPublishNameAvailability(name: string) {
|
export function doCheckPublishNameAvailability(name: string) {
|
||||||
return (dispatch: Dispatch) => {
|
return (dispatch: Dispatch) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
|
@ -750,6 +984,7 @@ export const doCheckPendingClaims = (onConfirmed: Function) => (
|
||||||
const checkClaimList = () => {
|
const checkClaimList = () => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const pendingIdSet = new Set(selectPendingIds(state));
|
const pendingIdSet = new Set(selectPendingIds(state));
|
||||||
|
const pendingCollections = selectPendingCollections(state);
|
||||||
Lbry.claim_list({ page: 1, page_size: 10 })
|
Lbry.claim_list({ page: 1, page_size: 10 })
|
||||||
.then(result => {
|
.then(result => {
|
||||||
const claims = result.items;
|
const claims = result.items;
|
||||||
|
@ -758,6 +993,10 @@ export const doCheckPendingClaims = (onConfirmed: Function) => (
|
||||||
const { claim_id: claimId } = claim;
|
const { claim_id: claimId } = claim;
|
||||||
if (claim.confirmations > 0 && pendingIdSet.has(claimId)) {
|
if (claim.confirmations > 0 && pendingIdSet.has(claimId)) {
|
||||||
pendingIdSet.delete(claimId);
|
pendingIdSet.delete(claimId);
|
||||||
|
if (Object.keys(pendingCollections).includes(claim.claim_id)) {
|
||||||
|
dispatch(doFetchItemsInCollection({ collectionId: claim.claim_id }));
|
||||||
|
dispatch(doCollectionDelete(claim.claim_id, 'pending'));
|
||||||
|
}
|
||||||
claimsToConfirm.push(claim);
|
claimsToConfirm.push(claim);
|
||||||
if (onConfirmed) {
|
if (onConfirmed) {
|
||||||
onConfirmed(claim);
|
onConfirmed(claim);
|
||||||
|
|
494
src/redux/actions/collections.js
Normal file
494
src/redux/actions/collections.js
Normal file
|
@ -0,0 +1,494 @@
|
||||||
|
// @flow
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
import Lbry from 'lbry';
|
||||||
|
import { doClaimSearch, doAbandonClaim } from 'redux/actions/claims';
|
||||||
|
import { makeSelectClaimForClaimId } from 'redux/selectors/claims';
|
||||||
|
import {
|
||||||
|
makeSelectCollectionForId,
|
||||||
|
// makeSelectPublishedCollectionForId, // for "save" or "copy" action
|
||||||
|
makeSelectMyPublishedCollectionForId,
|
||||||
|
makeSelectPublishedCollectionForId,
|
||||||
|
makeSelectUnpublishedCollectionForId,
|
||||||
|
makeSelectEditedCollectionForId,
|
||||||
|
} from 'redux/selectors/collections';
|
||||||
|
import * as COLS from 'constants/collections';
|
||||||
|
|
||||||
|
const getTimestamp = () => {
|
||||||
|
return Math.floor(Date.now() / 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const FETCH_BATCH_SIZE = 10;
|
||||||
|
|
||||||
|
export const doLocalCollectionCreate = (
|
||||||
|
name: string,
|
||||||
|
collectionItems: Array<string>,
|
||||||
|
type: string,
|
||||||
|
sourceId: string
|
||||||
|
) => (dispatch: Dispatch) => {
|
||||||
|
return dispatch({
|
||||||
|
type: ACTIONS.COLLECTION_NEW,
|
||||||
|
data: {
|
||||||
|
entry: {
|
||||||
|
id: uuid(), // start with a uuid, this becomes a claimId after publish
|
||||||
|
name: name,
|
||||||
|
updatedAt: getTimestamp(),
|
||||||
|
items: collectionItems || [],
|
||||||
|
sourceId: sourceId,
|
||||||
|
type: type,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const doCollectionDelete = (id: string, colKey: ?string = undefined) => (
|
||||||
|
dispatch: Dispatch,
|
||||||
|
getState: GetState
|
||||||
|
) => {
|
||||||
|
const state = getState();
|
||||||
|
const claim = makeSelectClaimForClaimId(id)(state);
|
||||||
|
const collectionDelete = () =>
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COLLECTION_DELETE,
|
||||||
|
data: {
|
||||||
|
id: id,
|
||||||
|
collectionKey: colKey,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (claim && !colKey) {
|
||||||
|
// could support "abandon, but keep" later
|
||||||
|
const { txid, nout } = claim;
|
||||||
|
return dispatch(doAbandonClaim(txid, nout, collectionDelete));
|
||||||
|
}
|
||||||
|
return collectionDelete();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Given a collection, save its collectionId to be resolved and displayed in Library
|
||||||
|
// export const doCollectionSave = (
|
||||||
|
// id: string,
|
||||||
|
// ) => (dispatch: Dispatch) => {
|
||||||
|
// return dispatch({
|
||||||
|
// type: ACTIONS.COLLECTION_SAVE,
|
||||||
|
// data: {
|
||||||
|
// id: id,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// };
|
||||||
|
|
||||||
|
// Given a collection and name, copy it to a local private collection with a name
|
||||||
|
// export const doCollectionCopy = (
|
||||||
|
// id: string,
|
||||||
|
// ) => (dispatch: Dispatch) => {
|
||||||
|
// return dispatch({
|
||||||
|
// type: ACTIONS.COLLECTION_COPY,
|
||||||
|
// data: {
|
||||||
|
// id: id,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// };
|
||||||
|
|
||||||
|
export const doFetchItemsInCollections = (
|
||||||
|
resolveItemsOptions: {
|
||||||
|
collectionIds: Array<string>,
|
||||||
|
pageSize?: number,
|
||||||
|
},
|
||||||
|
resolveStartedCallback?: () => void
|
||||||
|
) => async (dispatch: Dispatch, getState: GetState) => {
|
||||||
|
/*
|
||||||
|
1) make sure all the collection claims are loaded into claims reducer, search/resolve if necessary.
|
||||||
|
2) get the item claims for each
|
||||||
|
3) format and make sure they're in the order as in the claim
|
||||||
|
4) Build the collection objects and update collections reducer
|
||||||
|
5) Update redux claims reducer
|
||||||
|
*/
|
||||||
|
let state = getState();
|
||||||
|
const { collectionIds, pageSize } = resolveItemsOptions;
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COLLECTION_ITEMS_RESOLVE_STARTED,
|
||||||
|
data: { ids: collectionIds },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (resolveStartedCallback) resolveStartedCallback();
|
||||||
|
|
||||||
|
const collectionIdsToSearch = collectionIds.filter(claimId => !state.claims.byId[claimId]);
|
||||||
|
|
||||||
|
if (collectionIdsToSearch.length) {
|
||||||
|
await dispatch(doClaimSearch({ claim_ids: collectionIdsToSearch, page: 1, page_size: 9999 }));
|
||||||
|
}
|
||||||
|
|
||||||
|
const stateAfterClaimSearch = getState();
|
||||||
|
|
||||||
|
async function fetchItemsForCollectionClaim(claim: CollectionClaim, pageSize?: number) {
|
||||||
|
const totalItems = claim.value.claims && claim.value.claims.length;
|
||||||
|
const claimId = claim.claim_id;
|
||||||
|
const itemOrder = claim.value.claims;
|
||||||
|
|
||||||
|
const sortResults = (items: Array<Claim>, claimList) => {
|
||||||
|
const newItems: Array<Claim> = [];
|
||||||
|
claimList.forEach(id => {
|
||||||
|
const index = items.findIndex(i => i.claim_id === id);
|
||||||
|
if (index >= 0) {
|
||||||
|
newItems.push(items[index]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
/*
|
||||||
|
This will return newItems[] of length less than total_items below
|
||||||
|
if one or more of the claims has been abandoned. That's ok for now.
|
||||||
|
*/
|
||||||
|
return newItems;
|
||||||
|
};
|
||||||
|
|
||||||
|
const mergeBatches = (
|
||||||
|
arrayOfResults: Array<{ items: Array<Claim>, total_items: number }>,
|
||||||
|
claimList: Array<string>
|
||||||
|
) => {
|
||||||
|
const mergedResults: { items: Array<Claim>, total_items: number } = {
|
||||||
|
items: [],
|
||||||
|
total_items: 0,
|
||||||
|
};
|
||||||
|
arrayOfResults.forEach(result => {
|
||||||
|
mergedResults.items = mergedResults.items.concat(result.items);
|
||||||
|
mergedResults.total_items = result.total_items;
|
||||||
|
});
|
||||||
|
|
||||||
|
mergedResults.items = sortResults(mergedResults.items, claimList);
|
||||||
|
return mergedResults;
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const batchSize = pageSize || FETCH_BATCH_SIZE;
|
||||||
|
const batches: Array<Promise<any>> = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < Math.ceil(totalItems / batchSize); i++) {
|
||||||
|
batches[i] = Lbry.claim_search({
|
||||||
|
claim_ids: claim.value.claims,
|
||||||
|
page: i + 1,
|
||||||
|
page_size: batchSize,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const itemsInBatches = await Promise.all(batches);
|
||||||
|
const result = mergeBatches(itemsInBatches, itemOrder);
|
||||||
|
|
||||||
|
// $FlowFixMe
|
||||||
|
const itemsById: { claimId: string, items?: ?Array<GenericClaim> } = { claimId: claimId };
|
||||||
|
if (result.items) {
|
||||||
|
itemsById.items = result.items;
|
||||||
|
} else {
|
||||||
|
itemsById.items = null;
|
||||||
|
}
|
||||||
|
return itemsById;
|
||||||
|
} catch (e) {
|
||||||
|
return {
|
||||||
|
claimId: claimId,
|
||||||
|
items: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatForClaimActions(resultClaimsByUri) {
|
||||||
|
const formattedClaims = {};
|
||||||
|
Object.entries(resultClaimsByUri).forEach(([uri, uriResolveInfo]) => {
|
||||||
|
// Flow has terrible Object.entries support
|
||||||
|
// https://github.com/facebook/flow/issues/2221
|
||||||
|
if (uriResolveInfo) {
|
||||||
|
let result = {};
|
||||||
|
if (uriResolveInfo.value_type === 'channel') {
|
||||||
|
result.channel = uriResolveInfo;
|
||||||
|
// $FlowFixMe
|
||||||
|
result.claimsInChannel = uriResolveInfo.meta.claims_in_channel;
|
||||||
|
// ALSO SKIP COLLECTIONS
|
||||||
|
} else if (uriResolveInfo.value_type === 'collection') {
|
||||||
|
result.collection = uriResolveInfo;
|
||||||
|
} else {
|
||||||
|
result.stream = uriResolveInfo;
|
||||||
|
if (uriResolveInfo.signing_channel) {
|
||||||
|
result.channel = uriResolveInfo.signing_channel;
|
||||||
|
result.claimsInChannel =
|
||||||
|
(uriResolveInfo.signing_channel.meta &&
|
||||||
|
uriResolveInfo.signing_channel.meta.claims_in_channel) ||
|
||||||
|
0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// $FlowFixMe
|
||||||
|
formattedClaims[uri] = result;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return formattedClaims;
|
||||||
|
}
|
||||||
|
|
||||||
|
const invalidCollectionIds = [];
|
||||||
|
const promisedCollectionItemFetches = [];
|
||||||
|
collectionIds.forEach(collectionId => {
|
||||||
|
const claim = makeSelectClaimForClaimId(collectionId)(stateAfterClaimSearch);
|
||||||
|
if (!claim) {
|
||||||
|
invalidCollectionIds.push(collectionId);
|
||||||
|
} else {
|
||||||
|
promisedCollectionItemFetches.push(fetchItemsForCollectionClaim(claim, pageSize));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// $FlowFixMe
|
||||||
|
const collectionItemsById: Array<{
|
||||||
|
claimId: string,
|
||||||
|
items: ?Array<GenericClaim>,
|
||||||
|
}> = await Promise.all(promisedCollectionItemFetches);
|
||||||
|
|
||||||
|
const newCollectionObjectsById = {};
|
||||||
|
const resolvedItemsByUrl = {};
|
||||||
|
collectionItemsById.forEach(entry => {
|
||||||
|
// $FlowFixMe
|
||||||
|
const collectionItems: Array<any> = entry.items;
|
||||||
|
const collectionId = entry.claimId;
|
||||||
|
if (collectionItems) {
|
||||||
|
const claim = makeSelectClaimForClaimId(collectionId)(stateAfterClaimSearch);
|
||||||
|
|
||||||
|
const editedCollection = makeSelectEditedCollectionForId(collectionId)(stateAfterClaimSearch);
|
||||||
|
const { name, timestamp, value } = claim || {};
|
||||||
|
const { title } = value;
|
||||||
|
const valueTypes = new Set();
|
||||||
|
const streamTypes = new Set();
|
||||||
|
|
||||||
|
let newItems = [];
|
||||||
|
let isPlaylist;
|
||||||
|
|
||||||
|
if (collectionItems) {
|
||||||
|
collectionItems.forEach(collectionItem => {
|
||||||
|
newItems.push(collectionItem.permanent_url);
|
||||||
|
valueTypes.add(collectionItem.value_type);
|
||||||
|
if (collectionItem.value.stream_type) {
|
||||||
|
streamTypes.add(collectionItem.value.stream_type);
|
||||||
|
}
|
||||||
|
resolvedItemsByUrl[collectionItem.canonical_url] = collectionItem;
|
||||||
|
});
|
||||||
|
isPlaylist =
|
||||||
|
valueTypes.size === 1 &&
|
||||||
|
valueTypes.has('stream') &&
|
||||||
|
((streamTypes.size === 1 && (streamTypes.has('audio') || streamTypes.has('video'))) ||
|
||||||
|
(streamTypes.size === 2 && (streamTypes.has('audio') && streamTypes.has('video'))));
|
||||||
|
}
|
||||||
|
|
||||||
|
newCollectionObjectsById[collectionId] = {
|
||||||
|
items: newItems,
|
||||||
|
id: collectionId,
|
||||||
|
name: title || name,
|
||||||
|
itemCount: claim.value.claims.length,
|
||||||
|
type: isPlaylist ? 'playlist' : 'collection',
|
||||||
|
updatedAt: timestamp,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (editedCollection && timestamp > editedCollection['updatedAt']) {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COLLECTION_DELETE,
|
||||||
|
data: {
|
||||||
|
id: collectionId,
|
||||||
|
collectionKey: 'edited',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
invalidCollectionIds.push(collectionId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const formattedClaimsByUri = formatForClaimActions(collectionItemsById);
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.RESOLVE_URIS_COMPLETED,
|
||||||
|
data: { resolveInfo: formattedClaimsByUri },
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COLLECTION_ITEMS_RESOLVE_COMPLETED,
|
||||||
|
data: {
|
||||||
|
resolvedCollections: newCollectionObjectsById,
|
||||||
|
failedCollectionIds: invalidCollectionIds,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const doFetchItemsInCollection = (
|
||||||
|
options: { collectionId: string, pageSize?: number },
|
||||||
|
cb?: () => void
|
||||||
|
) => {
|
||||||
|
const { collectionId, pageSize } = options;
|
||||||
|
const newOptions: { collectionIds: Array<string>, pageSize?: number } = {
|
||||||
|
collectionIds: [collectionId],
|
||||||
|
};
|
||||||
|
if (pageSize) newOptions.pageSize = pageSize;
|
||||||
|
return doFetchItemsInCollections(newOptions, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const doCollectionEdit = (collectionId: string, params: CollectionEditParams) => async (
|
||||||
|
dispatch: Dispatch,
|
||||||
|
getState: GetState
|
||||||
|
) => {
|
||||||
|
const state = getState();
|
||||||
|
const collection: Collection = makeSelectCollectionForId(collectionId)(state);
|
||||||
|
const editedCollection: Collection = makeSelectEditedCollectionForId(collectionId)(state);
|
||||||
|
const unpublishedCollection: Collection = makeSelectUnpublishedCollectionForId(collectionId)(
|
||||||
|
state
|
||||||
|
);
|
||||||
|
const publishedCollection: Collection = makeSelectPublishedCollectionForId(collectionId)(state); // needs to be published only
|
||||||
|
|
||||||
|
const generateCollectionItemsFromSearchResult = results => {
|
||||||
|
return (
|
||||||
|
Object.values(results)
|
||||||
|
// $FlowFixMe
|
||||||
|
.reduce(
|
||||||
|
(
|
||||||
|
acc,
|
||||||
|
cur: {
|
||||||
|
stream: ?StreamClaim,
|
||||||
|
channel: ?ChannelClaim,
|
||||||
|
claimsInChannel: ?number,
|
||||||
|
collection: ?CollectionClaim,
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
let url;
|
||||||
|
if (cur.stream) {
|
||||||
|
url = cur.stream.permanent_url;
|
||||||
|
} else if (cur.channel) {
|
||||||
|
url = cur.channel.permanent_url;
|
||||||
|
} else if (cur.collection) {
|
||||||
|
url = cur.collection.permanent_url;
|
||||||
|
} else {
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
acc.push(url);
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!collection) {
|
||||||
|
return dispatch({
|
||||||
|
type: ACTIONS.COLLECTION_ERROR,
|
||||||
|
data: {
|
||||||
|
message: 'collection does not exist',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentItems = collection.items ? collection.items.concat() : [];
|
||||||
|
const { claims: passedClaims, order, claimIds, replace, remove, type } = params;
|
||||||
|
|
||||||
|
const collectionType = type || collection.type;
|
||||||
|
let newItems: Array<?string> = currentItems;
|
||||||
|
|
||||||
|
if (passedClaims) {
|
||||||
|
if (remove) {
|
||||||
|
const passedUrls = passedClaims.map(claim => claim.permanent_url);
|
||||||
|
// $FlowFixMe // need this?
|
||||||
|
newItems = currentItems.filter((item: string) => !passedUrls.includes(item));
|
||||||
|
} else {
|
||||||
|
passedClaims.forEach(claim => newItems.push(claim.permanent_url));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (claimIds) {
|
||||||
|
const batches = [];
|
||||||
|
if (claimIds.length > 50) {
|
||||||
|
for (let i = 0; i < Math.ceil(claimIds.length / 50); i++) {
|
||||||
|
batches[i] = claimIds.slice(i * 50, (i + 1) * 50);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
batches[0] = claimIds;
|
||||||
|
}
|
||||||
|
const resultArray = await Promise.all(
|
||||||
|
batches.map(batch => {
|
||||||
|
let options = { claim_ids: batch, page: 1, page_size: 50 };
|
||||||
|
return dispatch(doClaimSearch(options));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const searchResults = Object.assign({}, ...resultArray);
|
||||||
|
|
||||||
|
if (replace) {
|
||||||
|
newItems = generateCollectionItemsFromSearchResult(searchResults);
|
||||||
|
} else {
|
||||||
|
newItems = currentItems.concat(generateCollectionItemsFromSearchResult(searchResults));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (order) {
|
||||||
|
const [movedItem] = currentItems.splice(order.from, 1);
|
||||||
|
currentItems.splice(order.to, 0, movedItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log('p&e', publishedCollection.items, newItems, publishedCollection.items.join(','), newItems.join(','))
|
||||||
|
if (editedCollection) {
|
||||||
|
// delete edited if newItems are the same as publishedItems
|
||||||
|
if (publishedCollection.items.join(',') === newItems.join(',')) {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COLLECTION_DELETE,
|
||||||
|
data: {
|
||||||
|
id: collectionId,
|
||||||
|
collectionKey: 'edited',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COLLECTION_EDIT,
|
||||||
|
data: {
|
||||||
|
id: collectionId,
|
||||||
|
collectionKey: 'edited',
|
||||||
|
collection: {
|
||||||
|
items: newItems,
|
||||||
|
id: collectionId,
|
||||||
|
name: params.name || collection.name,
|
||||||
|
updatedAt: getTimestamp(),
|
||||||
|
type: collectionType,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (publishedCollection) {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COLLECTION_EDIT,
|
||||||
|
data: {
|
||||||
|
id: collectionId,
|
||||||
|
collectionKey: 'edited',
|
||||||
|
collection: {
|
||||||
|
items: newItems,
|
||||||
|
id: collectionId,
|
||||||
|
name: params.name || collection.name,
|
||||||
|
updatedAt: getTimestamp(),
|
||||||
|
type: collectionType,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else if (COLS.BUILTIN_LISTS.includes(collectionId)) {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COLLECTION_EDIT,
|
||||||
|
data: {
|
||||||
|
id: collectionId,
|
||||||
|
collectionKey: 'builtin',
|
||||||
|
collection: {
|
||||||
|
items: newItems,
|
||||||
|
id: collectionId,
|
||||||
|
name: params.name || collection.name,
|
||||||
|
updatedAt: getTimestamp(),
|
||||||
|
type: collectionType,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else if (unpublishedCollection) {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COLLECTION_EDIT,
|
||||||
|
data: {
|
||||||
|
id: collectionId,
|
||||||
|
collectionKey: 'unpublished',
|
||||||
|
collection: {
|
||||||
|
items: newItems,
|
||||||
|
id: collectionId,
|
||||||
|
name: params.name || collection.name,
|
||||||
|
updatedAt: getTimestamp(),
|
||||||
|
type: collectionType,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
|
@ -13,6 +13,9 @@ type SharedData = {
|
||||||
settings?: any,
|
settings?: any,
|
||||||
app_welcome_version?: number,
|
app_welcome_version?: number,
|
||||||
sharing_3P?: boolean,
|
sharing_3P?: boolean,
|
||||||
|
unpublishedCollections: CollectionGroup,
|
||||||
|
builtinCollections: CollectionGroup,
|
||||||
|
savedCollections: Array<string>,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -27,6 +30,9 @@ function extractUserState(rawObj: SharedData) {
|
||||||
settings,
|
settings,
|
||||||
app_welcome_version,
|
app_welcome_version,
|
||||||
sharing_3P,
|
sharing_3P,
|
||||||
|
unpublishedCollections,
|
||||||
|
builtinCollections,
|
||||||
|
savedCollections,
|
||||||
} = rawObj.value;
|
} = rawObj.value;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -38,6 +44,9 @@ function extractUserState(rawObj: SharedData) {
|
||||||
...(settings ? { settings } : {}),
|
...(settings ? { settings } : {}),
|
||||||
...(app_welcome_version ? { app_welcome_version } : {}),
|
...(app_welcome_version ? { app_welcome_version } : {}),
|
||||||
...(sharing_3P ? { sharing_3P } : {}),
|
...(sharing_3P ? { sharing_3P } : {}),
|
||||||
|
...(unpublishedCollections ? { unpublishedCollections } : {}),
|
||||||
|
...(builtinCollections ? { builtinCollections } : {}),
|
||||||
|
...(savedCollections ? { savedCollections } : {}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +64,9 @@ export function doPopulateSharedUserState(sharedSettings: any) {
|
||||||
settings,
|
settings,
|
||||||
app_welcome_version,
|
app_welcome_version,
|
||||||
sharing_3P,
|
sharing_3P,
|
||||||
|
unpublishedCollections,
|
||||||
|
builtinCollections,
|
||||||
|
savedCollections,
|
||||||
} = extractUserState(sharedSettings);
|
} = extractUserState(sharedSettings);
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.USER_STATE_POPULATE,
|
type: ACTIONS.USER_STATE_POPULATE,
|
||||||
|
@ -67,6 +79,9 @@ export function doPopulateSharedUserState(sharedSettings: any) {
|
||||||
settings,
|
settings,
|
||||||
welcomeVersion: app_welcome_version,
|
welcomeVersion: app_welcome_version,
|
||||||
allowAnalytics: sharing_3P,
|
allowAnalytics: sharing_3P,
|
||||||
|
unpublishedCollections,
|
||||||
|
builtinCollections,
|
||||||
|
savedCollections,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,7 +26,6 @@ export const buildSharedStateMiddleware = (
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
const actionResult = next(action);
|
const actionResult = next(action);
|
||||||
// Call `getState` after calling `next` to ensure the state has updated in response to the action
|
// Call `getState` after calling `next` to ensure the state has updated in response to the action
|
||||||
|
|
||||||
function runPreferences() {
|
function runPreferences() {
|
||||||
const nextState: { user: any, settings: any } = getState();
|
const nextState: { user: any, settings: any } = getState();
|
||||||
const syncEnabled =
|
const syncEnabled =
|
||||||
|
|
|
@ -13,6 +13,7 @@ import mergeClaim from 'util/merge-claim';
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
createChannelError: ?string,
|
createChannelError: ?string,
|
||||||
|
createCollectionError: ?string,
|
||||||
channelClaimCounts: { [string]: number },
|
channelClaimCounts: { [string]: number },
|
||||||
claimsByUri: { [string]: string },
|
claimsByUri: { [string]: string },
|
||||||
byId: { [string]: Claim },
|
byId: { [string]: Claim },
|
||||||
|
@ -21,9 +22,11 @@ type State = {
|
||||||
reflectingById: { [string]: ReflectingUpdate },
|
reflectingById: { [string]: ReflectingUpdate },
|
||||||
myClaims: ?Array<string>,
|
myClaims: ?Array<string>,
|
||||||
myChannelClaims: ?Array<string>,
|
myChannelClaims: ?Array<string>,
|
||||||
|
myCollectionClaims: ?Array<string>,
|
||||||
abandoningById: { [string]: boolean },
|
abandoningById: { [string]: boolean },
|
||||||
fetchingChannelClaims: { [string]: number },
|
fetchingChannelClaims: { [string]: number },
|
||||||
fetchingMyChannels: boolean,
|
fetchingMyChannels: boolean,
|
||||||
|
fetchingMyCollections: boolean,
|
||||||
fetchingClaimSearchByQuery: { [string]: boolean },
|
fetchingClaimSearchByQuery: { [string]: boolean },
|
||||||
purchaseUriSuccess: boolean,
|
purchaseUriSuccess: boolean,
|
||||||
myPurchases: ?Array<string>,
|
myPurchases: ?Array<string>,
|
||||||
|
@ -34,6 +37,7 @@ type State = {
|
||||||
claimSearchByQuery: { [string]: Array<string> },
|
claimSearchByQuery: { [string]: Array<string> },
|
||||||
claimSearchByQueryLastPageReached: { [string]: Array<boolean> },
|
claimSearchByQueryLastPageReached: { [string]: Array<boolean> },
|
||||||
creatingChannel: boolean,
|
creatingChannel: boolean,
|
||||||
|
creatingCollection: boolean,
|
||||||
paginatedClaimsByChannel: {
|
paginatedClaimsByChannel: {
|
||||||
[string]: {
|
[string]: {
|
||||||
all: Array<string>,
|
all: Array<string>,
|
||||||
|
@ -43,7 +47,9 @@ type State = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
updateChannelError: ?string,
|
updateChannelError: ?string,
|
||||||
|
updateCollectionError: ?string,
|
||||||
updatingChannel: boolean,
|
updatingChannel: boolean,
|
||||||
|
updatingCollection: boolean,
|
||||||
pendingChannelImport: string | boolean,
|
pendingChannelImport: string | boolean,
|
||||||
repostLoading: boolean,
|
repostLoading: boolean,
|
||||||
repostError: ?string,
|
repostError: ?string,
|
||||||
|
@ -66,6 +72,7 @@ const defaultState = {
|
||||||
fetchingChannelClaims: {},
|
fetchingChannelClaims: {},
|
||||||
resolvingUris: [],
|
resolvingUris: [],
|
||||||
myChannelClaims: undefined,
|
myChannelClaims: undefined,
|
||||||
|
myCollectionClaims: [],
|
||||||
myClaims: undefined,
|
myClaims: undefined,
|
||||||
myPurchases: undefined,
|
myPurchases: undefined,
|
||||||
myPurchasesPageNumber: undefined,
|
myPurchasesPageNumber: undefined,
|
||||||
|
@ -74,6 +81,7 @@ const defaultState = {
|
||||||
fetchingMyPurchases: false,
|
fetchingMyPurchases: false,
|
||||||
fetchingMyPurchasesError: undefined,
|
fetchingMyPurchasesError: undefined,
|
||||||
fetchingMyChannels: false,
|
fetchingMyChannels: false,
|
||||||
|
fetchingMyCollections: false,
|
||||||
abandoningById: {},
|
abandoningById: {},
|
||||||
pendingIds: [],
|
pendingIds: [],
|
||||||
reflectingById: {},
|
reflectingById: {},
|
||||||
|
@ -82,9 +90,13 @@ const defaultState = {
|
||||||
claimSearchByQueryLastPageReached: {},
|
claimSearchByQueryLastPageReached: {},
|
||||||
fetchingClaimSearchByQuery: {},
|
fetchingClaimSearchByQuery: {},
|
||||||
updateChannelError: '',
|
updateChannelError: '',
|
||||||
|
updateCollectionError: '',
|
||||||
updatingChannel: false,
|
updatingChannel: false,
|
||||||
creatingChannel: false,
|
creatingChannel: false,
|
||||||
createChannelError: undefined,
|
createChannelError: undefined,
|
||||||
|
updatingCollection: false,
|
||||||
|
creatingCollection: false,
|
||||||
|
createCollectionError: undefined,
|
||||||
pendingChannelImport: false,
|
pendingChannelImport: false,
|
||||||
repostLoading: false,
|
repostLoading: false,
|
||||||
repostError: undefined,
|
repostError: undefined,
|
||||||
|
@ -100,15 +112,7 @@ const defaultState = {
|
||||||
};
|
};
|
||||||
|
|
||||||
function handleClaimAction(state: State, action: any): State {
|
function handleClaimAction(state: State, action: any): State {
|
||||||
const {
|
const { resolveInfo }: ClaimActionResolveInfo = action.data;
|
||||||
resolveInfo,
|
|
||||||
}: {
|
|
||||||
[string]: {
|
|
||||||
stream: ?StreamClaim,
|
|
||||||
channel: ?ChannelClaim,
|
|
||||||
claimsInChannel: ?number,
|
|
||||||
},
|
|
||||||
} = action.data;
|
|
||||||
|
|
||||||
const byUri = Object.assign({}, state.claimsByUri);
|
const byUri = Object.assign({}, state.claimsByUri);
|
||||||
const byId = Object.assign({}, state.byId);
|
const byId = Object.assign({}, state.byId);
|
||||||
|
@ -119,7 +123,7 @@ function handleClaimAction(state: State, action: any): State {
|
||||||
|
|
||||||
Object.entries(resolveInfo).forEach(([url: string, resolveResponse: ResolveResponse]) => {
|
Object.entries(resolveInfo).forEach(([url: string, resolveResponse: ResolveResponse]) => {
|
||||||
// $FlowFixMe
|
// $FlowFixMe
|
||||||
const { claimsInChannel, stream, channel: channelFromResolve } = resolveResponse;
|
const { claimsInChannel, stream, channel: channelFromResolve, collection } = resolveResponse;
|
||||||
const channel = channelFromResolve || (stream && stream.signing_channel);
|
const channel = channelFromResolve || (stream && stream.signing_channel);
|
||||||
|
|
||||||
if (stream) {
|
if (stream) {
|
||||||
|
@ -158,15 +162,32 @@ function handleClaimAction(state: State, action: any): State {
|
||||||
} else {
|
} else {
|
||||||
byId[channel.claim_id] = channel;
|
byId[channel.claim_id] = channel;
|
||||||
}
|
}
|
||||||
// Also add the permanent_url here until lighthouse returns canonical_url for search results
|
|
||||||
byUri[channel.permanent_url] = channel.claim_id;
|
byUri[channel.permanent_url] = channel.claim_id;
|
||||||
byUri[channel.canonical_url] = channel.claim_id;
|
byUri[channel.canonical_url] = channel.claim_id;
|
||||||
newResolvingUrls.delete(channel.canonical_url);
|
newResolvingUrls.delete(channel.canonical_url);
|
||||||
newResolvingUrls.delete(channel.permanent_url);
|
newResolvingUrls.delete(channel.permanent_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (collection) {
|
||||||
|
if (pendingIds.includes(collection.claim_id)) {
|
||||||
|
byId[collection.claim_id] = mergeClaim(collection, byId[collection.claim_id]);
|
||||||
|
} else {
|
||||||
|
byId[collection.claim_id] = collection;
|
||||||
|
}
|
||||||
|
byUri[url] = collection.claim_id;
|
||||||
|
byUri[collection.canonical_url] = collection.claim_id;
|
||||||
|
byUri[collection.permanent_url] = collection.claim_id;
|
||||||
|
newResolvingUrls.delete(collection.canonical_url);
|
||||||
|
newResolvingUrls.delete(collection.permanent_url);
|
||||||
|
|
||||||
|
if (collection.is_my_output) {
|
||||||
|
myClaimIds.add(collection.claim_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
newResolvingUrls.delete(url);
|
newResolvingUrls.delete(url);
|
||||||
if (!stream && !channel && !pendingIds.includes(byUri[url])) {
|
if (!stream && !channel && !collection && !pendingIds.includes(byUri[url])) {
|
||||||
byUri[url] = null;
|
byUri[url] = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -306,6 +327,53 @@ reducers[ACTIONS.FETCH_CHANNEL_LIST_FAILED] = (state: State, action: any): State
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
reducers[ACTIONS.FETCH_COLLECTION_LIST_STARTED] = (state: State): State => ({
|
||||||
|
...state,
|
||||||
|
fetchingMyCollections: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.FETCH_COLLECTION_LIST_COMPLETED] = (state: State, action: any): State => {
|
||||||
|
const { claims }: { claims: Array<CollectionClaim> } = action.data;
|
||||||
|
const myClaims = state.myClaims || [];
|
||||||
|
let myClaimIds = new Set(myClaims);
|
||||||
|
const pendingIds = state.pendingIds || [];
|
||||||
|
let myCollectionClaimsSet = new Set([]);
|
||||||
|
const byId = Object.assign({}, state.byId);
|
||||||
|
const byUri = Object.assign({}, state.claimsByUri);
|
||||||
|
|
||||||
|
if (claims.length) {
|
||||||
|
myCollectionClaimsSet = new Set(state.myCollectionClaims);
|
||||||
|
claims.forEach(claim => {
|
||||||
|
const { meta } = claim;
|
||||||
|
const { canonical_url: canonicalUrl, permanent_url: permanentUrl, claim_id: claimId } = claim;
|
||||||
|
|
||||||
|
byUri[canonicalUrl] = claimId;
|
||||||
|
byUri[permanentUrl] = claimId;
|
||||||
|
|
||||||
|
// $FlowFixMe
|
||||||
|
myCollectionClaimsSet.add(claimId);
|
||||||
|
// we don't want to overwrite a pending result with a resolve
|
||||||
|
if (!pendingIds.some(c => c === claimId)) {
|
||||||
|
byId[claimId] = claim;
|
||||||
|
}
|
||||||
|
myClaimIds.add(claimId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
byId,
|
||||||
|
claimsByUri: byUri,
|
||||||
|
fetchingMyCollections: false,
|
||||||
|
myCollectionClaims: Array.from(myCollectionClaimsSet),
|
||||||
|
myClaims: myClaimIds ? Array.from(myClaimIds) : null,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
reducers[ACTIONS.FETCH_COLLECTION_LIST_FAILED] = (state: State): State => {
|
||||||
|
return { ...state, fetchingMyCollections: false };
|
||||||
|
};
|
||||||
|
|
||||||
reducers[ACTIONS.FETCH_CHANNEL_CLAIMS_STARTED] = (state: State, action: any): State => {
|
reducers[ACTIONS.FETCH_CHANNEL_CLAIMS_STARTED] = (state: State, action: any): State => {
|
||||||
const { uri, page } = action.data;
|
const { uri, page } = action.data;
|
||||||
const fetchingChannelClaims = Object.assign({}, state.fetchingChannelClaims);
|
const fetchingChannelClaims = Object.assign({}, state.fetchingChannelClaims);
|
||||||
|
@ -455,6 +523,7 @@ reducers[ACTIONS.ABANDON_CLAIM_SUCCEEDED] = (state: State, action: any): State =
|
||||||
const newMyClaims = state.myClaims ? state.myClaims.slice() : [];
|
const newMyClaims = state.myClaims ? state.myClaims.slice() : [];
|
||||||
const newMyChannelClaims = state.myChannelClaims ? state.myChannelClaims.slice() : [];
|
const newMyChannelClaims = state.myChannelClaims ? state.myChannelClaims.slice() : [];
|
||||||
const claimsByUri = Object.assign({}, state.claimsByUri);
|
const claimsByUri = Object.assign({}, state.claimsByUri);
|
||||||
|
const newMyCollectionClaims = state.myCollectionClaims ? state.myCollectionClaims.slice() : [];
|
||||||
|
|
||||||
Object.keys(claimsByUri).forEach(uri => {
|
Object.keys(claimsByUri).forEach(uri => {
|
||||||
if (claimsByUri[uri] === claimId) {
|
if (claimsByUri[uri] === claimId) {
|
||||||
|
@ -463,12 +532,14 @@ reducers[ACTIONS.ABANDON_CLAIM_SUCCEEDED] = (state: State, action: any): State =
|
||||||
});
|
});
|
||||||
const myClaims = newMyClaims.filter(i => i !== claimId);
|
const myClaims = newMyClaims.filter(i => i !== claimId);
|
||||||
const myChannelClaims = newMyChannelClaims.filter(i => i !== claimId);
|
const myChannelClaims = newMyChannelClaims.filter(i => i !== claimId);
|
||||||
|
const myCollectionClaims = newMyCollectionClaims.filter(i => i !== claimId);
|
||||||
|
|
||||||
delete byId[claimId];
|
delete byId[claimId];
|
||||||
|
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
myClaims,
|
myClaims,
|
||||||
myChannelClaims,
|
myChannelClaims,
|
||||||
|
myCollectionClaims,
|
||||||
byId,
|
byId,
|
||||||
claimsByUri,
|
claimsByUri,
|
||||||
});
|
});
|
||||||
|
@ -520,6 +591,61 @@ reducers[ACTIONS.UPDATE_CHANNEL_FAILED] = (state: State, action: any): State =>
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
reducers[ACTIONS.CLEAR_COLLECTION_ERRORS] = (state: State): State => ({
|
||||||
|
...state,
|
||||||
|
createCollectionError: null,
|
||||||
|
updateCollectionError: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.COLLECTION_PUBLISH_STARTED] = (state: State): State => ({
|
||||||
|
...state,
|
||||||
|
creatingCollection: true,
|
||||||
|
createCollectionError: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.COLLECTION_PUBLISH_COMPLETED] = (state: State, action: any): State => {
|
||||||
|
const myCollections = state.myCollectionClaims || [];
|
||||||
|
const myClaims = state.myClaims || [];
|
||||||
|
const { claimId } = action.data;
|
||||||
|
let myClaimIds = new Set(myClaims);
|
||||||
|
let myCollectionClaimsSet = new Set(myCollections);
|
||||||
|
myClaimIds.add(claimId);
|
||||||
|
myCollectionClaimsSet.add(claimId);
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
creatingCollection: false,
|
||||||
|
myClaims: Array.from(myClaimIds),
|
||||||
|
myCollectionClaims: Array.from(myCollectionClaimsSet),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
reducers[ACTIONS.COLLECTION_PUBLISH_FAILED] = (state: State, action: any): State => {
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
creatingCollection: false,
|
||||||
|
createCollectionError: action.data.error,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
reducers[ACTIONS.COLLECTION_PUBLISH_UPDATE_STARTED] = (state: State, action: any): State => {
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
updateCollectionError: '',
|
||||||
|
updatingCollection: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
reducers[ACTIONS.COLLECTION_PUBLISH_UPDATE_COMPLETED] = (state: State, action: any): State => {
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
updateCollectionError: '',
|
||||||
|
updatingCollection: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
reducers[ACTIONS.COLLECTION_PUBLISH_UPDATE_FAILED] = (state: State, action: any): State => {
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
updateCollectionError: action.data.error,
|
||||||
|
updatingCollection: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
reducers[ACTIONS.IMPORT_CHANNEL_STARTED] = (state: State): State =>
|
reducers[ACTIONS.IMPORT_CHANNEL_STARTED] = (state: State): State =>
|
||||||
Object.assign({}, state, { pendingChannelImports: true });
|
Object.assign({}, state, { pendingChannelImports: true });
|
||||||
|
|
||||||
|
|
223
src/redux/reducers/collections.js
Normal file
223
src/redux/reducers/collections.js
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
// @flow
|
||||||
|
import { handleActions } from 'util/redux-utils';
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
import * as COLS from 'constants/collections';
|
||||||
|
|
||||||
|
const getTimestamp = () => {
|
||||||
|
return Math.floor(Date.now() / 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultState: CollectionState = {
|
||||||
|
builtin: {
|
||||||
|
watchlater: {
|
||||||
|
items: [],
|
||||||
|
id: COLS.WATCH_LATER_ID,
|
||||||
|
name: 'Watch Later',
|
||||||
|
updatedAt: getTimestamp(),
|
||||||
|
type: COLS.COL_TYPE_PLAYLIST,
|
||||||
|
},
|
||||||
|
favorites: {
|
||||||
|
items: [],
|
||||||
|
id: COLS.FAVORITES_ID,
|
||||||
|
name: 'Favorites',
|
||||||
|
type: COLS.COL_TYPE_PLAYLIST,
|
||||||
|
updatedAt: getTimestamp(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
resolved: {},
|
||||||
|
unpublished: {}, // sync
|
||||||
|
edited: {},
|
||||||
|
pending: {},
|
||||||
|
saved: [],
|
||||||
|
isResolvingCollectionById: {},
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const collectionsReducer = handleActions(
|
||||||
|
{
|
||||||
|
[ACTIONS.COLLECTION_NEW]: (state, action) => {
|
||||||
|
const { entry: params } = action.data; // { id:, items: Array<string>}
|
||||||
|
// entry
|
||||||
|
const newListTemplate = {
|
||||||
|
id: params.id,
|
||||||
|
name: params.name,
|
||||||
|
items: [],
|
||||||
|
updatedAt: getTimestamp(),
|
||||||
|
type: params.type,
|
||||||
|
};
|
||||||
|
|
||||||
|
const newList = Object.assign({}, newListTemplate, { ...params });
|
||||||
|
const { unpublished: lists } = state;
|
||||||
|
const newLists = Object.assign({}, lists, { [params.id]: newList });
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
unpublished: newLists,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
[ACTIONS.COLLECTION_DELETE]: (state, action) => {
|
||||||
|
const { id, collectionKey } = action.data;
|
||||||
|
const { edited: editList, unpublished: unpublishedList, pending: pendingList } = state;
|
||||||
|
const newEditList = Object.assign({}, editList);
|
||||||
|
const newUnpublishedList = Object.assign({}, unpublishedList);
|
||||||
|
|
||||||
|
const newPendingList = Object.assign({}, pendingList);
|
||||||
|
|
||||||
|
if (collectionKey && state[collectionKey] && state[collectionKey][id]) {
|
||||||
|
const newList = Object.assign({}, state[collectionKey]);
|
||||||
|
delete newList[id];
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
[collectionKey]: newList,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
if (newEditList[id]) {
|
||||||
|
delete newEditList[id];
|
||||||
|
} else if (newUnpublishedList[id]) {
|
||||||
|
delete newUnpublishedList[id];
|
||||||
|
} else if (newPendingList[id]) {
|
||||||
|
delete newPendingList[id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
edited: newEditList,
|
||||||
|
unpublished: newUnpublishedList,
|
||||||
|
pending: newPendingList,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
[ACTIONS.COLLECTION_PENDING]: (state, action) => {
|
||||||
|
const { localId, claimId } = action.data;
|
||||||
|
const { edited: editList, unpublished: unpublishedList, pending: pendingList } = state;
|
||||||
|
const newEditList = Object.assign({}, editList);
|
||||||
|
const newUnpublishedList = Object.assign({}, unpublishedList);
|
||||||
|
const newPendingList = Object.assign({}, pendingList);
|
||||||
|
|
||||||
|
if (localId) {
|
||||||
|
// new publish
|
||||||
|
newPendingList[claimId] = Object.assign({}, newUnpublishedList[localId] || {});
|
||||||
|
delete newUnpublishedList[localId];
|
||||||
|
} else {
|
||||||
|
// edit update
|
||||||
|
newPendingList[claimId] = Object.assign({}, newEditList[claimId]);
|
||||||
|
delete newEditList[claimId];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
edited: newEditList,
|
||||||
|
unpublished: newUnpublishedList,
|
||||||
|
pending: newPendingList,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
[ACTIONS.COLLECTION_EDIT]: (state, action) => {
|
||||||
|
const { id, collectionKey, collection } = action.data;
|
||||||
|
|
||||||
|
if (COLS.BUILTIN_LISTS.includes(id)) {
|
||||||
|
const { builtin: lists } = state;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
[collectionKey]: { ...lists, [id]: collection },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (collectionKey === 'edited') {
|
||||||
|
const { edited: lists } = state;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
edited: { ...lists, [id]: collection },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const { unpublished: lists } = state;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
unpublished: { ...lists, [id]: collection },
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
[ACTIONS.COLLECTION_ERROR]: (state, action) => {
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
error: action.data.message,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
[ACTIONS.COLLECTION_ITEMS_RESOLVE_STARTED]: (state, action) => {
|
||||||
|
const { ids } = action.data;
|
||||||
|
const { isResolvingCollectionById } = state;
|
||||||
|
const newResolving = Object.assign({}, isResolvingCollectionById);
|
||||||
|
ids.forEach(id => {
|
||||||
|
newResolving[id] = true;
|
||||||
|
});
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
...state,
|
||||||
|
error: '',
|
||||||
|
isResolvingCollectionById: newResolving,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[ACTIONS.USER_STATE_POPULATE]: (state, action) => {
|
||||||
|
const { builtinCollections, savedCollections, unpublishedCollections } = action.data;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
unpublished: unpublishedCollections || state.unpublished,
|
||||||
|
builtin: builtinCollections || state.builtin,
|
||||||
|
saved: savedCollections || state.saved,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[ACTIONS.COLLECTION_ITEMS_RESOLVE_COMPLETED]: (state, action) => {
|
||||||
|
const { resolvedCollections, failedCollectionIds } = action.data;
|
||||||
|
const { pending, edited, isResolvingCollectionById, resolved } = state;
|
||||||
|
const newPending = Object.assign({}, pending);
|
||||||
|
const newEdited = Object.assign({}, edited);
|
||||||
|
const newResolved = Object.assign({}, resolved, resolvedCollections);
|
||||||
|
|
||||||
|
const resolvedIds = Object.keys(resolvedCollections);
|
||||||
|
const newResolving = Object.assign({}, isResolvingCollectionById);
|
||||||
|
if (resolvedCollections && Object.keys(resolvedCollections).length) {
|
||||||
|
resolvedIds.forEach(resolvedId => {
|
||||||
|
if (newEdited[resolvedId]) {
|
||||||
|
if (newEdited[resolvedId]['updatedAt'] < resolvedCollections[resolvedId]['updatedAt']) {
|
||||||
|
delete newEdited[resolvedId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete newResolving[resolvedId];
|
||||||
|
if (newPending[resolvedId]) {
|
||||||
|
delete newPending[resolvedId];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (failedCollectionIds && Object.keys(failedCollectionIds).length) {
|
||||||
|
failedCollectionIds.forEach(failedId => {
|
||||||
|
delete newResolving[failedId];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
...state,
|
||||||
|
pending: newPending,
|
||||||
|
resolved: newResolved,
|
||||||
|
edited: newEdited,
|
||||||
|
isResolvingCollectionById: newResolving,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[ACTIONS.COLLECTION_ITEMS_RESOLVE_FAILED]: (state, action) => {
|
||||||
|
const { ids } = action.data;
|
||||||
|
const { isResolvingCollectionById } = state;
|
||||||
|
const newResolving = Object.assign({}, isResolvingCollectionById);
|
||||||
|
ids.forEach(id => {
|
||||||
|
delete newResolving[id];
|
||||||
|
});
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
...state,
|
||||||
|
isResolvingCollectionById: newResolving,
|
||||||
|
error: action.data.message,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultState
|
||||||
|
);
|
||||||
|
|
||||||
|
export { collectionsReducer };
|
|
@ -95,6 +95,12 @@ export const makeSelectClaimIsPending = (uri: string) =>
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const makeSelectClaimIdForUri = (uri: string) =>
|
||||||
|
createSelector(
|
||||||
|
selectClaimIdsByUri,
|
||||||
|
claimIds => claimIds[uri]
|
||||||
|
);
|
||||||
|
|
||||||
export const selectReflectingById = createSelector(
|
export const selectReflectingById = createSelector(
|
||||||
selectState,
|
selectState,
|
||||||
state => state.reflectingById
|
state => state.reflectingById
|
||||||
|
@ -531,6 +537,11 @@ export const selectFetchingMyChannels = createSelector(
|
||||||
state => state.fetchingMyChannels
|
state => state.fetchingMyChannels
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const selectFetchingMyCollections = createSelector(
|
||||||
|
selectState,
|
||||||
|
state => state.fetchingMyCollections
|
||||||
|
);
|
||||||
|
|
||||||
export const selectMyChannelClaims = createSelector(
|
export const selectMyChannelClaims = createSelector(
|
||||||
selectState,
|
selectState,
|
||||||
selectClaimsById,
|
selectClaimsById,
|
||||||
|
@ -557,6 +568,11 @@ export const selectMyChannelUrls = createSelector(
|
||||||
claims => (claims ? claims.map(claim => claim.canonical_url || claim.permanent_url) : undefined)
|
claims => (claims ? claims.map(claim => claim.canonical_url || claim.permanent_url) : undefined)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const selectMyCollectionIds = createSelector(
|
||||||
|
selectState,
|
||||||
|
state => state.myCollectionClaims
|
||||||
|
);
|
||||||
|
|
||||||
export const selectResolvingUris = createSelector(
|
export const selectResolvingUris = createSelector(
|
||||||
selectState,
|
selectState,
|
||||||
state => state.resolvingUris || []
|
state => state.resolvingUris || []
|
||||||
|
@ -917,3 +933,23 @@ export const makeSelectStakedLevelForChannelUri = (uri: string) =>
|
||||||
return level;
|
return level;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const selectUpdatingCollection = createSelector(
|
||||||
|
selectState,
|
||||||
|
state => state.updatingCollection
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectUpdateCollectionError = createSelector(
|
||||||
|
selectState,
|
||||||
|
state => state.updateCollectionError
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectCreatingCollection = createSelector(
|
||||||
|
selectState,
|
||||||
|
state => state.creatingCollection
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectCreateCollectionError = createSelector(
|
||||||
|
selectState,
|
||||||
|
state => state.createCollectionError
|
||||||
|
);
|
||||||
|
|
234
src/redux/selectors/collections.js
Normal file
234
src/redux/selectors/collections.js
Normal file
|
@ -0,0 +1,234 @@
|
||||||
|
// @flow
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
import { selectMyCollectionIds } from 'redux/selectors/claims';
|
||||||
|
import { parseURI } from 'lbryURI';
|
||||||
|
|
||||||
|
const selectState = (state: { collections: CollectionState }) => state.collections;
|
||||||
|
|
||||||
|
export const selectSavedCollectionIds = createSelector(
|
||||||
|
selectState,
|
||||||
|
collectionState => collectionState.saved
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectBuiltinCollections = createSelector(
|
||||||
|
selectState,
|
||||||
|
state => state.builtin
|
||||||
|
);
|
||||||
|
export const selectResolvedCollections = createSelector(
|
||||||
|
selectState,
|
||||||
|
state => state.resolved
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectMyUnpublishedCollections = createSelector(
|
||||||
|
selectState,
|
||||||
|
state => state.unpublished
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectMyEditedCollections = createSelector(
|
||||||
|
selectState,
|
||||||
|
state => state.edited
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectPendingCollections = createSelector(
|
||||||
|
selectState,
|
||||||
|
state => state.pending
|
||||||
|
);
|
||||||
|
|
||||||
|
export const makeSelectEditedCollectionForId = (id: string) =>
|
||||||
|
createSelector(
|
||||||
|
selectMyEditedCollections,
|
||||||
|
eLists => eLists[id]
|
||||||
|
);
|
||||||
|
|
||||||
|
export const makeSelectPendingCollectionForId = (id: string) =>
|
||||||
|
createSelector(
|
||||||
|
selectPendingCollections,
|
||||||
|
pending => pending[id]
|
||||||
|
);
|
||||||
|
|
||||||
|
export const makeSelectPublishedCollectionForId = (id: string) =>
|
||||||
|
createSelector(
|
||||||
|
selectResolvedCollections,
|
||||||
|
rLists => rLists[id]
|
||||||
|
);
|
||||||
|
|
||||||
|
export const makeSelectUnpublishedCollectionForId = (id: string) =>
|
||||||
|
createSelector(
|
||||||
|
selectMyUnpublishedCollections,
|
||||||
|
rLists => rLists[id]
|
||||||
|
);
|
||||||
|
|
||||||
|
export const makeSelectCollectionIsMine = (id: string) =>
|
||||||
|
createSelector(
|
||||||
|
selectMyCollectionIds,
|
||||||
|
selectMyUnpublishedCollections,
|
||||||
|
selectBuiltinCollections,
|
||||||
|
(publicIds, privateIds, builtinIds) => {
|
||||||
|
return Boolean(publicIds.includes(id) || privateIds[id] || builtinIds[id]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectMyPublishedCollections = createSelector(
|
||||||
|
selectResolvedCollections,
|
||||||
|
selectPendingCollections,
|
||||||
|
selectMyEditedCollections,
|
||||||
|
selectMyCollectionIds,
|
||||||
|
(resolved, pending, edited, myIds) => {
|
||||||
|
// all resolved in myIds, plus those in pending and edited
|
||||||
|
const myPublishedCollections = Object.fromEntries(
|
||||||
|
Object.entries(pending).concat(
|
||||||
|
Object.entries(resolved).filter(
|
||||||
|
([key, val]) =>
|
||||||
|
myIds.includes(key) &&
|
||||||
|
// $FlowFixMe
|
||||||
|
!pending[key]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
// now add in edited:
|
||||||
|
Object.entries(edited).forEach(([id, item]) => {
|
||||||
|
myPublishedCollections[id] = item;
|
||||||
|
});
|
||||||
|
return myPublishedCollections;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectMyPublishedMixedCollections = createSelector(
|
||||||
|
selectMyPublishedCollections,
|
||||||
|
published => {
|
||||||
|
const myCollections = Object.fromEntries(
|
||||||
|
// $FlowFixMe
|
||||||
|
Object.entries(published).filter(([key, collection]) => {
|
||||||
|
// $FlowFixMe
|
||||||
|
return collection.type === 'collection';
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return myCollections;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectMyPublishedPlaylistCollections = createSelector(
|
||||||
|
selectMyPublishedCollections,
|
||||||
|
published => {
|
||||||
|
const myCollections = Object.fromEntries(
|
||||||
|
// $FlowFixMe
|
||||||
|
Object.entries(published).filter(([key, collection]) => {
|
||||||
|
// $FlowFixMe
|
||||||
|
return collection.type === 'playlist';
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return myCollections;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const makeSelectMyPublishedCollectionForId = (id: string) =>
|
||||||
|
createSelector(
|
||||||
|
selectMyPublishedCollections,
|
||||||
|
myPublishedCollections => myPublishedCollections[id]
|
||||||
|
);
|
||||||
|
|
||||||
|
// export const selectSavedCollections = createSelector(
|
||||||
|
// selectResolvedCollections,
|
||||||
|
// selectSavedCollectionIds,
|
||||||
|
// (resolved, myIds) => {
|
||||||
|
// const mySavedCollections = Object.fromEntries(
|
||||||
|
// Object.entries(resolved).filter(([key, val]) => myIds.includes(key))
|
||||||
|
// );
|
||||||
|
// return mySavedCollections;
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
|
||||||
|
export const makeSelectIsResolvingCollectionForId = (id: string) =>
|
||||||
|
createSelector(
|
||||||
|
selectState,
|
||||||
|
state => {
|
||||||
|
return state.isResolvingCollectionById[id];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const makeSelectCollectionForId = (id: string) =>
|
||||||
|
createSelector(
|
||||||
|
selectBuiltinCollections,
|
||||||
|
selectResolvedCollections,
|
||||||
|
selectMyUnpublishedCollections,
|
||||||
|
selectMyEditedCollections,
|
||||||
|
selectPendingCollections,
|
||||||
|
(bLists, rLists, uLists, eLists, pLists) => {
|
||||||
|
const collection = bLists[id] || uLists[id] || eLists[id] || rLists[id] || pLists[id];
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const makeSelectCollectionForIdHasClaimUrl = (id: string, url: string) =>
|
||||||
|
createSelector(
|
||||||
|
makeSelectCollectionForId(id),
|
||||||
|
collection => collection && collection.items.includes(url)
|
||||||
|
);
|
||||||
|
|
||||||
|
export const makeSelectUrlsForCollectionId = (id: string) =>
|
||||||
|
createSelector(
|
||||||
|
makeSelectCollectionForId(id),
|
||||||
|
collection => collection && collection.items
|
||||||
|
);
|
||||||
|
|
||||||
|
export const makeSelectClaimIdsForCollectionId = (id: string) =>
|
||||||
|
createSelector(
|
||||||
|
makeSelectCollectionForId(id),
|
||||||
|
collection => {
|
||||||
|
const items = (collection && collection.items) || [];
|
||||||
|
const ids = items.map(item => {
|
||||||
|
const { claimId } = parseURI(item);
|
||||||
|
return claimId;
|
||||||
|
});
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const makeSelectIndexForUrlInCollection = (url: string, id: string) =>
|
||||||
|
createSelector(
|
||||||
|
makeSelectUrlsForCollectionId(id),
|
||||||
|
urls => {
|
||||||
|
const index = urls && urls.findIndex(u => u === url);
|
||||||
|
if (index > -1) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const makeSelectNextUrlForCollectionAndUrl = (id: string, url: string) =>
|
||||||
|
createSelector(
|
||||||
|
makeSelectIndexForUrlInCollection(url, id),
|
||||||
|
makeSelectUrlsForCollectionId(id),
|
||||||
|
(index, urls) => {
|
||||||
|
if (urls && index >= -1) {
|
||||||
|
const url = urls[index + 1];
|
||||||
|
if (url) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const makeSelectNameForCollectionId = (id: string) =>
|
||||||
|
createSelector(
|
||||||
|
makeSelectCollectionForId(id),
|
||||||
|
collection => {
|
||||||
|
return (collection && collection.name) || '';
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const makeSelectCountForCollectionId = (id: string) =>
|
||||||
|
createSelector(
|
||||||
|
makeSelectCollectionForId(id),
|
||||||
|
collection => {
|
||||||
|
if (collection) {
|
||||||
|
if (collection.itemCount !== undefined) {
|
||||||
|
return collection.itemCount;
|
||||||
|
}
|
||||||
|
return collection.items.length;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
);
|
Loading…
Reference in a new issue