changes after redesign merge
This commit is contained in:
parent
37ccdd4fe4
commit
12d4b6c775
13 changed files with 1294 additions and 635 deletions
1116
build/index.js
1116
build/index.js
File diff suppressed because it is too large
Load diff
|
@ -39,8 +39,6 @@ export const FETCH_TRANSACTIONS_COMPLETED = 'FETCH_TRANSACTIONS_COMPLETED';
|
|||
export const UPDATE_BALANCE = 'UPDATE_BALANCE';
|
||||
export const CHECK_ADDRESS_IS_MINE_STARTED = 'CHECK_ADDRESS_IS_MINE_STARTED';
|
||||
export const CHECK_ADDRESS_IS_MINE_COMPLETED = 'CHECK_ADDRESS_IS_MINE_COMPLETED';
|
||||
export const SET_DRAFT_TRANSACTION_AMOUNT = 'SET_DRAFT_TRANSACTION_AMOUNT';
|
||||
export const SET_DRAFT_TRANSACTION_ADDRESS = 'SET_DRAFT_TRANSACTION_ADDRESS';
|
||||
export const SEND_TRANSACTION_STARTED = 'SEND_TRANSACTION_STARTED';
|
||||
export const SEND_TRANSACTION_COMPLETED = 'SEND_TRANSACTION_COMPLETED';
|
||||
export const SEND_TRANSACTION_FAILED = 'SEND_TRANSACTION_FAILED';
|
||||
|
@ -90,9 +88,11 @@ export const FETCH_AVAILABILITY_COMPLETED = 'FETCH_AVAILABILITY_COMPLETED';
|
|||
export const FILE_DELETE = 'FILE_DELETE';
|
||||
|
||||
// Search
|
||||
export const SEARCH_STARTED = 'SEARCH_STARTED';
|
||||
export const SEARCH_COMPLETED = 'SEARCH_COMPLETED';
|
||||
export const SEARCH_CANCELLED = 'SEARCH_CANCELLED';
|
||||
export const SEARCH_START = 'SEARCH_START';
|
||||
export const SEARCH_SUCCESS = 'SEARCH_SUCCESS';
|
||||
export const SEARCH_FAIL = 'SEARCH_FAIL';
|
||||
export const UPDATE_SEARCH_QUERY = 'UPDATE_SEARCH_QUERY';
|
||||
export const UPDATE_SEARCH_SUGGESTIONS = 'UPDATE_SEARCH_SUGGESTIONS';
|
||||
|
||||
// Settings
|
||||
export const DAEMON_SETTINGS_RECEIVED = 'DAEMON_SETTINGS_RECEIVED';
|
||||
|
@ -165,6 +165,8 @@ export const CHANNEL_SUBSCRIBE = 'CHANNEL_SUBSCRIBE';
|
|||
export const CHANNEL_UNSUBSCRIBE = 'CHANNEL_UNSUBSCRIBE';
|
||||
export const HAS_FETCHED_SUBSCRIPTIONS = 'HAS_FETCHED_SUBSCRIPTIONS';
|
||||
export const SET_SUBSCRIPTION_LATEST = 'SET_SUBSCRIPTION_LATEST';
|
||||
export const SET_SUBSCRIPTION_NOTIFICATION = 'SET_SUBSCRIPTION_NOTIFICATION';
|
||||
export const SET_SUBSCRIPTION_NOTIFICATIONS = 'SET_SUBSCRIPTION_NOTIFICATIONS';
|
||||
export const CHECK_SUBSCRIPTION_STARTED = 'CHECK_SUBSCRIPTION_STARTED';
|
||||
export const CHECK_SUBSCRIPTION_COMPLETED = 'CHECK_SUBSCRIPTION_COMPLETED';
|
||||
export const CHECK_SUBSCRIPTIONS_SUBSCRIBE = 'CHECK_SUBSCRIPTIONS_SUBSCRIBE';
|
||||
|
@ -176,3 +178,13 @@ export const SET_VIDEO_PAUSE = 'SET_VIDEO_PAUSE';
|
|||
export const MEDIA_PLAY = 'MEDIA_PLAY';
|
||||
export const MEDIA_PAUSE = 'MEDIA_PAUSE';
|
||||
export const MEDIA_POSITION = 'MEDIA_POSITION';
|
||||
|
||||
// Publishing
|
||||
export const CLEAR_PUBLISH = 'CLEAR_PUBLISH';
|
||||
export const UPDATE_PUBLISH_FORM = 'UPDATE_PUBLISH_FORM';
|
||||
export const PUBLISH_START = 'PUBLISH_START';
|
||||
export const PUBLISH_SUCCESS = 'PUBLISH_SUCCESS';
|
||||
export const PUBLISH_FAIL = 'PUBLISH_FAIL';
|
||||
export const CLEAR_PUBLISH_ERROR = 'CLEAR_PUBLISH_ERROR';
|
||||
export const REMOVE_PENDING_PUBLISH = 'REMOVE_PENDING_PUBLISH';
|
||||
export const DO_PREPARE_EDIT = 'DO_PREPARE_EDIT';
|
||||
|
|
3
src/constants/search.js
Normal file
3
src/constants/search.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
export const FILE = 'file';
|
||||
export const CHANNEL = 'channel';
|
||||
export const SEARCH = 'search';
|
|
@ -43,7 +43,7 @@ export {
|
|||
doFetchFileInfosAndPublishedClaims,
|
||||
} from 'redux/actions/file_info';
|
||||
|
||||
export { doSearch } from 'redux/actions/search';
|
||||
export { doSearch, doUpdateSearchQuery } from 'redux/actions/search';
|
||||
|
||||
export {
|
||||
doUpdateBalance,
|
||||
|
@ -128,6 +128,7 @@ export {
|
|||
selectFileInfosDownloaded,
|
||||
selectDownloadingFileInfos,
|
||||
selectTotalDownloadProgress,
|
||||
selectSearchDownloadUris,
|
||||
} from 'redux/selectors/file_info';
|
||||
|
||||
export {
|
||||
|
@ -144,15 +145,17 @@ export {
|
|||
selectHistoryIndex,
|
||||
selectHistoryStack,
|
||||
selectActiveHistoryEntry,
|
||||
selectNavLinks,
|
||||
} from 'redux/selectors/navigation';
|
||||
|
||||
export {
|
||||
makeSelectSearchUris,
|
||||
selectSearchState,
|
||||
selectSearchQuery,
|
||||
selectSearchValue,
|
||||
selectIsSearching,
|
||||
selectSearchUrisByQuery,
|
||||
selectWunderBarAddress,
|
||||
selectWunderBarIcon,
|
||||
} from 'redux/selectors/search';
|
||||
|
||||
export {
|
||||
|
|
|
@ -1,68 +1,173 @@
|
|||
import * as ACTIONS from 'constants/action_types';
|
||||
import { buildURI } from 'lbryURI';
|
||||
import * as SEARCH_TYPES from 'constants/search';
|
||||
import { buildURI, normalizeURI, parseURI } from 'lbryURI';
|
||||
import { doResolveUri } from 'redux/actions/claims';
|
||||
import { selectCurrentPage } from 'redux/selectors/navigation';
|
||||
import { batchActions } from 'util/batchActions';
|
||||
import { makeSelectSearchUris } from 'redux/selectors/search';
|
||||
import handleFetchResponse from 'util/handle-fetch';
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export function doSearch(rawQuery, currentPageNotSearchHandler) {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const page = selectCurrentPage(state);
|
||||
|
||||
const query = rawQuery.replace(/^lbry:\/\//i, '');
|
||||
|
||||
if (!query) {
|
||||
dispatch({
|
||||
type: ACTIONS.SEARCH_CANCELLED,
|
||||
});
|
||||
return;
|
||||
}
|
||||
export const doSearch = rawQuery => (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const query = rawQuery.replace(/^lbry:\/\//i, '');
|
||||
|
||||
if (!query) {
|
||||
dispatch({
|
||||
type: ACTIONS.SEARCH_STARTED,
|
||||
data: { query },
|
||||
type: ACTIONS.SEARCH_FAIL,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (page !== 'search') {
|
||||
if (currentPageNotSearchHandler) {
|
||||
currentPageNotSearchHandler();
|
||||
}
|
||||
} else {
|
||||
fetch(`https://lighthouse.lbry.io/search?s=${query}`)
|
||||
.then(
|
||||
response =>
|
||||
response.status === 200
|
||||
? Promise.resolve(response.json())
|
||||
: Promise.reject(new Error(response.statusText))
|
||||
)
|
||||
.then(data => {
|
||||
const uris = [];
|
||||
const actions = [];
|
||||
// If we have already searched for something, we don't need to do anything
|
||||
const urisForQuery = makeSelectSearchUris(query)(state);
|
||||
if (urisForQuery && !!urisForQuery.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
data.forEach(result => {
|
||||
const uri = buildURI({
|
||||
name: result.name,
|
||||
claimId: result.claimId,
|
||||
});
|
||||
actions.push(doResolveUri(uri));
|
||||
uris.push(uri);
|
||||
});
|
||||
dispatch({
|
||||
type: ACTIONS.SEARCH_START,
|
||||
});
|
||||
|
||||
actions.push({
|
||||
type: ACTIONS.SEARCH_COMPLETED,
|
||||
data: {
|
||||
query,
|
||||
uris,
|
||||
},
|
||||
});
|
||||
dispatch(batchActions(...actions));
|
||||
})
|
||||
.catch(() => {
|
||||
dispatch({
|
||||
type: ACTIONS.SEARCH_CANCELLED,
|
||||
});
|
||||
// If the user is on the file page with a pre-populated uri and they select
|
||||
// the search option without typing anything, searchQuery will be empty
|
||||
// We need to populate it so the input is filled on the search page
|
||||
if (!state.search.searchQuery) {
|
||||
dispatch({
|
||||
type: ACTIONS.UPDATE_SEARCH_QUERY,
|
||||
data: { searchQuery: query },
|
||||
});
|
||||
}
|
||||
|
||||
fetch(`https://lighthouse.lbry.io/search?s=${query}`)
|
||||
.then(handleFetchResponse)
|
||||
.then(data => {
|
||||
const uris = [];
|
||||
const actions = [];
|
||||
|
||||
data.forEach(result => {
|
||||
const uri = buildURI({
|
||||
claimName: result.name,
|
||||
claimId: result.claimId,
|
||||
});
|
||||
actions.push(doResolveUri(uri));
|
||||
uris.push(uri);
|
||||
});
|
||||
|
||||
actions.push({
|
||||
type: ACTIONS.SEARCH_SUCCESS,
|
||||
data: {
|
||||
query,
|
||||
uris,
|
||||
},
|
||||
});
|
||||
dispatch(batchActions(...actions));
|
||||
})
|
||||
.catch(() => {
|
||||
dispatch({
|
||||
type: ACTIONS.SEARCH_FAIL,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const doUpdateSearchQuery = (query: string, shouldSkipSuggestions: ?boolean) => dispatch => {
|
||||
dispatch({
|
||||
type: ACTIONS.UPDATE_SEARCH_QUERY,
|
||||
data: { query },
|
||||
});
|
||||
|
||||
// Don't fetch new suggestions if the user just added a space
|
||||
if (!query.endsWith(' ') || !shouldSkipSuggestions) {
|
||||
dispatch(getSearchSuggestions(query));
|
||||
}
|
||||
};
|
||||
|
||||
export const getSearchSuggestions = (value: string) => dispatch => {
|
||||
const query = value.trim();
|
||||
|
||||
const isPrefix = () =>
|
||||
query === '@' || query === 'lbry:' || query === 'lbry:/' || query === 'lbry://';
|
||||
|
||||
if (!query || isPrefix()) {
|
||||
dispatch({
|
||||
type: ACTIONS.UPDATE_SEARCH_SUGGESTIONS,
|
||||
data: { suggestions: [] },
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let suggestions = [];
|
||||
try {
|
||||
// If the user is about to manually add the claim id ignore it until they
|
||||
// actually add one. This would hardly ever happen, but then the search
|
||||
// suggestions won't change just from adding a '#' after a uri
|
||||
let uriQuery = query;
|
||||
if (uriQuery.endsWith('#')) {
|
||||
uriQuery = uriQuery.slice(0, -1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const uri = normalizeURI(uriQuery);
|
||||
const { claimName, isChannel } = parseURI(uri);
|
||||
|
||||
suggestions.push(
|
||||
{
|
||||
value: uri,
|
||||
shorthand: isChannel ? claimName.slice(1) : claimName,
|
||||
type: isChannel ? SEARCH_TYPES.CHANNEL : SEARCH_TYPES.FILE,
|
||||
},
|
||||
{
|
||||
value: claimName,
|
||||
type: SEARCH_TYPES.SEARCH,
|
||||
}
|
||||
);
|
||||
|
||||
// If it's a valid url, don't fetch any extra search results
|
||||
return dispatch({
|
||||
type: ACTIONS.UPDATE_SEARCH_SUGGESTIONS,
|
||||
data: { suggestions },
|
||||
});
|
||||
} catch (e) {
|
||||
suggestions.push({
|
||||
value: query,
|
||||
type: SEARCH_TYPES.SEARCH,
|
||||
});
|
||||
}
|
||||
|
||||
// Populate the current search query suggestion before fetching results
|
||||
dispatch({
|
||||
type: ACTIONS.UPDATE_SEARCH_SUGGESTIONS,
|
||||
data: { suggestions },
|
||||
});
|
||||
|
||||
// strip out any basic stuff for more accurate search results
|
||||
let searchValue = value.replace(/lbry:\/\//g, '').replace(/-/g, ' ');
|
||||
if (searchValue.includes('#')) {
|
||||
// This should probably be more robust, but I think it's fine for now
|
||||
// Remove everything after # to get rid of the claim id
|
||||
searchValue = searchValue.substring(0, searchValue.indexOf('#'));
|
||||
}
|
||||
|
||||
return fetch(`https://lighthouse.lbry.io/autocomplete?s=${searchValue}`)
|
||||
.then(handleFetchResponse)
|
||||
.then(apiSuggestions => {
|
||||
const formattedSuggestions = apiSuggestions.slice(0, 6).map(suggestion => {
|
||||
// This will need to be more robust when the api starts returning lbry uris
|
||||
const isChannel = suggestion.startsWith('@');
|
||||
const suggestionObj = {
|
||||
value: isChannel ? `lbry://${suggestion}` : suggestion,
|
||||
shorthand: isChannel ? suggestion.slice(1) : '',
|
||||
type: isChannel ? 'channel' : 'search',
|
||||
};
|
||||
|
||||
return suggestionObj;
|
||||
});
|
||||
|
||||
suggestions = suggestions.concat(formattedSuggestions);
|
||||
dispatch({
|
||||
type: ACTIONS.UPDATE_SEARCH_SUGGESTIONS,
|
||||
data: { suggestions },
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
// If the fetch fails, do nothing
|
||||
// Basic search suggestions are already populated at this point
|
||||
});
|
||||
};
|
||||
|
|
|
@ -34,7 +34,7 @@ export function doFetchTransactions() {
|
|||
type: ACTIONS.FETCH_TRANSACTIONS_STARTED,
|
||||
});
|
||||
|
||||
Lbry.transaction_list({ include_tip_info: true }).then(results => {
|
||||
Lbry.transaction_list().then(results => {
|
||||
dispatch({
|
||||
type: ACTIONS.FETCH_TRANSACTIONS_COMPLETED,
|
||||
data: {
|
||||
|
@ -62,8 +62,8 @@ export function doGetNewAddress() {
|
|||
type: ACTIONS.GET_NEW_ADDRESS_STARTED,
|
||||
});
|
||||
|
||||
// Removed localStorage use, since address is expected to be stored in redux store
|
||||
Lbry.wallet_new_address().then(address => {
|
||||
// localStorage.setItem('wallet_address', address);
|
||||
dispatch({
|
||||
type: ACTIONS.GET_NEW_ADDRESS_COMPLETED,
|
||||
data: { address },
|
||||
|
|
|
@ -1,32 +1,103 @@
|
|||
// @flow
|
||||
import * as ACTIONS from 'constants/action_types';
|
||||
import { handleActions } from 'util/redux-utils';
|
||||
|
||||
const reducers = {};
|
||||
const defaultState = {
|
||||
type SearchSuccess = {
|
||||
type: ACTIONS.SEARCH_SUCCESS,
|
||||
data: {
|
||||
query: string,
|
||||
uris: Array<string>,
|
||||
},
|
||||
};
|
||||
|
||||
type UpdateSearchQuery = {
|
||||
type: ACTIONS.UPDATE_SEARCH_QUERY,
|
||||
data: {
|
||||
query: string,
|
||||
},
|
||||
};
|
||||
|
||||
type SearchSuggestion = {
|
||||
value: string,
|
||||
shorthand: string,
|
||||
type: string,
|
||||
};
|
||||
|
||||
type UpdateSearchSuggestions = {
|
||||
type: ACTIONS.UPDATE_SEARCH_SUGGESTIONS,
|
||||
data: {
|
||||
suggestions: Array<SearchSuggestion>,
|
||||
},
|
||||
};
|
||||
|
||||
type SearchState = {
|
||||
isActive: boolean,
|
||||
searchQuery: string,
|
||||
suggestions: Array<SearchSuggestion>,
|
||||
urisByQuery: {},
|
||||
searching: false,
|
||||
};
|
||||
|
||||
reducers[ACTIONS.SEARCH_STARTED] = state =>
|
||||
Object.assign({}, state, {
|
||||
searching: true,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.SEARCH_COMPLETED] = (state, action) => {
|
||||
const { query, uris } = action.data;
|
||||
|
||||
return Object.assign({}, state, {
|
||||
searching: false,
|
||||
urisByQuery: Object.assign({}, state.urisByQuery, { [query]: uris }),
|
||||
});
|
||||
const defaultState = {
|
||||
isActive: false,
|
||||
searchQuery: '', // needs to be an empty string for input focusing
|
||||
suggestions: [],
|
||||
urisByQuery: {},
|
||||
};
|
||||
|
||||
reducers[ACTIONS.SEARCH_CANCELLED] = state =>
|
||||
Object.assign({}, state, {
|
||||
searching: false,
|
||||
});
|
||||
export const searchReducer = handleActions(
|
||||
{
|
||||
[ACTIONS.SEARCH_START]: (state: SearchState): SearchState => ({
|
||||
...state,
|
||||
searching: true,
|
||||
}),
|
||||
[ACTIONS.SEARCH_SUCCESS]: (state: SearchState, action: SearchSuccess): SearchState => {
|
||||
const { query, uris } = action.data;
|
||||
|
||||
export function searchReducer(state = defaultState, action) {
|
||||
const handler = reducers[action.type];
|
||||
if (handler) return handler(state, action);
|
||||
return state;
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
searching: false,
|
||||
urisByQuery: Object.assign({}, state.urisByQuery, { [query]: uris }),
|
||||
};
|
||||
},
|
||||
|
||||
[ACTIONS.SEARCH_FAIL]: (state: SearchState): SearchState => ({
|
||||
...state,
|
||||
searching: false,
|
||||
}),
|
||||
|
||||
[ACTIONS.UPDATE_SEARCH_QUERY]: (
|
||||
state: SearchState,
|
||||
action: UpdateSearchQuery
|
||||
): SearchState => ({
|
||||
...state,
|
||||
searchQuery: action.data.query,
|
||||
isActive: true,
|
||||
}),
|
||||
|
||||
[ACTIONS.UPDATE_SEARCH_SUGGESTIONS]: (
|
||||
state: SearchState,
|
||||
action: UpdateSearchSuggestions
|
||||
): SearchState => ({
|
||||
...state,
|
||||
suggestions: action.data.suggestions,
|
||||
}),
|
||||
|
||||
// clear the searchQuery on back/forward
|
||||
// it may be populated by the page title for search/file pages
|
||||
// if going home, it should be blank
|
||||
[ACTIONS.HISTORY_NAVIGATE]: (state: SearchState): SearchState => ({
|
||||
...state,
|
||||
searchQuery: '',
|
||||
suggestions: [],
|
||||
isActive: false,
|
||||
}),
|
||||
// sets isActive to false so the uri will be populated correctly if the
|
||||
// user is on a file page. The search query will still be present on any
|
||||
// other page
|
||||
[ACTIONS.CLOSE_MODAL]: (state: SearchState): SearchState => ({
|
||||
...state,
|
||||
isActive: false,
|
||||
}),
|
||||
},
|
||||
defaultState
|
||||
);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import * as ACTIONS from 'constants/action_types';
|
||||
|
||||
const reducers = {};
|
||||
const receiveAddress = null; // localStorage.getItem('receiveAddress');
|
||||
const buildDraftTransaction = () => ({
|
||||
amount: undefined,
|
||||
address: undefined,
|
||||
|
@ -12,7 +11,7 @@ const defaultState = {
|
|||
blocks: {},
|
||||
transactions: {},
|
||||
fetchingTransactions: false,
|
||||
receiveAddress,
|
||||
receiveAddress: null,
|
||||
gettingNewAddress: false,
|
||||
draftTransaction: buildDraftTransaction(),
|
||||
sendingSupport: false,
|
||||
|
@ -46,7 +45,7 @@ reducers[ACTIONS.GET_NEW_ADDRESS_STARTED] = state =>
|
|||
reducers[ACTIONS.GET_NEW_ADDRESS_COMPLETED] = (state, action) => {
|
||||
const { address } = action.data;
|
||||
|
||||
// localStorage.setItem('receiveAddress', address);
|
||||
// Say no to localStorage!
|
||||
return Object.assign({}, state, {
|
||||
gettingNewAddress: false,
|
||||
receiveAddress: address,
|
||||
|
|
|
@ -3,6 +3,7 @@ import {
|
|||
selectIsFetchingClaimListMine,
|
||||
selectMyClaims,
|
||||
} from 'redux/selectors/claims';
|
||||
import { buildURI } from 'lbryURI';
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
export const selectState = state => state.fileInfo || {};
|
||||
|
@ -27,7 +28,6 @@ export const makeSelectFileInfoForUri = uri =>
|
|||
createSelector(selectClaimsByUri, selectFileInfosByOutpoint, (claims, byOutpoint) => {
|
||||
const claim = claims[uri];
|
||||
const outpoint = claim ? `${claim.txid}:${claim.nout}` : undefined;
|
||||
|
||||
return outpoint ? byOutpoint[outpoint] : undefined;
|
||||
});
|
||||
|
||||
|
@ -104,3 +104,92 @@ export const selectTotalDownloadProgress = createSelector(selectDownloadingFileI
|
|||
if (fileInfos.length > 0) return totalProgress / fileInfos.length / 100.0;
|
||||
return -1;
|
||||
});
|
||||
|
||||
export const selectSearchDownloadUris = query =>
|
||||
createSelector(selectFileInfosDownloaded, fileInfos => {
|
||||
if (!query || !fileInfos.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const queryParts = query.toLowerCase().split(' ');
|
||||
const searchQueryDictionary = {};
|
||||
queryParts.forEach(subQuery => {
|
||||
searchQueryDictionary[subQuery] = subQuery;
|
||||
});
|
||||
|
||||
const arrayContainsQueryPart = array => {
|
||||
for (let i = 0; i < array.length; i += 1) {
|
||||
const subQuery = array[i];
|
||||
if (searchQueryDictionary[subQuery]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const downloadResultsFromQuery = [];
|
||||
fileInfos.forEach(fileInfo => {
|
||||
const { channel_name, claim_name, metadata } = fileInfo;
|
||||
const { author, description, title } = metadata;
|
||||
|
||||
if (channel_name) {
|
||||
const channelName = channel_name.toLowerCase();
|
||||
const strippedOutChannelName = channelName.slice(1); // trim off the @
|
||||
if (searchQueryDictionary[channel_name] || searchQueryDictionary[strippedOutChannelName]) {
|
||||
downloadResultsFromQuery.push(fileInfo);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const nameParts = claim_name.toLowerCase().split('-');
|
||||
if (arrayContainsQueryPart(nameParts)) {
|
||||
downloadResultsFromQuery.push(fileInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
const titleParts = title.toLowerCase().split(' ');
|
||||
if (arrayContainsQueryPart(titleParts)) {
|
||||
downloadResultsFromQuery.push(fileInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
if (author) {
|
||||
const authorParts = author.toLowerCase().split(' ');
|
||||
if (arrayContainsQueryPart(authorParts)) {
|
||||
downloadResultsFromQuery.push(fileInfo);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (description) {
|
||||
const descriptionParts = description.toLowerCase().split(' ');
|
||||
if (arrayContainsQueryPart(descriptionParts)) {
|
||||
downloadResultsFromQuery.push(fileInfo);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return downloadResultsFromQuery.length
|
||||
? downloadResultsFromQuery.map(fileInfo => {
|
||||
const {
|
||||
channel_name: channelName,
|
||||
claim_id: claimId,
|
||||
claim_name: claimName,
|
||||
value,
|
||||
metadata,
|
||||
} = fileInfo;
|
||||
const uriParams = {};
|
||||
|
||||
if (channelName) {
|
||||
uriParams.channelName = channelName;
|
||||
}
|
||||
|
||||
uriParams.claimId = claimId;
|
||||
uriParams.claimId = claimId;
|
||||
uriParams.contentName = claimName;
|
||||
|
||||
const uri = buildURI(uriParams);
|
||||
return uri;
|
||||
})
|
||||
: null;
|
||||
});
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { createSelector } from 'reselect';
|
||||
import { normalizeURI } from 'lbryURI';
|
||||
import { parseQueryParams, toQueryString } from 'util/query_params';
|
||||
import { parseQueryParams } from 'util/query_params';
|
||||
|
||||
export const selectState = state => state.navigation || {};
|
||||
|
||||
|
@ -22,103 +21,6 @@ export const selectCurrentParams = createSelector(selectCurrentPath, path => {
|
|||
export const makeSelectCurrentParam = param =>
|
||||
createSelector(selectCurrentParams, params => (params ? params[param] : undefined));
|
||||
|
||||
export const selectHeaderLinks = createSelector(selectCurrentPage, page => {
|
||||
// This contains intentional fall throughs
|
||||
switch (page) {
|
||||
case 'wallet':
|
||||
case 'history':
|
||||
case 'send':
|
||||
case 'getcredits':
|
||||
case 'invite':
|
||||
case 'rewards':
|
||||
case 'backup':
|
||||
return {
|
||||
wallet: __('Overview'),
|
||||
getcredits: __('Get Credits'),
|
||||
send: __('Send / Receive'),
|
||||
rewards: __('Rewards'),
|
||||
invite: __('Invites'),
|
||||
history: __('History'),
|
||||
};
|
||||
case 'downloaded':
|
||||
case 'published':
|
||||
return {
|
||||
downloaded: __('Downloaded'),
|
||||
published: __('Published'),
|
||||
};
|
||||
case 'settings':
|
||||
case 'help':
|
||||
return {
|
||||
settings: __('Settings'),
|
||||
help: __('Help'),
|
||||
};
|
||||
case 'discover':
|
||||
case 'subscriptions':
|
||||
return {
|
||||
discover: __('Discover'),
|
||||
subscriptions: __('Subscriptions'),
|
||||
};
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
export const selectPageTitle = createSelector(
|
||||
selectCurrentPage,
|
||||
selectCurrentParams,
|
||||
(page, params) => {
|
||||
switch (page) {
|
||||
case 'settings':
|
||||
return __('Settings');
|
||||
case 'report':
|
||||
return __('Report');
|
||||
case 'wallet':
|
||||
return __('Wallet');
|
||||
case 'send':
|
||||
return __('Send or Receive LBRY Credits');
|
||||
case 'getcredits':
|
||||
return __('Get LBRY Credits');
|
||||
case 'backup':
|
||||
return __('Backup Your Wallet');
|
||||
case 'rewards':
|
||||
return __('Rewards');
|
||||
case 'invite':
|
||||
return __('Invites');
|
||||
case 'start':
|
||||
return __('Start');
|
||||
case 'publish':
|
||||
return params.id ? __('Edit') : __('Publish');
|
||||
case 'help':
|
||||
return __('Help');
|
||||
case 'developer':
|
||||
return __('Developer');
|
||||
case 'show': {
|
||||
const parts = [normalizeURI(params.uri)];
|
||||
// If the params has any keys other than "uri"
|
||||
if (Object.keys(params).length > 1) {
|
||||
parts.push(toQueryString(Object.assign({}, params, { uri: null })));
|
||||
}
|
||||
return parts.join('?');
|
||||
}
|
||||
case 'downloaded':
|
||||
return __('Downloads & Purchases');
|
||||
case 'published':
|
||||
return __('Publications');
|
||||
case 'search':
|
||||
return params.query ? __('Search results for %s', params.query) : __('Search');
|
||||
case 'subscriptions':
|
||||
return __('Your Subscriptions');
|
||||
case 'discover':
|
||||
case false:
|
||||
case null:
|
||||
case '':
|
||||
return '';
|
||||
default:
|
||||
return page[0].toUpperCase() + (page.length > 0 ? page.substr(1) : '');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const selectPathAfterAuth = createSelector(selectState, state => state.pathAfterAuth);
|
||||
|
||||
export const selectIsBackDisabled = createSelector(selectState, state => state.index === 0);
|
||||
|
@ -128,6 +30,8 @@ export const selectIsForwardDisabled = createSelector(
|
|||
state => state.index === state.stack.length - 1
|
||||
);
|
||||
|
||||
export const selectIsHome = createSelector(selectCurrentPage, page => page === 'discover');
|
||||
|
||||
export const selectHistoryIndex = createSelector(selectState, state => state.index);
|
||||
|
||||
export const selectHistoryStack = createSelector(selectState, state => state.stack);
|
||||
|
@ -137,3 +41,162 @@ export const selectActiveHistoryEntry = createSelector(
|
|||
selectState,
|
||||
state => state.stack[state.index]
|
||||
);
|
||||
|
||||
export const selectPageTitle = createSelector(selectCurrentPage, page => {
|
||||
switch (page) {
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
});
|
||||
|
||||
export const selectNavLinks = createSelector(
|
||||
selectCurrentPage,
|
||||
selectHistoryStack,
|
||||
(currentPage, historyStack) => {
|
||||
const isWalletPage = page =>
|
||||
page === 'wallet' ||
|
||||
page === 'send' ||
|
||||
page === 'getcredits' ||
|
||||
page === 'rewards' ||
|
||||
page === 'history' ||
|
||||
page === 'invite';
|
||||
|
||||
const isMyLbryPage = page =>
|
||||
page === 'downloaded' || page === 'published' || page === 'settings';
|
||||
|
||||
const previousStack = historyStack.slice().reverse();
|
||||
|
||||
const getPreviousSubLinkPath = checkIfValidPage => {
|
||||
for (let i = 0; i < previousStack.length; i += 1) {
|
||||
const currentStackItem = previousStack[i];
|
||||
|
||||
// Trim off the "/" from the path
|
||||
const pageInStack = currentStackItem.path.slice(1);
|
||||
if (checkIfValidPage(pageInStack)) {
|
||||
return currentStackItem.path;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
// Gets the last active sublink in a section
|
||||
const getActiveSublink = category => {
|
||||
if (category === 'wallet') {
|
||||
const previousPath = getPreviousSubLinkPath(isWalletPage);
|
||||
return previousPath || '/wallet';
|
||||
} else if (category === 'myLbry') {
|
||||
const previousPath = getPreviousSubLinkPath(isMyLbryPage);
|
||||
return previousPath || '/downloaded';
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const isCurrentlyWalletPage = isWalletPage(currentPage);
|
||||
const isCurrentlyMyLbryPage = isMyLbryPage(currentPage);
|
||||
|
||||
const walletSubLinks = [
|
||||
{
|
||||
label: 'Overview',
|
||||
path: '/wallet',
|
||||
active: currentPage === 'wallet',
|
||||
},
|
||||
{
|
||||
label: 'Send & Recieve',
|
||||
path: '/send',
|
||||
active: currentPage === 'send',
|
||||
},
|
||||
{
|
||||
label: 'Get Credits',
|
||||
path: '/getcredits',
|
||||
active: currentPage === 'getcredits',
|
||||
},
|
||||
{
|
||||
label: 'Rewards',
|
||||
path: '/rewards',
|
||||
active: currentPage === 'rewards',
|
||||
},
|
||||
{
|
||||
label: 'Invites',
|
||||
path: '/invite',
|
||||
active: currentPage === 'invite',
|
||||
},
|
||||
{
|
||||
label: 'Transactions',
|
||||
path: '/history',
|
||||
active: currentPage === 'history',
|
||||
},
|
||||
];
|
||||
|
||||
const myLbrySubLinks = [
|
||||
{
|
||||
label: 'Downloads',
|
||||
path: '/downloaded',
|
||||
active: currentPage === 'downloaded',
|
||||
},
|
||||
{
|
||||
label: 'Publishes',
|
||||
path: '/published',
|
||||
active: currentPage === 'published',
|
||||
},
|
||||
{
|
||||
label: 'Settings',
|
||||
path: '/settings',
|
||||
active: currentPage === 'settings',
|
||||
},
|
||||
{
|
||||
label: 'Backup',
|
||||
path: '/backup',
|
||||
active: currentPage === 'backup',
|
||||
},
|
||||
];
|
||||
|
||||
const navLinks = {
|
||||
primary: [
|
||||
{
|
||||
label: 'Explore',
|
||||
path: '/discover',
|
||||
active: currentPage === 'discover',
|
||||
icon: 'Compass',
|
||||
},
|
||||
{
|
||||
label: 'Subscriptions',
|
||||
path: '/subscriptions',
|
||||
active: currentPage === 'subscriptions',
|
||||
icon: 'AtSign',
|
||||
},
|
||||
],
|
||||
secondary: [
|
||||
{
|
||||
label: 'Wallet',
|
||||
icon: 'CreditCard',
|
||||
subLinks: walletSubLinks,
|
||||
path: isCurrentlyWalletPage ? '/wallet' : getActiveSublink('wallet'),
|
||||
active: isWalletPage(currentPage),
|
||||
},
|
||||
{
|
||||
label: 'My LBRY',
|
||||
icon: 'Settings',
|
||||
subLinks: myLbrySubLinks,
|
||||
path: isCurrentlyMyLbryPage ? '/downloaded' : getActiveSublink('myLbry'),
|
||||
active: isMyLbryPage(currentPage),
|
||||
},
|
||||
{
|
||||
label: 'Publish',
|
||||
icon: 'UploadCloud',
|
||||
path: '/publish',
|
||||
active: currentPage === 'publish',
|
||||
},
|
||||
{
|
||||
label: 'Help',
|
||||
path: '/help',
|
||||
active: currentPage === 'help',
|
||||
icon: 'HelpCircle',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
return navLinks;
|
||||
}
|
||||
);
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import {
|
||||
selectCurrentPage,
|
||||
selectCurrentParams,
|
||||
selectPageTitle,
|
||||
} from 'redux/selectors/navigation';
|
||||
import { selectCurrentPage, selectCurrentParams } from 'redux/selectors/navigation';
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
export const selectState = state => state.search || {};
|
||||
|
||||
export const selectSearchState = selectState;
|
||||
|
||||
export const selectSearchValue = createSelector(selectState, state => state.searchQuery);
|
||||
|
||||
export const selectSearchQuery = createSelector(
|
||||
selectCurrentPage,
|
||||
selectCurrentParams,
|
||||
|
@ -26,54 +26,14 @@ export const makeSelectSearchUris = query =>
|
|||
|
||||
export const selectWunderBarAddress = createSelector(
|
||||
selectCurrentPage,
|
||||
selectPageTitle,
|
||||
selectSearchQuery,
|
||||
(page, title, query) => (page !== 'search' ? title : query || title)
|
||||
);
|
||||
|
||||
export const selectWunderBarIcon = createSelector(
|
||||
selectCurrentPage,
|
||||
selectCurrentParams,
|
||||
(page, params) => {
|
||||
switch (page) {
|
||||
case 'auth':
|
||||
return 'icon-user';
|
||||
case 'settings':
|
||||
return 'icon-gear';
|
||||
case 'help':
|
||||
return 'icon-question';
|
||||
case 'report':
|
||||
return 'icon-file';
|
||||
case 'downloaded':
|
||||
return 'icon-folder';
|
||||
case 'published':
|
||||
return 'icon-folder';
|
||||
case 'history':
|
||||
return 'icon-history';
|
||||
case 'send':
|
||||
return 'icon-send';
|
||||
case 'rewards':
|
||||
return 'icon-rocket';
|
||||
case 'invite':
|
||||
return 'icon-envelope-open';
|
||||
case 'getcredits':
|
||||
return 'icon-shopping-cart';
|
||||
case 'wallet':
|
||||
case 'backup':
|
||||
return 'icon-bank';
|
||||
case 'show':
|
||||
return 'icon-file';
|
||||
case 'publish':
|
||||
return params.id ? __('icon-pencil') : __('icon-upload');
|
||||
case 'developer':
|
||||
return 'icon-code';
|
||||
case 'discover':
|
||||
case 'search':
|
||||
return 'icon-search';
|
||||
case 'subscriptions':
|
||||
return 'icon-th-list';
|
||||
default:
|
||||
return 'icon-file';
|
||||
(page, query, params) => {
|
||||
// only populate the wunderbar address if we are on the file/channel pages
|
||||
// or show the search query
|
||||
if (page === 'show') {
|
||||
return params.uri;
|
||||
}
|
||||
return query;
|
||||
}
|
||||
);
|
||||
|
|
5
src/util/handle-fetch.js
Normal file
5
src/util/handle-fetch.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
export default function handleFetchResponse(response) {
|
||||
return response.status === 200
|
||||
? Promise.resolve(response.json())
|
||||
: Promise.reject(new Error(response.statusText));
|
||||
}
|
17
src/util/redux-utils.js
Normal file
17
src/util/redux-utils.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
// util for creating reducers
|
||||
// based off of redux-actions
|
||||
// https://redux-actions.js.org/docs/api/handleAction.html#handleactions
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const handleActions = (actionMap, defaultState) => (state = defaultState, action) => {
|
||||
const handler = actionMap[action.type];
|
||||
|
||||
if (handler) {
|
||||
const newState = handler(state, action);
|
||||
return Object.assign({}, state, newState);
|
||||
}
|
||||
|
||||
// just return the original state if no handler
|
||||
// returning a copy here breaks redux-persist
|
||||
return state;
|
||||
};
|
Loading…
Add table
Reference in a new issue