wip
wip clean clean review wip wallet sync wip collection publishing build refactor, publishing, pending, editing wip wip fetch collections on resolve select collections or playlists build return edit success fix collection claimId selector small rename flow type fixes collection edit params type param and flowtypes
This commit is contained in:
parent
757e8c24ec
commit
8d0f9c18fd
17 changed files with 1650 additions and 18 deletions
80
dist/flow-typed/Claim.js
vendored
80
dist/flow-typed/Claim.js
vendored
|
@ -1,11 +1,15 @@
|
|||
// @flow
|
||||
|
||||
declare type Claim = StreamClaim | ChannelClaim;
|
||||
declare type Claim = StreamClaim | ChannelClaim | CollectionClaim;
|
||||
|
||||
declare type ChannelClaim = GenericClaim & {
|
||||
value: ChannelMetadata,
|
||||
};
|
||||
|
||||
declare type CollectionClaim = GenericClaim & {
|
||||
value: CollectionMetadata,
|
||||
};
|
||||
|
||||
declare type StreamClaim = GenericClaim & {
|
||||
value: StreamMetadata,
|
||||
};
|
||||
|
@ -30,7 +34,7 @@ declare type GenericClaim = {
|
|||
short_url: string, // permanent_url with short id, no channel
|
||||
txid: string, // unique tx id
|
||||
type: 'claim' | 'update' | 'support',
|
||||
value_type: 'stream' | 'channel',
|
||||
value_type: 'stream' | 'channel' | 'collection',
|
||||
signing_channel?: ChannelClaim,
|
||||
reposted_claim?: GenericClaim,
|
||||
repost_channel_url?: string,
|
||||
|
@ -74,6 +78,10 @@ declare type ChannelMetadata = GenericMetadata & {
|
|||
featured?: Array<string>,
|
||||
};
|
||||
|
||||
declare type CollectionMetadata = GenericMetadata & {
|
||||
claims: Array<string>,
|
||||
}
|
||||
|
||||
declare type StreamMetadata = GenericMetadata & {
|
||||
license?: string, // License "title" ex: Creative Commons, Custom copyright
|
||||
license_url?: string, // Link to full license
|
||||
|
@ -136,3 +144,71 @@ declare type PurchaseReceipt = {
|
|||
txid: string,
|
||||
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>,
|
||||
}
|
||||
|
|
40
dist/flow-typed/Collections.js
vendored
Normal file
40
dist/flow-typed/Collections.js
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
declare type CollectionUpdateParams = {
|
||||
remove?: boolean,
|
||||
claims?: Array<Claim>,
|
||||
name?: string,
|
||||
order?: { from: number, to: number },
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
35
dist/flow-typed/Lbry.js
vendored
35
dist/flow-typed/Lbry.js
vendored
|
@ -170,6 +170,37 @@ declare type ChannelSignResponse = {
|
|||
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 = {
|
||||
items: Array<FileListItem>,
|
||||
page: number,
|
||||
|
@ -288,6 +319,10 @@ declare type LbryTypes = {
|
|||
support_abandon: (params: {}) => Promise<SupportAbandonResponse>,
|
||||
stream_repost: (params: StreamRepostOptions) => Promise<StreamRepostResponse>,
|
||||
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_list: (params: {}) => Promise<FileListResponse>,
|
||||
|
|
80
flow-typed/Claim.js
vendored
80
flow-typed/Claim.js
vendored
|
@ -1,11 +1,15 @@
|
|||
// @flow
|
||||
|
||||
declare type Claim = StreamClaim | ChannelClaim;
|
||||
declare type Claim = StreamClaim | ChannelClaim | CollectionClaim;
|
||||
|
||||
declare type ChannelClaim = GenericClaim & {
|
||||
value: ChannelMetadata,
|
||||
};
|
||||
|
||||
declare type CollectionClaim = GenericClaim & {
|
||||
value: CollectionMetadata,
|
||||
};
|
||||
|
||||
declare type StreamClaim = GenericClaim & {
|
||||
value: StreamMetadata,
|
||||
};
|
||||
|
@ -30,7 +34,7 @@ declare type GenericClaim = {
|
|||
short_url: string, // permanent_url with short id, no channel
|
||||
txid: string, // unique tx id
|
||||
type: 'claim' | 'update' | 'support',
|
||||
value_type: 'stream' | 'channel',
|
||||
value_type: 'stream' | 'channel' | 'collection',
|
||||
signing_channel?: ChannelClaim,
|
||||
reposted_claim?: GenericClaim,
|
||||
repost_channel_url?: string,
|
||||
|
@ -74,6 +78,10 @@ declare type ChannelMetadata = GenericMetadata & {
|
|||
featured?: Array<string>,
|
||||
};
|
||||
|
||||
declare type CollectionMetadata = GenericMetadata & {
|
||||
claims: Array<string>,
|
||||
}
|
||||
|
||||
declare type StreamMetadata = GenericMetadata & {
|
||||
license?: string, // License "title" ex: Creative Commons, Custom copyright
|
||||
license_url?: string, // Link to full license
|
||||
|
@ -136,3 +144,71 @@ declare type PurchaseReceipt = {
|
|||
txid: string,
|
||||
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>,
|
||||
}
|
||||
|
|
41
flow-typed/Collections.js
vendored
Normal file
41
flow-typed/Collections.js
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
declare type CollectionUpdateParams = {
|
||||
remove?: boolean,
|
||||
claims?: Array<Claim>,
|
||||
name?: string,
|
||||
order?: { from: number, to: number },
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
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 = {
|
||||
items: Array<FileListItem>,
|
||||
page: number,
|
||||
|
@ -288,6 +319,10 @@ declare type LbryTypes = {
|
|||
support_abandon: (params: {}) => Promise<SupportAbandonResponse>,
|
||||
stream_repost: (params: StreamRepostOptions) => Promise<StreamRepostResponse>,
|
||||
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_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_COMPLETED = 'FETCH_CHANNEL_LIST_COMPLETED';
|
||||
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_COMPLETED = 'CREATE_CHANNEL_COMPLETED';
|
||||
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_COMPLETED = 'IMPORT_CHANNEL_COMPLETED';
|
||||
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_COMPLETED = 'PUBLISH_COMPLETED';
|
||||
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_FAILED = 'CLAIM_REPOST_FAILED';
|
||||
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_COMPLETED = 'CHECK_PUBLISH_NAME_COMPLETED';
|
||||
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_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
|
||||
export const COMMENT_LIST_STARTED = 'COMMENT_LIST_STARTED';
|
||||
export const COMMENT_LIST_COMPLETED = 'COMMENT_LIST_COMPLETED';
|
||||
|
|
5
src/constants/collections.js
Normal file
5
src/constants/collections.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
export const COLLECTION_ID = 'colid';
|
||||
export const COLLECTION_INDEX = 'colindex';
|
||||
|
||||
export const WATCH_LATER_ID = 'watchlater';
|
||||
export const FAVORITES_ID = 'favorites';
|
42
src/index.js
42
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 DAEMON_SETTINGS from 'constants/daemon_settings';
|
||||
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 Lbry, { apiCall } from 'lbry';
|
||||
import LbryFirst from 'lbry-first';
|
||||
|
@ -35,6 +36,7 @@ export {
|
|||
MATURE_TAGS,
|
||||
SPEECH_URLS,
|
||||
SHARED_PREFERENCES,
|
||||
COLLECTIONS_CONSTS,
|
||||
};
|
||||
|
||||
// common
|
||||
|
@ -57,6 +59,13 @@ export { buildSharedStateMiddleware } from 'redux/middleware/shared-state';
|
|||
|
||||
// actions
|
||||
export { doToast, doDismissToast, doError, doDismissError } from 'redux/actions/notifications';
|
||||
export {
|
||||
doLocalCollectionCreate,
|
||||
doFetchItemsInCollection,
|
||||
doFetchItemsInCollections,
|
||||
doCollectionEdit,
|
||||
doLocalCollectionDelete,
|
||||
} from 'redux/actions/collections';
|
||||
|
||||
export {
|
||||
doFetchClaimsByChannel,
|
||||
|
@ -66,6 +75,7 @@ export {
|
|||
doResolveUris,
|
||||
doResolveUri,
|
||||
doFetchChannelListMine,
|
||||
doFetchCollectionListMine,
|
||||
doCreateChannel,
|
||||
doUpdateChannel,
|
||||
doClaimSearch,
|
||||
|
@ -76,6 +86,8 @@ export {
|
|||
doCheckPublishNameAvailability,
|
||||
doPurchaseList,
|
||||
doCheckPendingClaims,
|
||||
doCollectionPublish,
|
||||
doCollectionPublishUpdate,
|
||||
} from 'redux/actions/claims';
|
||||
|
||||
export { doClearPurchasedUriSuccess, doPurchaseUri, doFileGet } from 'redux/actions/file';
|
||||
|
@ -140,11 +152,34 @@ export { fileInfoReducer } from 'redux/reducers/file_info';
|
|||
export { notificationsReducer } from 'redux/reducers/notifications';
|
||||
export { publishReducer } from 'redux/reducers/publish';
|
||||
export { walletReducer } from 'redux/reducers/wallet';
|
||||
export { collectionsReducer } from 'redux/reducers/collections';
|
||||
|
||||
// selectors
|
||||
export { makeSelectContentPositionForUri } from 'redux/selectors/content';
|
||||
|
||||
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,
|
||||
makeSelectIsResolvingCollectionForId,
|
||||
makeSelectNextUrlForCollection,
|
||||
} from 'redux/selectors/collections';
|
||||
|
||||
export {
|
||||
makeSelectClaimForUri,
|
||||
|
@ -209,6 +244,8 @@ export {
|
|||
selectAllMyClaimsByOutpoint,
|
||||
selectMyClaimsOutpoints,
|
||||
selectFetchingMyChannels,
|
||||
selectFetchingMyCollections,
|
||||
selectMyCollectionIds,
|
||||
selectMyChannelClaims,
|
||||
selectResolvingUris,
|
||||
selectPlayingUri,
|
||||
|
@ -237,6 +274,11 @@ export {
|
|||
selectFetchingMyPurchasesError,
|
||||
selectMyPurchasesCount,
|
||||
selectPurchaseUriSuccess,
|
||||
makeSelectClaimIdForUri,
|
||||
selectUpdatingCollection,
|
||||
selectUpdateCollectionError,
|
||||
selectCreatingCollection,
|
||||
selectCreateCollectionError,
|
||||
} from 'redux/selectors/claims';
|
||||
|
||||
export {
|
||||
|
|
|
@ -90,6 +90,10 @@ const Lbry: LbryTypes = {
|
|||
support_create: params => daemonCallWithResult('support_create', params),
|
||||
support_list: params => daemonCallWithResult('support_list', 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_list: (params = {}) => daemonCallWithResult('file_list', params),
|
||||
|
|
|
@ -11,13 +11,20 @@ import {
|
|||
selectMyChannelClaims,
|
||||
selectPendingIds,
|
||||
selectClaimsById,
|
||||
makeSelectClaimForClaimId,
|
||||
} from 'redux/selectors/claims';
|
||||
|
||||
import { doFetchTxoPage } from 'redux/actions/wallet';
|
||||
import { selectSupportsByOutpoint } from 'redux/selectors/wallet';
|
||||
import { creditsToString } from 'util/format-credits';
|
||||
import { batchActions } from 'util/batch-actions';
|
||||
import { createNormalizedClaimSearchKey } from 'util/claim';
|
||||
import { PAGE_SIZE } from 'constants/claim';
|
||||
import {
|
||||
makeSelectEditedCollectionForId,
|
||||
selectPendingCollections,
|
||||
} from 'redux/selectors/collections';
|
||||
import { doFetchItemsInCollection, doFetchItemsInCollections } from 'redux/actions/collections';
|
||||
|
||||
type ResolveEntries = Array<[string, GenericClaim]>;
|
||||
|
||||
|
@ -61,12 +68,16 @@ export function doResolveUris(
|
|||
stream: ?StreamClaim,
|
||||
channel: ?ChannelClaim,
|
||||
claimsInChannel: ?number,
|
||||
collection: ?CollectionClaim,
|
||||
},
|
||||
} = {};
|
||||
|
||||
const collectionIds: Array<string> = [];
|
||||
|
||||
return Lbry.resolve({ urls: urisToResolve, ...options }).then(
|
||||
async(result: ResolveResponse) => {
|
||||
let repostedResults = {};
|
||||
const collectionClaimIdsToResolve = [];
|
||||
const repostsToResolve = [];
|
||||
const fallbackResolveInfo = {
|
||||
stream: null,
|
||||
|
@ -80,6 +91,7 @@ export function doResolveUris(
|
|||
// https://github.com/facebook/flow/issues/2221
|
||||
if (uriResolveInfo) {
|
||||
if (uriResolveInfo.error) {
|
||||
// $FlowFixMe
|
||||
resolveInfo[uri] = { ...fallbackResolveInfo };
|
||||
} else {
|
||||
if (checkReposts) {
|
||||
|
@ -96,6 +108,10 @@ export function doResolveUris(
|
|||
result.channel = uriResolveInfo;
|
||||
// $FlowFixMe
|
||||
result.claimsInChannel = uriResolveInfo.meta.claims_in_channel;
|
||||
} else if (uriResolveInfo.value_type === 'collection') {
|
||||
result.collection = uriResolveInfo;
|
||||
// $FlowFixMe
|
||||
collectionIds.push(uriResolveInfo.claim_id);
|
||||
} else {
|
||||
result.stream = uriResolveInfo;
|
||||
if (uriResolveInfo.signing_channel) {
|
||||
|
@ -127,6 +143,13 @@ export function doResolveUris(
|
|||
type: ACTIONS.RESOLVE_URIS_COMPLETED,
|
||||
data: { resolveInfo },
|
||||
});
|
||||
|
||||
if (collectionIds.length) {
|
||||
dispatch(doFetchItemsInCollections({ collectionIds: collectionIds, pageSize: 5 }));
|
||||
}
|
||||
// now collection claims are added, get their stuff
|
||||
// if collections: doResolveCollections(claimIds)
|
||||
|
||||
return result;
|
||||
}
|
||||
);
|
||||
|
@ -573,11 +596,43 @@ 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,
|
||||
})
|
||||
);
|
||||
// update or fetch collections?
|
||||
};
|
||||
|
||||
const failure = error => {
|
||||
dispatch({
|
||||
type: ACTIONS.FETCH_COLLECTION_LIST_FAILED,
|
||||
data: error,
|
||||
});
|
||||
};
|
||||
|
||||
Lbry.collection_list({ page, page_size: pageSize, resolve_claims: 1 }).then(callback, failure);
|
||||
};
|
||||
}
|
||||
|
||||
export function doClaimSearch(
|
||||
options: {
|
||||
page_size: number,
|
||||
page: number,
|
||||
no_totals: boolean,
|
||||
no_totals?: boolean,
|
||||
any_tags?: Array<string>,
|
||||
claim_ids?: Array<string>,
|
||||
channel_ids?: Array<string>,
|
||||
|
@ -618,7 +673,8 @@ export function doClaimSearch(
|
|||
pageSize: options.page_size,
|
||||
},
|
||||
});
|
||||
return true;
|
||||
// was return true
|
||||
return resolveInfo;
|
||||
};
|
||||
|
||||
const failure = err => {
|
||||
|
@ -679,6 +735,128 @@ 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<string>,
|
||||
languages?: Array<string>,
|
||||
claims: Array<string>,
|
||||
},
|
||||
localId: string
|
||||
) {
|
||||
return (dispatch: Dispatch) => {
|
||||
// $FlowFixMe
|
||||
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,
|
||||
},
|
||||
// shift unpublished collection to pending collection with new publish id
|
||||
// recent publish won't resolve this second. handle it in checkPending
|
||||
{
|
||||
type: ACTIONS.COLLECTION_PENDING,
|
||||
data: { localId: localId, claimId: collectionClaim.claim_id },
|
||||
},
|
||||
{
|
||||
type: ACTIONS.UPDATE_PENDING_CLAIMS,
|
||||
data: {
|
||||
claims: [collectionClaim],
|
||||
},
|
||||
}
|
||||
)
|
||||
);
|
||||
dispatch(doCheckPendingClaims());
|
||||
dispatch(doFetchCollectionListMine(1, 10));
|
||||
resolve(collectionClaim);
|
||||
}
|
||||
|
||||
function failure(error) {
|
||||
dispatch({
|
||||
type: ACTIONS.COLLECTION_PUBLISH_FAILED,
|
||||
data: {
|
||||
error: error.message,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
Lbry.collection_create(options).then(success, failure);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function doCollectionPublishUpdate(options: {
|
||||
bid?: string,
|
||||
blocking?: true,
|
||||
title?: string,
|
||||
thumbnail_url?: string,
|
||||
description?: string,
|
||||
claim_id: string,
|
||||
tags?: Array<string>,
|
||||
languages?: Array<string>,
|
||||
}) {
|
||||
return (dispatch: Dispatch, getState: GetState) => {
|
||||
const state = getState();
|
||||
// select claim forclaim_id
|
||||
// get publish params from claim
|
||||
// $FlowFixMe
|
||||
|
||||
const collectionClaim = makeSelectClaimForClaimId(options.claim_id)(state);
|
||||
// TODO: add old claim entries to params
|
||||
const editItems = makeSelectEditedCollectionForId(options.claim_id)(state);
|
||||
const oldParams: CollectionUpdateParams = {
|
||||
bid: collectionClaim.amount,
|
||||
};
|
||||
// $FlowFixMe
|
||||
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.UPDATE_PENDING_CLAIMS,
|
||||
data: {
|
||||
claims: [collectionClaim],
|
||||
},
|
||||
});
|
||||
dispatch(doFetchCollectionListMine(1, 10));
|
||||
resolve(collectionClaim);
|
||||
}
|
||||
|
||||
function failure(error) {
|
||||
dispatch({
|
||||
type: ACTIONS.COLLECTION_PUBLISH_UPDATE_FAILED,
|
||||
data: {
|
||||
error: error.message,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
Lbry.collection_update(options).then(success, failure);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function doCheckPublishNameAvailability(name: string) {
|
||||
return (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
|
@ -750,6 +928,7 @@ export const doCheckPendingClaims = (onConfirmed: Function) => (
|
|||
const checkClaimList = () => {
|
||||
const state = getState();
|
||||
const pendingIdSet = new Set(selectPendingIds(state));
|
||||
const pendingCollections = selectPendingCollections(state);
|
||||
Lbry.claim_list({ page: 1, page_size: 10 })
|
||||
.then(result => {
|
||||
const claims = result.items;
|
||||
|
@ -758,6 +937,9 @@ export const doCheckPendingClaims = (onConfirmed: Function) => (
|
|||
const { claim_id: claimId } = claim;
|
||||
if (claim.confirmations > 0 && pendingIdSet.has(claimId)) {
|
||||
pendingIdSet.delete(claimId);
|
||||
if (Object.keys(pendingCollections).includes(claim.claim_id)) {
|
||||
dispatch(doFetchItemsInCollection({ collectionId: claim.claim_id }));
|
||||
}
|
||||
claimsToConfirm.push(claim);
|
||||
if (onConfirmed) {
|
||||
onConfirmed(claim);
|
||||
|
|
459
src/redux/actions/collections.js
Normal file
459
src/redux/actions/collections.js
Normal file
|
@ -0,0 +1,459 @@
|
|||
// @flow
|
||||
import * as ACTIONS from 'constants/action_types';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import Lbry from 'lbry';
|
||||
import { doClaimSearch } from 'redux/actions/claims';
|
||||
import { makeSelectClaimForClaimId } from 'redux/selectors/claims';
|
||||
import {
|
||||
makeSelectCollectionForId,
|
||||
// makeSelectPublishedCollectionForId, // for "save" or "copy" action
|
||||
makeSelectMyPublishedCollectionForId,
|
||||
makeSelectUnpublishedCollectionForId,
|
||||
makeSelectEditedCollectionForId,
|
||||
} from 'redux/selectors/collections';
|
||||
const WATCH_LATER_ID = 'watchlater';
|
||||
const FAVORITES_ID = 'favorites';
|
||||
|
||||
const BUILTIN_LISTS = [WATCH_LATER_ID, FAVORITES_ID];
|
||||
|
||||
const getTimestamp = () => {
|
||||
return Math.floor(Date.now() / 1000);
|
||||
};
|
||||
|
||||
// maybe take items param
|
||||
export const doLocalCollectionCreate = (
|
||||
name: string,
|
||||
collectionItems: 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,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const doLocalCollectionDelete = (id: string) => (dispatch: Dispatch) => {
|
||||
return dispatch({
|
||||
type: ACTIONS.COLLECTION_DELETE,
|
||||
data: {
|
||||
id: id,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 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) => {
|
||||
let state = getState();
|
||||
// for each collection id,
|
||||
// make sure the collection is resolved, the items are resolved, and build the collection objects
|
||||
|
||||
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) {
|
||||
let claimSearchOptions = { claim_ids: collectionIdsToSearch, page: 1, page_size: 9999 };
|
||||
await dispatch(doClaimSearch(claimSearchOptions));
|
||||
}
|
||||
const invalidCollectionIds = [];
|
||||
const stateAfterClaimSearch = getState();
|
||||
|
||||
async function resolveCollectionItems(claimId, totalItems, pageSize) {
|
||||
// take [ {}, {} ], return {}
|
||||
// only need items [ Claim... ] and total_items
|
||||
const mergeResults = (arrayOfResults: Array<{ items: any, total_items: number }>) => {
|
||||
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;
|
||||
});
|
||||
return mergedResults;
|
||||
};
|
||||
|
||||
try {
|
||||
const BATCH_SIZE = 10; // up batch size when sdk bug fixed
|
||||
const batches = [];
|
||||
let fetchResult;
|
||||
if (!pageSize) {
|
||||
// batch all
|
||||
for (let i = 0; i < Math.ceil(totalItems / BATCH_SIZE); i++) {
|
||||
batches[i] = Lbry.collection_resolve({
|
||||
claim_id: claimId,
|
||||
page: i + 1,
|
||||
page_size: BATCH_SIZE,
|
||||
});
|
||||
}
|
||||
const resultArray = await Promise.all(batches);
|
||||
fetchResult = mergeResults(resultArray);
|
||||
} else {
|
||||
fetchResult = await Lbry.collection_resolve({
|
||||
claim_id: claimId,
|
||||
page: 1,
|
||||
page_size: pageSize,
|
||||
});
|
||||
}
|
||||
// $FlowFixMe
|
||||
const itemsById: { claimId: string, items?: ?Array<GenericClaim> } = { claimId: claimId };
|
||||
if (fetchResult.items) {
|
||||
itemsById.items = fetchResult.items;
|
||||
} else {
|
||||
itemsById.items = null;
|
||||
}
|
||||
return itemsById;
|
||||
} catch (e) {
|
||||
return {
|
||||
claimId: claimId,
|
||||
items: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const promises = [];
|
||||
collectionIds.forEach(collectionId => {
|
||||
const claim = makeSelectClaimForClaimId(collectionId)(stateAfterClaimSearch);
|
||||
if (!claim) {
|
||||
invalidCollectionIds.push(collectionId);
|
||||
} else {
|
||||
const claimCount = claim.value.claims && claim.value.claims.length;
|
||||
if (pageSize) {
|
||||
promises.push(resolveCollectionItems(collectionId, claimCount, pageSize));
|
||||
} else {
|
||||
promises.push(resolveCollectionItems(collectionId, claimCount));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// $FlowFixMe
|
||||
const resolvedCollectionItemsById: Array<{
|
||||
claimId: string,
|
||||
items: ?Array<GenericClaim>,
|
||||
}> = await Promise.all(promises);
|
||||
|
||||
function processClaims(resultClaimsByUri) {
|
||||
const processedClaims = {};
|
||||
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 {
|
||||
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
|
||||
processedClaims[uri] = result;
|
||||
}
|
||||
});
|
||||
return processedClaims;
|
||||
}
|
||||
|
||||
const newCollectionItemsById = {};
|
||||
const flatResolvedCollectionItems = {};
|
||||
resolvedCollectionItemsById.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 } = claim || {};
|
||||
const valueTypes = new Set();
|
||||
const streamTypes = new Set();
|
||||
|
||||
let items = [];
|
||||
collectionItems.forEach(collectionItem => {
|
||||
// here's where we would just items.push(collectionItem.permanent_url
|
||||
items.push(collectionItem.permanent_url);
|
||||
valueTypes.add(collectionItem.value_type);
|
||||
if (collectionItem.value.stream_type) {
|
||||
streamTypes.add(collectionItem.value.stream_type);
|
||||
}
|
||||
flatResolvedCollectionItems[collectionItem.canonical_url] = collectionItem;
|
||||
});
|
||||
const 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'))));
|
||||
|
||||
newCollectionItemsById[collectionId] = {
|
||||
items,
|
||||
id: collectionId,
|
||||
name: name,
|
||||
itemCount: claim.value.claims.length,
|
||||
type: isPlaylist ? 'playlist' : 'collection',
|
||||
updatedAt: timestamp,
|
||||
};
|
||||
// clear any stale edits
|
||||
if (editedCollection && timestamp > editedCollection['updatedAt']) {
|
||||
dispatch({
|
||||
type: ACTIONS.COLLECTION_DELETE,
|
||||
data: {
|
||||
id: collectionId,
|
||||
collectionKey: 'edited',
|
||||
},
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// no collection items? probably in pending.
|
||||
}
|
||||
});
|
||||
const processedClaimsByUri = processClaims(flatResolvedCollectionItems);
|
||||
|
||||
dispatch({
|
||||
type: ACTIONS.RESOLVE_URIS_COMPLETED,
|
||||
data: { resolveInfo: processedClaimsByUri },
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: ACTIONS.COLLECTION_ITEMS_RESOLVE_COMPLETED,
|
||||
data: {
|
||||
resolvedCollections: newCollectionItemsById,
|
||||
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 = makeSelectMyPublishedCollectionForId(collectionId)(state);
|
||||
|
||||
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';
|
||||
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);
|
||||
}
|
||||
|
||||
if (editedCollection) {
|
||||
if (publishedCollection.items.join(',') === newItems.join(',')) {
|
||||
// delete edited if newItems are the same as publishedItems
|
||||
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 (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,
|
||||
app_welcome_version?: number,
|
||||
sharing_3P?: boolean,
|
||||
unpublishedCollectionTest: CollectionGroup,
|
||||
builtinCollectionTest: CollectionGroup,
|
||||
savedCollectionTest: Array<string>,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -27,6 +30,9 @@ function extractUserState(rawObj: SharedData) {
|
|||
settings,
|
||||
app_welcome_version,
|
||||
sharing_3P,
|
||||
unpublishedCollectionTest,
|
||||
builtinCollectionTest,
|
||||
savedCollectionTest,
|
||||
} = rawObj.value;
|
||||
|
||||
return {
|
||||
|
@ -38,6 +44,9 @@ function extractUserState(rawObj: SharedData) {
|
|||
...(settings ? { settings } : {}),
|
||||
...(app_welcome_version ? { app_welcome_version } : {}),
|
||||
...(sharing_3P ? { sharing_3P } : {}),
|
||||
...(unpublishedCollectionTest ? { unpublishedCollectionTest } : {}),
|
||||
...(builtinCollectionTest ? { builtinCollectionTest } : {}),
|
||||
...(savedCollectionTest ? { savedCollectionTest } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -55,6 +64,9 @@ export function doPopulateSharedUserState(sharedSettings: any) {
|
|||
settings,
|
||||
app_welcome_version,
|
||||
sharing_3P,
|
||||
unpublishedCollectionTest,
|
||||
builtinCollectionTest,
|
||||
savedCollectionTest,
|
||||
} = extractUserState(sharedSettings);
|
||||
dispatch({
|
||||
type: ACTIONS.USER_STATE_POPULATE,
|
||||
|
@ -67,6 +79,9 @@ export function doPopulateSharedUserState(sharedSettings: any) {
|
|||
settings,
|
||||
welcomeVersion: app_welcome_version,
|
||||
allowAnalytics: sharing_3P,
|
||||
unpublishedCollectionTest,
|
||||
builtinCollectionTest,
|
||||
savedCollectionTest,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
|
@ -13,6 +13,7 @@ import mergeClaim from 'util/merge-claim';
|
|||
|
||||
type State = {
|
||||
createChannelError: ?string,
|
||||
createCollectionError: ?string,
|
||||
channelClaimCounts: { [string]: number },
|
||||
claimsByUri: { [string]: string },
|
||||
byId: { [string]: Claim },
|
||||
|
@ -21,9 +22,11 @@ type State = {
|
|||
reflectingById: { [string]: ReflectingUpdate },
|
||||
myClaims: ?Array<string>,
|
||||
myChannelClaims: ?Array<string>,
|
||||
myCollectionClaims: ?Array<string>,
|
||||
abandoningById: { [string]: boolean },
|
||||
fetchingChannelClaims: { [string]: number },
|
||||
fetchingMyChannels: boolean,
|
||||
fetchingMyCollections: boolean,
|
||||
fetchingClaimSearchByQuery: { [string]: boolean },
|
||||
purchaseUriSuccess: boolean,
|
||||
myPurchases: ?Array<string>,
|
||||
|
@ -34,6 +37,7 @@ type State = {
|
|||
claimSearchByQuery: { [string]: Array<string> },
|
||||
claimSearchByQueryLastPageReached: { [string]: Array<boolean> },
|
||||
creatingChannel: boolean,
|
||||
creatingCollection: boolean,
|
||||
paginatedClaimsByChannel: {
|
||||
[string]: {
|
||||
all: Array<string>,
|
||||
|
@ -43,7 +47,9 @@ type State = {
|
|||
},
|
||||
},
|
||||
updateChannelError: ?string,
|
||||
updateCollectionError: ?string,
|
||||
updatingChannel: boolean,
|
||||
updatingCollection: boolean,
|
||||
pendingChannelImport: string | boolean,
|
||||
repostLoading: boolean,
|
||||
repostError: ?string,
|
||||
|
@ -66,6 +72,7 @@ const defaultState = {
|
|||
fetchingChannelClaims: {},
|
||||
resolvingUris: [],
|
||||
myChannelClaims: undefined,
|
||||
myCollectionClaims: [],
|
||||
myClaims: undefined,
|
||||
myPurchases: undefined,
|
||||
myPurchasesPageNumber: undefined,
|
||||
|
@ -74,6 +81,7 @@ const defaultState = {
|
|||
fetchingMyPurchases: false,
|
||||
fetchingMyPurchasesError: undefined,
|
||||
fetchingMyChannels: false,
|
||||
fetchingMyCollections: false,
|
||||
abandoningById: {},
|
||||
pendingIds: [],
|
||||
reflectingById: {},
|
||||
|
@ -82,9 +90,13 @@ const defaultState = {
|
|||
claimSearchByQueryLastPageReached: {},
|
||||
fetchingClaimSearchByQuery: {},
|
||||
updateChannelError: '',
|
||||
updateCollectionError: '',
|
||||
updatingChannel: false,
|
||||
creatingChannel: false,
|
||||
createChannelError: undefined,
|
||||
updatingCollection: false,
|
||||
creatingCollection: false,
|
||||
createCollectionError: undefined,
|
||||
pendingChannelImport: false,
|
||||
repostLoading: false,
|
||||
repostError: undefined,
|
||||
|
@ -100,15 +112,7 @@ const defaultState = {
|
|||
};
|
||||
|
||||
function handleClaimAction(state: State, action: any): State {
|
||||
const {
|
||||
resolveInfo,
|
||||
}: {
|
||||
[string]: {
|
||||
stream: ?StreamClaim,
|
||||
channel: ?ChannelClaim,
|
||||
claimsInChannel: ?number,
|
||||
},
|
||||
} = action.data;
|
||||
const { resolveInfo }: ClaimActionResolveInfo = action.data;
|
||||
|
||||
const byUri = Object.assign({}, state.claimsByUri);
|
||||
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]) => {
|
||||
// $FlowFixMe
|
||||
const { claimsInChannel, stream, channel: channelFromResolve } = resolveResponse;
|
||||
const { claimsInChannel, stream, channel: channelFromResolve, collection } = resolveResponse;
|
||||
const channel = channelFromResolve || (stream && stream.signing_channel);
|
||||
|
||||
if (stream) {
|
||||
|
@ -165,8 +169,29 @@ function handleClaimAction(state: State, action: any): State {
|
|||
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;
|
||||
|
||||
// If url isn't a canonical_url, make sure that is added too
|
||||
byUri[collection.canonical_url] = collection.claim_id;
|
||||
|
||||
// Also add the permanent_url here until lighthouse returns canonical_url for search results
|
||||
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);
|
||||
if (!stream && !channel && !pendingIds.includes(byUri[url])) {
|
||||
if (!stream && !channel && !collection && !pendingIds.includes(byUri[url])) {
|
||||
byUri[url] = null;
|
||||
}
|
||||
});
|
||||
|
@ -306,6 +331,57 @@ 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 myCollectionClaims;
|
||||
const byId = Object.assign({}, state.byId);
|
||||
const byUri = Object.assign({}, state.claimsByUri);
|
||||
|
||||
if (!claims.length) {
|
||||
// $FlowFixMe
|
||||
myCollectionClaims = null;
|
||||
} else {
|
||||
myCollectionClaims = new Set(state.myCollectionClaims);
|
||||
claims.forEach(claim => {
|
||||
const { meta } = claim;
|
||||
const { canonical_url: canonicalUrl, permanent_url: permanentUrl, claim_id: claimId } = claim;
|
||||
// maybe add info about items in collection
|
||||
|
||||
byUri[canonicalUrl] = claimId;
|
||||
byUri[permanentUrl] = claimId;
|
||||
|
||||
// $FlowFixMe
|
||||
myCollectionClaims.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: myCollectionClaims ? Array.from(myCollectionClaims) : null,
|
||||
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 => {
|
||||
const { uri, page } = action.data;
|
||||
const fetchingChannelClaims = Object.assign({}, state.fetchingChannelClaims);
|
||||
|
@ -520,6 +596,54 @@ 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 => {
|
||||
return Object.assign({}, state, {
|
||||
creatingCollection: false,
|
||||
});
|
||||
};
|
||||
|
||||
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,
|
||||
});
|
||||
};
|
||||
|
||||
// COLLECTION_PUBLISH_ABANDON_...
|
||||
|
||||
reducers[ACTIONS.IMPORT_CHANNEL_STARTED] = (state: State): State =>
|
||||
Object.assign({}, state, { pendingChannelImports: true });
|
||||
|
||||
|
|
235
src/redux/reducers/collections.js
Normal file
235
src/redux/reducers/collections.js
Normal file
|
@ -0,0 +1,235 @@
|
|||
// @flow
|
||||
import { handleActions } from 'util/redux-utils';
|
||||
import * as ACTIONS from 'constants/action_types';
|
||||
import * as COLLECTION_CONSTS from 'constants/collections';
|
||||
|
||||
const WATCH_LATER_ID = 'watchlater';
|
||||
const FAVORITES_ID = 'favorites';
|
||||
|
||||
const BUILTIN_LISTS = [WATCH_LATER_ID, FAVORITES_ID];
|
||||
const getTimestamp = () => {
|
||||
return Math.floor(Date.now() / 1000);
|
||||
};
|
||||
|
||||
const defaultState: CollectionState = {
|
||||
builtin: {
|
||||
watchlater: {
|
||||
items: ['lbry://seriouspublish#c1b740eb88f96b465f65e5f1542564539df1c62e'],
|
||||
id: WATCH_LATER_ID,
|
||||
name: 'Watch Later',
|
||||
updatedAt: getTimestamp(),
|
||||
type: 'playlist',
|
||||
},
|
||||
favorites: {
|
||||
items: ['lbry://seriouspublish#c1b740eb88f96b465f65e5f1542564539df1c62e'],
|
||||
id: FAVORITES_ID,
|
||||
name: 'Favorites',
|
||||
type: 'collection',
|
||||
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 || 'mixed',
|
||||
};
|
||||
|
||||
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]) {
|
||||
delete state[collectionKey][id];
|
||||
} 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);
|
||||
|
||||
const isEdit = editList[localId];
|
||||
if (localId) {
|
||||
// pending from unpublished -> published
|
||||
// delete from local
|
||||
newPendingList[claimId] = Object.assign(
|
||||
{},
|
||||
newEditList[localId] || newUnpublishedList[localId] || {}
|
||||
);
|
||||
if (isEdit) {
|
||||
delete newEditList[localId];
|
||||
} else {
|
||||
delete newUnpublishedList[localId];
|
||||
}
|
||||
} else {
|
||||
// pending from edited published -> published
|
||||
if (isEdit) {
|
||||
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 (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 { builtinCollectionTest, savedCollectionTest, unpublishedCollectionTest } = action.data;
|
||||
return {
|
||||
...state,
|
||||
unpublished: unpublishedCollectionTest || state.unpublished,
|
||||
builtin: builtinCollectionTest || state.builtin,
|
||||
saved: savedCollectionTest || state.saved,
|
||||
};
|
||||
},
|
||||
[ACTIONS.COLLECTION_ITEMS_RESOLVE_COMPLETED]: (state, action) => {
|
||||
const { resolvedCollections, failedCollectionIds } = action.data;
|
||||
const {
|
||||
pending: pendingList,
|
||||
edited: editList,
|
||||
isResolvingCollectionById,
|
||||
resolved: lists,
|
||||
} = state;
|
||||
const resolvedIds = Object.keys(resolvedCollections);
|
||||
const newResolving = Object.assign({}, isResolvingCollectionById);
|
||||
if (resolvedCollections && resolvedCollections.length) {
|
||||
resolvedIds.forEach(resolvedId => {
|
||||
if (editList[resolvedId]) {
|
||||
if (editList[resolvedId]['updatedAt'] < resolvedCollections[resolvedId]['updatedAt']) {
|
||||
delete editList[resolvedId];
|
||||
}
|
||||
}
|
||||
delete newResolving[resolvedId];
|
||||
if (pendingList[resolvedId]) {
|
||||
delete pendingList[resolvedId];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (failedCollectionIds && failedCollectionIds.length) {
|
||||
failedCollectionIds.forEach(failedId => {
|
||||
delete newResolving[failedId];
|
||||
});
|
||||
}
|
||||
|
||||
const newLists = Object.assign({}, lists, resolvedCollections);
|
||||
|
||||
return Object.assign({}, state, {
|
||||
...state,
|
||||
pending: pendingList,
|
||||
resolved: newLists,
|
||||
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(
|
||||
selectState,
|
||||
state => state.reflectingById
|
||||
|
@ -531,6 +537,11 @@ export const selectFetchingMyChannels = createSelector(
|
|||
state => state.fetchingMyChannels
|
||||
);
|
||||
|
||||
export const selectFetchingMyCollections = createSelector(
|
||||
selectState,
|
||||
state => state.fetchingMyCollections
|
||||
);
|
||||
|
||||
export const selectMyChannelClaims = createSelector(
|
||||
selectState,
|
||||
selectClaimsById,
|
||||
|
@ -557,6 +568,11 @@ export const selectMyChannelUrls = createSelector(
|
|||
claims => (claims ? claims.map(claim => claim.canonical_url || claim.permanent_url) : undefined)
|
||||
);
|
||||
|
||||
export const selectMyCollectionIds = createSelector(
|
||||
selectState,
|
||||
state => state.myCollectionClaims
|
||||
);
|
||||
|
||||
export const selectResolvingUris = createSelector(
|
||||
selectState,
|
||||
state => state.resolvingUris || []
|
||||
|
@ -917,3 +933,23 @@ export const makeSelectStakedLevelForChannelUri = (uri: string) =>
|
|||
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
|
||||
);
|
||||
|
|
203
src/redux/selectors/collections.js
Normal file
203
src/redux/selectors/collections.js
Normal file
|
@ -0,0 +1,203 @@
|
|||
// @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]);
|
||||
}
|
||||
);
|
||||
|
||||
// for library page, we want all published
|
||||
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(edited)
|
||||
.filter(
|
||||
([key, val]) => myIds.includes(key)
|
||||
// $FlowFixMe
|
||||
)
|
||||
.concat(
|
||||
Object.entries(resolved).filter(
|
||||
([key, val]) =>
|
||||
myIds.includes(key) &&
|
||||
// $FlowFixMe
|
||||
(!pending[key] && !edited[key])
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
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 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 makeSelectNextUrlForCollection = (id: string, index: number) =>
|
||||
createSelector(
|
||||
makeSelectUrlsForCollectionId(id),
|
||||
urls => {
|
||||
const url = urls[index + 1];
|
||||
if (url) {
|
||||
return url;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
export const makeSelectNameForCollectionId = (id: string) =>
|
||||
createSelector(
|
||||
makeSelectCollectionForId(id),
|
||||
collection => {
|
||||
return (collection && collection.name) || '';
|
||||
}
|
||||
);
|
Loading…
Reference in a new issue