use canonical_url everywhere and update url parse/build functions to work with canonical_url's properly
This commit is contained in:
parent
4f812db1c7
commit
f5289f9811
17 changed files with 515 additions and 346 deletions
|
@ -6,6 +6,7 @@
|
||||||
./flow-typed
|
./flow-typed
|
||||||
|
|
||||||
[options]
|
[options]
|
||||||
|
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe
|
||||||
module.system.node.resolve_dirname=./src
|
module.system.node.resolve_dirname=./src
|
||||||
module.name_mapper='^redux\(.*\)$' -> '<PROJECT_ROOT>/src/redux\1'
|
module.name_mapper='^redux\(.*\)$' -> '<PROJECT_ROOT>/src/redux\1'
|
||||||
module.name_mapper='^util\(.*\)$' -> '<PROJECT_ROOT>/src/util\1'
|
module.name_mapper='^util\(.*\)$' -> '<PROJECT_ROOT>/src/util\1'
|
||||||
|
|
362
dist/bundle.es.js
vendored
362
dist/bundle.es.js
vendored
|
@ -897,45 +897,49 @@ const getSearchQueryString = (query, options = {}, includeUserOptions = false) =
|
||||||
|
|
||||||
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
||||||
|
|
||||||
|
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
|
||||||
|
|
||||||
|
//
|
||||||
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
const channelNameMinLength = 1;
|
const channelNameMinLength = 1;
|
||||||
const claimIdMaxLength = 40;
|
const claimIdMaxLength = 40;
|
||||||
|
|
||||||
// see https://spec.lbry.com/#urls
|
// see https://spec.lbry.com/#urls
|
||||||
const regexInvalidURI = /[ =&#:$@%?;/\\"<>%{}|^~[\]`\u{0000}-\u{0008}\u{000b}-\u{000c}\u{000e}-\u{001F}\u{D800}-\u{DFFF}\u{FFFE}-\u{FFFF}]/u;
|
const regexInvalidURI = /[ =&#:$@%?;/\\"<>%{}|^~[\]`\u{0000}-\u{0008}\u{000b}-\u{000c}\u{000e}-\u{001F}\u{D800}-\u{DFFF}\u{FFFE}-\u{FFFF}]/u;
|
||||||
const regexAddress = /^(b|r)(?=[^0OIl]{32,33})[0-9A-Za-z]{32,33}$/;
|
const regexAddress = /^(b|r)(?=[^0OIl]{32,33})[0-9A-Za-z]{32,33}$/;
|
||||||
|
const regexPartProtocol = '^((?:lbry://)?)';
|
||||||
|
const regexPartStreamOrChannelName = '([^:$#/]*)';
|
||||||
|
const regexPartModifierSeparator = '([:$#]?)([^/]*)';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a LBRY name into its component parts. Throws errors with user-friendly
|
* Parses a LBRY name into its component parts. Throws errors with user-friendly
|
||||||
* messages for invalid names.
|
* messages for invalid names.
|
||||||
*
|
*
|
||||||
* N.B. that "name" indicates the value in the name position of the URI. For
|
|
||||||
* claims for channel content, this will actually be the channel name, and
|
|
||||||
* the content name is in the path (e.g. lbry://@channel/content)
|
|
||||||
*
|
|
||||||
* In most situations, you'll want to use the contentName and channelName keys
|
|
||||||
* and ignore the name key.
|
|
||||||
*
|
|
||||||
* Returns a dictionary with keys:
|
* Returns a dictionary with keys:
|
||||||
* - name (string): The value in the "name" position in the URI. Note that this
|
* - path (string)
|
||||||
* could be either content name or channel name; see above.
|
|
||||||
* - path (string, if persent)
|
|
||||||
* - claimSequence (int, if present)
|
|
||||||
* - bidPosition (int, if present)
|
|
||||||
* - claimId (string, if present)
|
|
||||||
* - isChannel (boolean)
|
* - isChannel (boolean)
|
||||||
* - contentName (string): For anon claims, the name; for channel claims, the path
|
* - streamName (string, if present)
|
||||||
* - channelName (string, if present): Channel name without @
|
* - streamClaimId (string, if present)
|
||||||
|
* - channelName (string, if present)
|
||||||
|
* - channelClaimId (string, if present)
|
||||||
|
* - primaryClaimSequence (int, if present)
|
||||||
|
* - secondaryClaimSequence (int, if present)
|
||||||
|
* - primaryBidPosition (int, if present)
|
||||||
|
* - secondaryBidPosition (int, if present)
|
||||||
*/
|
*/
|
||||||
function parseURI(URI, requireProto = false) {
|
|
||||||
// Break into components. Empty sub-matches are converted to null
|
|
||||||
const componentsRegex = new RegExp('^((?:lbry://)?)' + // protocol
|
|
||||||
'([^:$#/]*)' + // claim name (stops at the first separator or end)
|
|
||||||
'([:$#]?)([^/]*)' + // modifier separator, modifier (stops at the first path separator or end)
|
|
||||||
'(/?)(.*)' // path separator, path
|
|
||||||
);
|
|
||||||
const [proto, claimName, modSep, modVal, pathSep, path] = componentsRegex.exec(URI).slice(1).map(match => match || null);
|
|
||||||
|
|
||||||
let contentName;
|
function parseURI(URL, requireProto = false) {
|
||||||
|
// Break into components. Empty sub-matches are converted to null
|
||||||
|
const componentsRegex = new RegExp(regexPartProtocol + // protocol
|
||||||
|
regexPartStreamOrChannelName + // stream or channel name (stops at the first separator or end)
|
||||||
|
regexPartModifierSeparator + // modifier separator, modifier (stops at the first path separator or end)
|
||||||
|
'(/?)' + // path separator, there should only be one (optional) slash to separate the stream and channel parts
|
||||||
|
regexPartStreamOrChannelName + regexPartModifierSeparator);
|
||||||
|
|
||||||
|
const regexMatch = componentsRegex.exec(URL) || [];
|
||||||
|
const [proto, ...rest] = regexMatch.slice(1).map(match => match || null);
|
||||||
|
const path = rest.join('');
|
||||||
|
const [streamNameOrChannelName, primaryModSeparator, primaryModValue, pathSep, possibleStreamName, secondaryModSeparator, secondaryModValue] = rest;
|
||||||
|
|
||||||
// Validate protocol
|
// Validate protocol
|
||||||
if (requireProto && !proto) {
|
if (requireProto && !proto) {
|
||||||
|
@ -943,14 +947,15 @@ function parseURI(URI, requireProto = false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate and process name
|
// Validate and process name
|
||||||
if (!claimName) {
|
if (!streamNameOrChannelName) {
|
||||||
throw new Error(__('URI does not include name.'));
|
throw new Error(__('URI does not include name.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
const isChannel = claimName.startsWith('@');
|
const includesChannel = streamNameOrChannelName.startsWith('@');
|
||||||
const channelName = isChannel ? claimName.slice(1) : claimName;
|
const isChannel = streamNameOrChannelName.startsWith('@') && !possibleStreamName;
|
||||||
|
const channelName = includesChannel && streamNameOrChannelName.slice(1);
|
||||||
|
|
||||||
if (isChannel) {
|
if (includesChannel) {
|
||||||
if (!channelName) {
|
if (!channelName) {
|
||||||
throw new Error(__('No channel name after @.'));
|
throw new Error(__('No channel name after @.'));
|
||||||
}
|
}
|
||||||
|
@ -958,30 +963,42 @@ function parseURI(URI, requireProto = false) {
|
||||||
if (channelName.length < channelNameMinLength) {
|
if (channelName.length < channelNameMinLength) {
|
||||||
throw new Error(__(`Channel names must be at least %s characters.`, channelNameMinLength));
|
throw new Error(__(`Channel names must be at least %s characters.`, channelNameMinLength));
|
||||||
}
|
}
|
||||||
|
|
||||||
contentName = path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const nameBadChars = (channelName || claimName).match(regexInvalidURI);
|
// Validate and process modifier
|
||||||
if (nameBadChars) {
|
const [primaryClaimId, primaryClaimSequence, primaryBidPosition] = parseURIModifier(primaryModSeparator, primaryModValue);
|
||||||
throw new Error(__(`Invalid character %s in name: %s.`, nameBadChars.length === 1 ? '' : 's', nameBadChars.join(', ')));
|
const [secondaryClaimId, secondaryClaimSequence, secondaryBidPosition] = parseURIModifier(secondaryModSeparator, secondaryModValue);
|
||||||
}
|
const streamName = includesChannel ? possibleStreamName : streamNameOrChannelName;
|
||||||
|
const streamClaimId = includesChannel ? secondaryClaimId : primaryClaimId;
|
||||||
|
const channelClaimId = includesChannel && primaryClaimId;
|
||||||
|
|
||||||
// Validate and process modifier (claim ID, bid position or claim sequence)
|
return _extends({
|
||||||
|
isChannel,
|
||||||
|
path
|
||||||
|
}, streamName ? { streamName } : {}, streamClaimId ? { streamClaimId } : {}, channelName ? { channelName } : {}, channelClaimId ? { channelClaimId } : {}, primaryClaimSequence ? { primaryClaimSequence: parseInt(primaryClaimSequence, 10) } : {}, secondaryClaimSequence ? { secondaryClaimSequence: parseInt(secondaryClaimSequence, 10) } : {}, primaryBidPosition ? { primaryBidPosition: parseInt(primaryBidPosition, 10) } : {}, secondaryBidPosition ? { secondaryBidPosition: parseInt(secondaryBidPosition, 10) } : {}, {
|
||||||
|
|
||||||
|
// The values below should not be used for new uses of parseURI
|
||||||
|
// They will not work properly with canonical_urls
|
||||||
|
claimName: streamNameOrChannelName,
|
||||||
|
claimId: primaryClaimId
|
||||||
|
}, streamName ? { contentName: streamName } : {});
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseURIModifier(modSeperator, modValue) {
|
||||||
let claimId;
|
let claimId;
|
||||||
let claimSequence;
|
let claimSequence;
|
||||||
let bidPosition;
|
let bidPosition;
|
||||||
if (modSep) {
|
if (modSeperator) {
|
||||||
if (!modVal) {
|
if (!modValue) {
|
||||||
throw new Error(__(`No modifier provided after separator %s.`, modSep));
|
throw new Error(__(`No modifier provided after separator %s.`, modSeperator));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modSep === '#') {
|
if (modSeperator === '#') {
|
||||||
claimId = modVal;
|
claimId = modValue;
|
||||||
} else if (modSep === ':') {
|
} else if (modSeperator === ':') {
|
||||||
claimSequence = modVal;
|
claimSequence = modValue;
|
||||||
} else if (modSep === '$') {
|
} else if (modSeperator === '$') {
|
||||||
bidPosition = modVal;
|
bidPosition = modValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -997,27 +1014,7 @@ function parseURI(URI, requireProto = false) {
|
||||||
throw new Error(__('Bid position must be a number.'));
|
throw new Error(__('Bid position must be a number.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate and process path
|
return [claimId, claimSequence, bidPosition];
|
||||||
if (path) {
|
|
||||||
if (!isChannel) {
|
|
||||||
throw new Error(__('Only channel URIs may have a path.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
const pathBadChars = path.match(regexInvalidURI);
|
|
||||||
if (pathBadChars) {
|
|
||||||
throw new Error(__(`Invalid character in path: %s`, pathBadChars.join(', ')));
|
|
||||||
}
|
|
||||||
|
|
||||||
contentName = path;
|
|
||||||
} else if (pathSep) {
|
|
||||||
throw new Error(__('No path provided after /'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return _extends({
|
|
||||||
claimName,
|
|
||||||
path,
|
|
||||||
isChannel
|
|
||||||
}, contentName ? { contentName } : {}, channelName ? { channelName } : {}, claimSequence ? { claimSequence: parseInt(claimSequence, 10) } : {}, bidPosition ? { bidPosition: parseInt(bidPosition, 10) } : {}, claimId ? { claimId } : {}, path ? { path } : {});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1025,67 +1022,119 @@ function parseURI(URI, requireProto = false) {
|
||||||
*
|
*
|
||||||
* The channelName key will accept names with or without the @ prefix.
|
* The channelName key will accept names with or without the @ prefix.
|
||||||
*/
|
*/
|
||||||
function buildURI(URIObj, includeProto = true, protoDefault = 'lbry://') {
|
function buildURI(UrlObj, includeProto = true, protoDefault = 'lbry://') {
|
||||||
const { claimId, claimSequence, bidPosition, contentName, channelName } = URIObj;
|
const {
|
||||||
|
streamName,
|
||||||
|
streamClaimId,
|
||||||
|
channelName,
|
||||||
|
channelClaimId,
|
||||||
|
primaryClaimSequence,
|
||||||
|
primaryBidPosition,
|
||||||
|
secondaryClaimSequence,
|
||||||
|
secondaryBidPosition
|
||||||
|
} = UrlObj,
|
||||||
|
deprecatedParts = _objectWithoutProperties(UrlObj, ['streamName', 'streamClaimId', 'channelName', 'channelClaimId', 'primaryClaimSequence', 'primaryBidPosition', 'secondaryClaimSequence', 'secondaryBidPosition']);
|
||||||
|
const { claimId, claimName, contentName } = deprecatedParts;
|
||||||
|
|
||||||
let { claimName, path } = URIObj;
|
if (!isProduction) {
|
||||||
|
if (claimId) {
|
||||||
if (channelName) {
|
console.error(__("'claimId' should no longer be used. Use 'streamClaimId' or 'channelClaimId' instead"));
|
||||||
const channelNameFormatted = channelName.startsWith('@') ? channelName : `@${channelName}`;
|
|
||||||
if (!claimName) {
|
|
||||||
claimName = channelNameFormatted;
|
|
||||||
} else if (claimName !== channelNameFormatted) {
|
|
||||||
throw new Error(__('Received a channel content URI, but claim name and channelName do not match. "name" represents the value in the name position of the URI (lbry://name...), which for channel content will be the channel name. In most cases, to construct a channel URI you should just pass channelName and contentName.'));
|
|
||||||
}
|
}
|
||||||
|
if (claimName) {
|
||||||
|
console.error(__("'claimName' should no longer be used. Use 'streamClaimName' or 'channelClaimName' instead"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contentName) {
|
if (contentName) {
|
||||||
if (!claimName) {
|
console.error(__("'contentName' should no longer be used. Use 'streamName' instead"));
|
||||||
claimName = contentName;
|
|
||||||
} else if (!path) {
|
|
||||||
path = contentName;
|
|
||||||
}
|
|
||||||
if (path && path !== contentName) {
|
|
||||||
throw new Error(__('Path and contentName do not match. Only one is required; most likely you wanted contentName.'));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (includeProto ? protoDefault : '') + claimName + (claimId ? `#${claimId}` : '') + (claimSequence ? `:${claimSequence}` : '') + (bidPosition ? `${bidPosition}` : '') + (path ? `/${path}` : '');
|
if (!claimName && !channelName && !streamName) {
|
||||||
|
throw new Error(__("'claimName', 'channelName', and 'streamName' are all empty. One must be present to build a url."));
|
||||||
|
}
|
||||||
|
|
||||||
|
const formattedChannelName = channelName && (channelName.startsWith('@') ? channelName : `@${channelName}`);
|
||||||
|
const primaryClaimName = claimName || formattedChannelName || streamName;
|
||||||
|
const primaryClaimId = claimId || (formattedChannelName ? channelClaimId : streamClaimId);
|
||||||
|
const secondaryClaimName = !claimName && (formattedChannelName ? streamName : null);
|
||||||
|
const secondaryClaimId = secondaryClaimName && streamClaimId;
|
||||||
|
|
||||||
|
return (includeProto ? protoDefault : '') +
|
||||||
|
// primaryClaimName will always exist here because we throw above if there is no "name" value passed in
|
||||||
|
// $FlowFixMe
|
||||||
|
primaryClaimName + (primaryClaimId ? `#${primaryClaimId}` : '') + (primaryClaimSequence ? `:${primaryClaimSequence}` : '') + (primaryBidPosition ? `${primaryBidPosition}` : '') + (secondaryClaimName ? `/${secondaryClaimName}` : '') + (secondaryClaimId ? `#${secondaryClaimId}` : '') + (secondaryClaimSequence ? `:${secondaryClaimSequence}` : '') + (secondaryBidPosition ? `${secondaryBidPosition}` : '');
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Takes a parseable LBRY URI and converts it to standard, canonical format */
|
/* Takes a parseable LBRY URL and converts it to standard, canonical format */
|
||||||
function normalizeURI(URI) {
|
function normalizeURI(URL) {
|
||||||
const { claimName, path, bidPosition, claimSequence, claimId } = parseURI(URI);
|
const {
|
||||||
return buildURI({ claimName, path, claimSequence, bidPosition, claimId });
|
streamName,
|
||||||
|
streamClaimId,
|
||||||
|
channelName,
|
||||||
|
channelClaimId,
|
||||||
|
primaryClaimSequence,
|
||||||
|
primaryBidPosition,
|
||||||
|
secondaryClaimSequence,
|
||||||
|
secondaryBidPosition
|
||||||
|
} = parseURI(URL);
|
||||||
|
|
||||||
|
return buildURI({
|
||||||
|
streamName,
|
||||||
|
streamClaimId,
|
||||||
|
channelName,
|
||||||
|
channelClaimId,
|
||||||
|
primaryClaimSequence,
|
||||||
|
primaryBidPosition,
|
||||||
|
secondaryClaimSequence,
|
||||||
|
secondaryBidPosition
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function isURIValid(URI) {
|
function isURIValid(URL) {
|
||||||
let parts;
|
|
||||||
try {
|
try {
|
||||||
parts = parseURI(normalizeURI(URI));
|
parseURI(normalizeURI(URL));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return parts && parts.claimName;
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isNameValid(claimName) {
|
function isNameValid(claimName) {
|
||||||
return !regexInvalidURI.test(claimName);
|
return !regexInvalidURI.test(claimName);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isURIClaimable(URI) {
|
function isURIClaimable(URL) {
|
||||||
let parts;
|
let parts;
|
||||||
try {
|
try {
|
||||||
parts = parseURI(normalizeURI(URI));
|
parts = parseURI(normalizeURI(URL));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return parts && parts.claimName && !parts.claimId && !parts.bidPosition && !parts.claimSequence && !parts.isChannel && !parts.path;
|
|
||||||
|
return parts && parts.streamName && !parts.streamClaimId && !parts.isChannel;
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertToShareLink(URI) {
|
function convertToShareLink(URL) {
|
||||||
const { claimName, path, bidPosition, claimSequence, claimId } = parseURI(URI);
|
const {
|
||||||
return buildURI({ claimName, path, claimSequence, bidPosition, claimId }, true, 'https://open.lbry.com/');
|
streamName,
|
||||||
|
streamClaimId,
|
||||||
|
channelName,
|
||||||
|
channelClaimId,
|
||||||
|
primaryBidPosition,
|
||||||
|
primaryClaimSequence,
|
||||||
|
secondaryBidPosition,
|
||||||
|
secondaryClaimSequence
|
||||||
|
} = parseURI(URL);
|
||||||
|
return buildURI({
|
||||||
|
streamName,
|
||||||
|
streamClaimId,
|
||||||
|
channelName,
|
||||||
|
channelClaimId,
|
||||||
|
primaryBidPosition,
|
||||||
|
primaryClaimSequence,
|
||||||
|
secondaryBidPosition,
|
||||||
|
secondaryClaimSequence
|
||||||
|
}, true, 'https://open.lbry.com/');
|
||||||
}
|
}
|
||||||
|
|
||||||
var _extends$1 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
var _extends$1 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
||||||
|
@ -1130,13 +1179,13 @@ const selectSearchSuggestions = reselect.createSelector(selectSearchValue, selec
|
||||||
let searchSuggestions = [];
|
let searchSuggestions = [];
|
||||||
try {
|
try {
|
||||||
const uri = normalizeURI(query);
|
const uri = normalizeURI(query);
|
||||||
const { claimName, isChannel } = parseURI(uri);
|
const { channelName, streamName, isChannel } = parseURI(uri);
|
||||||
searchSuggestions.push({
|
searchSuggestions.push({
|
||||||
value: claimName,
|
value: streamName,
|
||||||
type: SEARCH_TYPES.SEARCH
|
type: SEARCH_TYPES.SEARCH
|
||||||
}, {
|
}, {
|
||||||
value: uri,
|
value: uri,
|
||||||
shorthand: isChannel ? claimName.slice(1) : claimName,
|
shorthand: isChannel ? channelName : streamName,
|
||||||
type: isChannel ? SEARCH_TYPES.CHANNEL : SEARCH_TYPES.FILE
|
type: isChannel ? SEARCH_TYPES.CHANNEL : SEARCH_TYPES.FILE
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -1157,11 +1206,11 @@ const selectSearchSuggestions = reselect.createSelector(selectSearchValue, selec
|
||||||
// determine if it's a channel
|
// determine if it's a channel
|
||||||
try {
|
try {
|
||||||
const uri = normalizeURI(suggestion);
|
const uri = normalizeURI(suggestion);
|
||||||
const { claimName, isChannel } = parseURI(uri);
|
const { channelName, streamName, isChannel } = parseURI(uri);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
value: uri,
|
value: uri,
|
||||||
shorthand: isChannel ? claimName.slice(1) : claimName,
|
shorthand: isChannel ? channelName : streamName,
|
||||||
type: isChannel ? SEARCH_TYPES.CHANNEL : SEARCH_TYPES.FILE
|
type: isChannel ? SEARCH_TYPES.CHANNEL : SEARCH_TYPES.FILE
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -1377,7 +1426,7 @@ const selectTransactionListFilter = reselect.createSelector(selectState$1, state
|
||||||
|
|
||||||
var _extends$2 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
var _extends$2 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
||||||
|
|
||||||
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
|
function _objectWithoutProperties$1(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
|
||||||
|
|
||||||
const matureTagMap = MATURE_TAGS.reduce((acc, tag) => _extends$2({}, acc, { [tag]: true }), {});
|
const matureTagMap = MATURE_TAGS.reduce((acc, tag) => _extends$2({}, acc, { [tag]: true }), {});
|
||||||
|
|
||||||
|
@ -1404,7 +1453,7 @@ const isClaimNsfw = claim => {
|
||||||
function createNormalizedClaimSearchKey(options) {
|
function createNormalizedClaimSearchKey(options) {
|
||||||
// Ignore page because we don't care what the last page searched was, we want everything
|
// Ignore page because we don't care what the last page searched was, we want everything
|
||||||
// Ignore release_time because that will change depending on when you call claim_search ex: release_time: ">12344567"
|
// Ignore release_time because that will change depending on when you call claim_search ex: release_time: ">12344567"
|
||||||
const rest = _objectWithoutProperties(options, ['page', 'release_time']);
|
const rest = _objectWithoutProperties$1(options, ['page', 'release_time']);
|
||||||
const query = JSON.stringify(rest);
|
const query = JSON.stringify(rest);
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
@ -1445,8 +1494,10 @@ const selectPendingClaims = reselect.createSelector(selectState$2, state => Obje
|
||||||
|
|
||||||
const makeSelectClaimIsPending = uri => reselect.createSelector(selectPendingById, pendingById => {
|
const makeSelectClaimIsPending = uri => reselect.createSelector(selectPendingById, pendingById => {
|
||||||
let claimId;
|
let claimId;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
({ claimId } = parseURI(uri));
|
const { isChannel, channelClaimId, streamClaimId } = parseURI(uri);
|
||||||
|
claimId = isChannel ? channelClaimId : streamClaimId;
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
if (claimId) {
|
if (claimId) {
|
||||||
|
@ -1455,7 +1506,8 @@ const makeSelectClaimIsPending = uri => reselect.createSelector(selectPendingByI
|
||||||
});
|
});
|
||||||
|
|
||||||
const makeSelectPendingByUri = uri => reselect.createSelector(selectPendingById, pendingById => {
|
const makeSelectPendingByUri = uri => reselect.createSelector(selectPendingById, pendingById => {
|
||||||
const { claimId } = parseURI(uri);
|
const { isChannel, channelClaimId, streamClaimId } = parseURI(uri);
|
||||||
|
const claimId = isChannel ? channelClaimId : streamClaimId;
|
||||||
return pendingById[claimId];
|
return pendingById[claimId];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1464,13 +1516,16 @@ const makeSelectClaimForUri = uri => reselect.createSelector(selectClaimsByUri,
|
||||||
// It won't be in claimsByUri because resolving it will return nothing
|
// It won't be in claimsByUri because resolving it will return nothing
|
||||||
|
|
||||||
let valid;
|
let valid;
|
||||||
let claimId;
|
let channelClaimId;
|
||||||
|
let streamClaimId;
|
||||||
|
let isChannel;
|
||||||
try {
|
try {
|
||||||
({ claimId } = parseURI(uri));
|
({ isChannel, channelClaimId, streamClaimId } = parseURI(uri));
|
||||||
valid = true;
|
valid = true;
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
if (valid) {
|
if (valid) {
|
||||||
|
const claimId = isChannel ? channelClaimId : streamClaimId;
|
||||||
const pendingClaim = pendingById[claimId];
|
const pendingClaim = pendingById[claimId];
|
||||||
|
|
||||||
if (pendingClaim) {
|
if (pendingClaim) {
|
||||||
|
@ -1720,15 +1775,18 @@ const selectClaimSearchByQueryLastPageReached = reselect.createSelector(selectSt
|
||||||
|
|
||||||
const makeSelectShortUrlForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => claim && claim.short_url);
|
const makeSelectShortUrlForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => claim && claim.short_url);
|
||||||
|
|
||||||
|
const makeSelectCanonicalUrlForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => claim && claim.canonical_url);
|
||||||
|
|
||||||
const makeSelectSupportsForUri = uri => reselect.createSelector(selectSupportsByOutpoint, makeSelectClaimForUri(uri), (byOutpoint, claim) => {
|
const makeSelectSupportsForUri = uri => reselect.createSelector(selectSupportsByOutpoint, makeSelectClaimForUri(uri), (byOutpoint, claim) => {
|
||||||
if (!claim || !claim.is_mine) {
|
if (!claim || !claim.is_mine) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { claim_id: claimId } = claim;
|
const { claim_id: claimId } = claim;
|
||||||
let total = parseFloat("0.0");
|
let total = 0;
|
||||||
|
|
||||||
Object.values(byOutpoint).forEach(support => {
|
Object.values(byOutpoint).forEach(support => {
|
||||||
|
// $FlowFixMe
|
||||||
const { claim_id, amount } = support;
|
const { claim_id, amount } = support;
|
||||||
total = claim_id === claimId && amount ? total + parseFloat(amount) : total;
|
total = claim_id === claimId && amount ? total + parseFloat(amount) : total;
|
||||||
});
|
});
|
||||||
|
@ -2433,10 +2491,10 @@ function doClaimSearch(options = {
|
||||||
|
|
||||||
const success = data => {
|
const success = data => {
|
||||||
const resolveInfo = {};
|
const resolveInfo = {};
|
||||||
const uris = [];
|
const urls = [];
|
||||||
data.items.forEach(stream => {
|
data.items.forEach(stream => {
|
||||||
resolveInfo[stream.permanent_url] = { stream };
|
resolveInfo[stream.canonical_url] = { stream };
|
||||||
uris.push(stream.permanent_url);
|
urls.push(stream.canonical_url);
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
|
@ -2444,7 +2502,7 @@ function doClaimSearch(options = {
|
||||||
data: {
|
data: {
|
||||||
query,
|
query,
|
||||||
resolveInfo,
|
resolveInfo,
|
||||||
uris,
|
urls,
|
||||||
append: options.page && options.page !== 1,
|
append: options.page && options.page !== 1,
|
||||||
pageSize: options.page_size
|
pageSize: options.page_size
|
||||||
}
|
}
|
||||||
|
@ -2858,12 +2916,12 @@ function doSetFileListSort(page, value) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function _objectWithoutProperties$1(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
|
function _objectWithoutProperties$2(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
|
||||||
|
|
||||||
const selectState$5 = state => state.publish || {};
|
const selectState$5 = state => state.publish || {};
|
||||||
|
|
||||||
const selectPublishFormValues = reselect.createSelector(selectState$5, state => {
|
const selectPublishFormValues = reselect.createSelector(selectState$5, state => {
|
||||||
const formValues = _objectWithoutProperties$1(state, ['pendingPublish']);
|
const formValues = _objectWithoutProperties$2(state, ['pendingPublish']);
|
||||||
return formValues;
|
return formValues;
|
||||||
});
|
});
|
||||||
const makeSelectPublishFormValue = item => reselect.createSelector(selectState$5, state => state[item]);
|
const makeSelectPublishFormValue = item => reselect.createSelector(selectState$5, state => state[item]);
|
||||||
|
@ -2876,8 +2934,16 @@ const selectIsStillEditing = reselect.createSelector(selectPublishFormValues, pu
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { isChannel: currentIsChannel, claimName: currentClaimName, contentName: currentContentName } = parseURI(uri);
|
const {
|
||||||
const { isChannel: editIsChannel, claimName: editClaimName, contentName: editContentName } = parseURI(editingURI);
|
isChannel: currentIsChannel,
|
||||||
|
claimName: currentClaimName,
|
||||||
|
contentName: currentContentName
|
||||||
|
} = parseURI(uri);
|
||||||
|
const {
|
||||||
|
isChannel: editIsChannel,
|
||||||
|
claimName: editClaimName,
|
||||||
|
contentName: editContentName
|
||||||
|
} = parseURI(editingURI);
|
||||||
|
|
||||||
// Depending on the previous/current use of a channel, we need to compare different things
|
// Depending on the previous/current use of a channel, we need to compare different things
|
||||||
// ex: going from a channel to anonymous, the new uri won't return contentName, so we need to use claimName
|
// ex: going from a channel to anonymous, the new uri won't return contentName, so we need to use claimName
|
||||||
|
@ -2904,7 +2970,7 @@ const selectIsResolvingPublishUris = reselect.createSelector(selectState$5, sele
|
||||||
|
|
||||||
let isResolvingShortUri;
|
let isResolvingShortUri;
|
||||||
if (isChannel) {
|
if (isChannel) {
|
||||||
const shortUri = buildURI({ contentName: name });
|
const shortUri = buildURI({ streamName: name });
|
||||||
isResolvingShortUri = resolvingUris.includes(shortUri);
|
isResolvingShortUri = resolvingUris.includes(shortUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3376,13 +3442,21 @@ from, isBackgroundSearch = false) => (dispatch, getState) => {
|
||||||
const actions = [];
|
const actions = [];
|
||||||
|
|
||||||
data.forEach(result => {
|
data.forEach(result => {
|
||||||
if (result.name) {
|
if (result) {
|
||||||
const uri = buildURI({
|
const { name, claimId } = result;
|
||||||
claimName: result.name,
|
const urlObj = {};
|
||||||
claimId: result.claimId
|
|
||||||
});
|
if (name.startsWith('@')) {
|
||||||
actions.push(doResolveUri(uri));
|
urlObj.channelName = name;
|
||||||
uris.push(uri);
|
urlObj.channelClaimId = claimId;
|
||||||
|
} else {
|
||||||
|
urlObj.streamName = name;
|
||||||
|
urlObj.streamClaimId = claimId;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = buildURI(urlObj);
|
||||||
|
actions.push(doResolveUri(url));
|
||||||
|
uris.push(url);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -3561,26 +3635,25 @@ function handleClaimAction(state, action) {
|
||||||
const byId = Object.assign({}, state.byId);
|
const byId = Object.assign({}, state.byId);
|
||||||
const channelClaimCounts = Object.assign({}, state.channelClaimCounts);
|
const channelClaimCounts = Object.assign({}, state.channelClaimCounts);
|
||||||
|
|
||||||
Object.entries(resolveInfo).forEach(([uri, resolveResponse]) => {
|
Object.entries(resolveInfo).forEach(([url, resolveResponse]) => {
|
||||||
// $FlowFixMe
|
// $FlowFixMe
|
||||||
if (resolveResponse.claimsInChannel) {
|
const { claimsInChannel, stream, channel } = resolveResponse;
|
||||||
// $FlowFixMe
|
if (claimsInChannel) {
|
||||||
channelClaimCounts[uri] = resolveResponse.claimsInChannel;
|
channelClaimCounts[url] = claimsInChannel;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// $FlowFixMe
|
|
||||||
Object.entries(resolveInfo).forEach(([uri, { channel, stream }]) => {
|
|
||||||
if (stream) {
|
if (stream) {
|
||||||
byId[stream.claim_id] = stream;
|
byId[stream.claim_id] = stream;
|
||||||
byUri[uri] = stream.claim_id;
|
byUri[url] = stream.claim_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (channel) {
|
if (channel) {
|
||||||
byId[channel.claim_id] = channel;
|
byId[channel.claim_id] = channel;
|
||||||
byUri[stream ? channel.permanent_url : uri] = channel.claim_id;
|
byUri[stream ? channel.canonical_url : url] = channel.claim_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!stream && !channel) {
|
if (!stream && !channel) {
|
||||||
byUri[uri] = null;
|
byUri[url] = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -3792,17 +3865,17 @@ reducers[CLAIM_SEARCH_COMPLETED] = (state, action) => {
|
||||||
const fetchingClaimSearchByQuery = Object.assign({}, state.fetchingClaimSearchByQuery);
|
const fetchingClaimSearchByQuery = Object.assign({}, state.fetchingClaimSearchByQuery);
|
||||||
const claimSearchByQuery = Object.assign({}, state.claimSearchByQuery);
|
const claimSearchByQuery = Object.assign({}, state.claimSearchByQuery);
|
||||||
const claimSearchByQueryLastPageReached = Object.assign({}, state.claimSearchByQueryLastPageReached);
|
const claimSearchByQueryLastPageReached = Object.assign({}, state.claimSearchByQueryLastPageReached);
|
||||||
const { append, query, uris, pageSize } = action.data;
|
const { append, query, urls, pageSize } = action.data;
|
||||||
|
|
||||||
if (append) {
|
if (append) {
|
||||||
// todo: check for duplicate uris when concatenating?
|
// todo: check for duplicate urls when concatenating?
|
||||||
claimSearchByQuery[query] = claimSearchByQuery[query] && claimSearchByQuery[query].length ? claimSearchByQuery[query].concat(uris) : uris;
|
claimSearchByQuery[query] = claimSearchByQuery[query] && claimSearchByQuery[query].length ? claimSearchByQuery[query].concat(urls) : urls;
|
||||||
} else {
|
} else {
|
||||||
claimSearchByQuery[query] = uris;
|
claimSearchByQuery[query] = urls;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the returned number of uris is less than the page size, so we're on the last page
|
// the returned number of urls is less than the page size, so we're on the last page
|
||||||
claimSearchByQueryLastPageReached[query] = uris.length < pageSize;
|
claimSearchByQueryLastPageReached[query] = urls.length < pageSize;
|
||||||
|
|
||||||
delete fetchingClaimSearchByQuery[query];
|
delete fetchingClaimSearchByQuery[query];
|
||||||
|
|
||||||
|
@ -4232,7 +4305,7 @@ const notificationsReducer = handleActions({
|
||||||
|
|
||||||
var _extends$a = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
var _extends$a = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
||||||
|
|
||||||
function _objectWithoutProperties$2(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
|
function _objectWithoutProperties$3(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
|
||||||
|
|
||||||
const defaultState$6 = {
|
const defaultState$6 = {
|
||||||
editingURI: undefined,
|
editingURI: undefined,
|
||||||
|
@ -4282,7 +4355,7 @@ const publishReducer = handleActions({
|
||||||
publishSuccess: true
|
publishSuccess: true
|
||||||
}),
|
}),
|
||||||
[DO_PREPARE_EDIT]: (state, action) => {
|
[DO_PREPARE_EDIT]: (state, action) => {
|
||||||
const publishData = _objectWithoutProperties$2(action.data, []);
|
const publishData = _objectWithoutProperties$3(action.data, []);
|
||||||
const { channel, name, uri } = publishData;
|
const { channel, name, uri } = publishData;
|
||||||
|
|
||||||
// The short uri is what is presented to the user
|
// The short uri is what is presented to the user
|
||||||
|
@ -4892,6 +4965,7 @@ exports.isNameValid = isNameValid;
|
||||||
exports.isURIClaimable = isURIClaimable;
|
exports.isURIClaimable = isURIClaimable;
|
||||||
exports.isURIValid = isURIValid;
|
exports.isURIValid = isURIValid;
|
||||||
exports.makeSelectAmountForUri = makeSelectAmountForUri;
|
exports.makeSelectAmountForUri = makeSelectAmountForUri;
|
||||||
|
exports.makeSelectCanonicalUrlForUri = makeSelectCanonicalUrlForUri;
|
||||||
exports.makeSelectChannelForClaimUri = makeSelectChannelForClaimUri;
|
exports.makeSelectChannelForClaimUri = makeSelectChannelForClaimUri;
|
||||||
exports.makeSelectClaimForUri = makeSelectClaimForUri;
|
exports.makeSelectClaimForUri = makeSelectClaimForUri;
|
||||||
exports.makeSelectClaimIsMine = makeSelectClaimIsMine;
|
exports.makeSelectClaimIsMine = makeSelectClaimIsMine;
|
||||||
|
|
1
dist/flow-typed/Claim.js
vendored
1
dist/flow-typed/Claim.js
vendored
|
@ -23,6 +23,7 @@ declare type GenericClaim = {
|
||||||
decoded_claim: boolean, // Not available currently https://github.com/lbryio/lbry/issues/2044
|
decoded_claim: boolean, // Not available currently https://github.com/lbryio/lbry/issues/2044
|
||||||
timestamp?: number, // date of last transaction
|
timestamp?: number, // date of last transaction
|
||||||
height: number, // block height the tx was confirmed
|
height: number, // block height the tx was confirmed
|
||||||
|
is_mine: boolean,
|
||||||
name: string,
|
name: string,
|
||||||
normalized_name: string, // `name` normalized via unicode NFD spec,
|
normalized_name: string, // `name` normalized via unicode NFD spec,
|
||||||
nout: number, // index number for an output of a tx
|
nout: number, // index number for an output of a tx
|
||||||
|
|
2
dist/flow-typed/Lbry.js
vendored
2
dist/flow-typed/Lbry.js
vendored
|
@ -65,7 +65,7 @@ declare type VersionResponse = {
|
||||||
|
|
||||||
declare type ResolveResponse = {
|
declare type ResolveResponse = {
|
||||||
// Keys are the url(s) passed to resolve
|
// Keys are the url(s) passed to resolve
|
||||||
[string]: Claim | { error?: {} },
|
[string]: { error?: {}, stream?: StreamClaim, channel?: ChannelClaim, claimsInChannel?: number },
|
||||||
};
|
};
|
||||||
|
|
||||||
declare type GetResponse = FileListItem & { error?: string };
|
declare type GetResponse = FileListItem & { error?: string };
|
||||||
|
|
2
dist/flow-typed/i18n.js
vendored
Normal file
2
dist/flow-typed/i18n.js
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
// @flow
|
||||||
|
declare function __(a: string, b?: string | number): string;
|
1
flow-typed/Claim.js
vendored
1
flow-typed/Claim.js
vendored
|
@ -23,6 +23,7 @@ declare type GenericClaim = {
|
||||||
decoded_claim: boolean, // Not available currently https://github.com/lbryio/lbry/issues/2044
|
decoded_claim: boolean, // Not available currently https://github.com/lbryio/lbry/issues/2044
|
||||||
timestamp?: number, // date of last transaction
|
timestamp?: number, // date of last transaction
|
||||||
height: number, // block height the tx was confirmed
|
height: number, // block height the tx was confirmed
|
||||||
|
is_mine: boolean,
|
||||||
name: string,
|
name: string,
|
||||||
normalized_name: string, // `name` normalized via unicode NFD spec,
|
normalized_name: string, // `name` normalized via unicode NFD spec,
|
||||||
nout: number, // index number for an output of a tx
|
nout: number, // index number for an output of a tx
|
||||||
|
|
2
flow-typed/Lbry.js
vendored
2
flow-typed/Lbry.js
vendored
|
@ -65,7 +65,7 @@ declare type VersionResponse = {
|
||||||
|
|
||||||
declare type ResolveResponse = {
|
declare type ResolveResponse = {
|
||||||
// Keys are the url(s) passed to resolve
|
// Keys are the url(s) passed to resolve
|
||||||
[string]: Claim | { error?: {} },
|
[string]: { error?: {}, stream?: StreamClaim, channel?: ChannelClaim, claimsInChannel?: number },
|
||||||
};
|
};
|
||||||
|
|
||||||
declare type GetResponse = FileListItem & { error?: string };
|
declare type GetResponse = FileListItem & { error?: string };
|
||||||
|
|
2
flow-typed/i18n.js
vendored
Normal file
2
flow-typed/i18n.js
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
// @flow
|
||||||
|
declare function __(a: string, b?: string | number): string;
|
|
@ -172,6 +172,7 @@ export {
|
||||||
makeSelectPendingByUri,
|
makeSelectPendingByUri,
|
||||||
makeSelectClaimsInChannelForCurrentPageState,
|
makeSelectClaimsInChannelForCurrentPageState,
|
||||||
makeSelectShortUrlForUri,
|
makeSelectShortUrlForUri,
|
||||||
|
makeSelectCanonicalUrlForUri,
|
||||||
makeSelectSupportsForUri,
|
makeSelectSupportsForUri,
|
||||||
selectPendingById,
|
selectPendingById,
|
||||||
selectClaimsById,
|
selectClaimsById,
|
||||||
|
|
331
src/lbryURI.js
331
src/lbryURI.js
|
@ -1,46 +1,77 @@
|
||||||
|
// @flow
|
||||||
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
const channelNameMinLength = 1;
|
const channelNameMinLength = 1;
|
||||||
const claimIdMaxLength = 40;
|
const claimIdMaxLength = 40;
|
||||||
|
|
||||||
// see https://spec.lbry.com/#urls
|
// see https://spec.lbry.com/#urls
|
||||||
export const regexInvalidURI = /[ =&#:$@%?;/\\"<>%{}|^~[\]`\u{0000}-\u{0008}\u{000b}-\u{000c}\u{000e}-\u{001F}\u{D800}-\u{DFFF}\u{FFFE}-\u{FFFF}]/u;
|
export const regexInvalidURI = /[ =&#:$@%?;/\\"<>%{}|^~[\]`\u{0000}-\u{0008}\u{000b}-\u{000c}\u{000e}-\u{001F}\u{D800}-\u{DFFF}\u{FFFE}-\u{FFFF}]/u;
|
||||||
export const regexAddress = /^(b|r)(?=[^0OIl]{32,33})[0-9A-Za-z]{32,33}$/;
|
export const regexAddress = /^(b|r)(?=[^0OIl]{32,33})[0-9A-Za-z]{32,33}$/;
|
||||||
|
const regexPartProtocol = '^((?:lbry://)?)';
|
||||||
|
const regexPartStreamOrChannelName = '([^:$#/]*)';
|
||||||
|
const regexPartModifierSeparator = '([:$#]?)([^/]*)';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a LBRY name into its component parts. Throws errors with user-friendly
|
* Parses a LBRY name into its component parts. Throws errors with user-friendly
|
||||||
* messages for invalid names.
|
* messages for invalid names.
|
||||||
*
|
*
|
||||||
* N.B. that "name" indicates the value in the name position of the URI. For
|
|
||||||
* claims for channel content, this will actually be the channel name, and
|
|
||||||
* the content name is in the path (e.g. lbry://@channel/content)
|
|
||||||
*
|
|
||||||
* In most situations, you'll want to use the contentName and channelName keys
|
|
||||||
* and ignore the name key.
|
|
||||||
*
|
|
||||||
* Returns a dictionary with keys:
|
* Returns a dictionary with keys:
|
||||||
* - name (string): The value in the "name" position in the URI. Note that this
|
* - path (string)
|
||||||
* could be either content name or channel name; see above.
|
|
||||||
* - path (string, if persent)
|
|
||||||
* - claimSequence (int, if present)
|
|
||||||
* - bidPosition (int, if present)
|
|
||||||
* - claimId (string, if present)
|
|
||||||
* - isChannel (boolean)
|
* - isChannel (boolean)
|
||||||
* - contentName (string): For anon claims, the name; for channel claims, the path
|
* - streamName (string, if present)
|
||||||
* - channelName (string, if present): Channel name without @
|
* - streamClaimId (string, if present)
|
||||||
|
* - channelName (string, if present)
|
||||||
|
* - channelClaimId (string, if present)
|
||||||
|
* - primaryClaimSequence (int, if present)
|
||||||
|
* - secondaryClaimSequence (int, if present)
|
||||||
|
* - primaryBidPosition (int, if present)
|
||||||
|
* - secondaryBidPosition (int, if present)
|
||||||
*/
|
*/
|
||||||
export function parseURI(URI, requireProto = false) {
|
|
||||||
|
type ChannelUrlObj = {};
|
||||||
|
|
||||||
|
type LbryUrlObj = {
|
||||||
|
// Path and channel will always exist when calling parseURI
|
||||||
|
// But they may not exist when code calls buildURI
|
||||||
|
isChannel?: boolean,
|
||||||
|
path?: string,
|
||||||
|
streamName?: string,
|
||||||
|
streamClaimId?: string,
|
||||||
|
channelName?: string,
|
||||||
|
channelClaimId?: string,
|
||||||
|
primaryClaimSequence?: number,
|
||||||
|
secondaryClaimSequence?: number,
|
||||||
|
primaryBidPosition?: number,
|
||||||
|
secondaryBidPosition?: number,
|
||||||
|
|
||||||
|
// Below are considered deprecated and should not be used due to unreliableness with claim.canonical_url
|
||||||
|
claimName?: string,
|
||||||
|
claimId?: string,
|
||||||
|
contentName?: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function parseURI(URL: string, requireProto: boolean = false): LbryUrlObj {
|
||||||
// Break into components. Empty sub-matches are converted to null
|
// Break into components. Empty sub-matches are converted to null
|
||||||
const componentsRegex = new RegExp(
|
const componentsRegex = new RegExp(
|
||||||
'^((?:lbry://)?)' + // protocol
|
regexPartProtocol + // protocol
|
||||||
'([^:$#/]*)' + // claim name (stops at the first separator or end)
|
regexPartStreamOrChannelName + // stream or channel name (stops at the first separator or end)
|
||||||
'([:$#]?)([^/]*)' + // modifier separator, modifier (stops at the first path separator or end)
|
regexPartModifierSeparator + // modifier separator, modifier (stops at the first path separator or end)
|
||||||
'(/?)(.*)' // path separator, path
|
'(/?)' + // path separator, there should only be one (optional) slash to separate the stream and channel parts
|
||||||
|
regexPartStreamOrChannelName +
|
||||||
|
regexPartModifierSeparator
|
||||||
);
|
);
|
||||||
const [proto, claimName, modSep, modVal, pathSep, path] = componentsRegex
|
|
||||||
.exec(URI)
|
|
||||||
.slice(1)
|
|
||||||
.map(match => match || null);
|
|
||||||
|
|
||||||
let contentName;
|
const regexMatch = componentsRegex.exec(URL) || [];
|
||||||
|
const [proto, ...rest] = regexMatch.slice(1).map(match => match || null);
|
||||||
|
const path = rest.join('');
|
||||||
|
const [
|
||||||
|
streamNameOrChannelName,
|
||||||
|
primaryModSeparator,
|
||||||
|
primaryModValue,
|
||||||
|
pathSep,
|
||||||
|
possibleStreamName,
|
||||||
|
secondaryModSeparator,
|
||||||
|
secondaryModValue,
|
||||||
|
] = rest;
|
||||||
|
|
||||||
// Validate protocol
|
// Validate protocol
|
||||||
if (requireProto && !proto) {
|
if (requireProto && !proto) {
|
||||||
|
@ -48,14 +79,15 @@ export function parseURI(URI, requireProto = false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate and process name
|
// Validate and process name
|
||||||
if (!claimName) {
|
if (!streamNameOrChannelName) {
|
||||||
throw new Error(__('URI does not include name.'));
|
throw new Error(__('URI does not include name.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
const isChannel = claimName.startsWith('@');
|
const includesChannel = streamNameOrChannelName.startsWith('@');
|
||||||
const channelName = isChannel ? claimName.slice(1) : claimName;
|
const isChannel = streamNameOrChannelName.startsWith('@') && !possibleStreamName;
|
||||||
|
const channelName = includesChannel && streamNameOrChannelName.slice(1);
|
||||||
|
|
||||||
if (isChannel) {
|
if (includesChannel) {
|
||||||
if (!channelName) {
|
if (!channelName) {
|
||||||
throw new Error(__('No channel name after @.'));
|
throw new Error(__('No channel name after @.'));
|
||||||
}
|
}
|
||||||
|
@ -63,36 +95,58 @@ export function parseURI(URI, requireProto = false) {
|
||||||
if (channelName.length < channelNameMinLength) {
|
if (channelName.length < channelNameMinLength) {
|
||||||
throw new Error(__(`Channel names must be at least %s characters.`, channelNameMinLength));
|
throw new Error(__(`Channel names must be at least %s characters.`, channelNameMinLength));
|
||||||
}
|
}
|
||||||
|
|
||||||
contentName = path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const nameBadChars = (channelName || claimName).match(regexInvalidURI);
|
// Validate and process modifier
|
||||||
if (nameBadChars) {
|
const [primaryClaimId, primaryClaimSequence, primaryBidPosition] = parseURIModifier(
|
||||||
throw new Error(
|
primaryModSeparator,
|
||||||
__(
|
primaryModValue
|
||||||
`Invalid character %s in name: %s.`,
|
|
||||||
nameBadChars.length === 1 ? '' : 's',
|
|
||||||
nameBadChars.join(', ')
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
const [secondaryClaimId, secondaryClaimSequence, secondaryBidPosition] = parseURIModifier(
|
||||||
|
secondaryModSeparator,
|
||||||
|
secondaryModValue
|
||||||
|
);
|
||||||
|
const streamName = includesChannel ? possibleStreamName : streamNameOrChannelName;
|
||||||
|
const streamClaimId = includesChannel ? secondaryClaimId : primaryClaimId;
|
||||||
|
const channelClaimId = includesChannel && primaryClaimId;
|
||||||
|
|
||||||
// Validate and process modifier (claim ID, bid position or claim sequence)
|
return {
|
||||||
|
isChannel,
|
||||||
|
path,
|
||||||
|
...(streamName ? { streamName } : {}),
|
||||||
|
...(streamClaimId ? { streamClaimId } : {}),
|
||||||
|
...(channelName ? { channelName } : {}),
|
||||||
|
...(channelClaimId ? { channelClaimId } : {}),
|
||||||
|
...(primaryClaimSequence ? { primaryClaimSequence: parseInt(primaryClaimSequence, 10) } : {}),
|
||||||
|
...(secondaryClaimSequence
|
||||||
|
? { secondaryClaimSequence: parseInt(secondaryClaimSequence, 10) }
|
||||||
|
: {}),
|
||||||
|
...(primaryBidPosition ? { primaryBidPosition: parseInt(primaryBidPosition, 10) } : {}),
|
||||||
|
...(secondaryBidPosition ? { secondaryBidPosition: parseInt(secondaryBidPosition, 10) } : {}),
|
||||||
|
|
||||||
|
// The values below should not be used for new uses of parseURI
|
||||||
|
// They will not work properly with canonical_urls
|
||||||
|
claimName: streamNameOrChannelName,
|
||||||
|
claimId: primaryClaimId,
|
||||||
|
...(streamName ? { contentName: streamName } : {}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseURIModifier(modSeperator: ?string, modValue: ?string) {
|
||||||
let claimId;
|
let claimId;
|
||||||
let claimSequence;
|
let claimSequence;
|
||||||
let bidPosition;
|
let bidPosition;
|
||||||
if (modSep) {
|
if (modSeperator) {
|
||||||
if (!modVal) {
|
if (!modValue) {
|
||||||
throw new Error(__(`No modifier provided after separator %s.`, modSep));
|
throw new Error(__(`No modifier provided after separator %s.`, modSeperator));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modSep === '#') {
|
if (modSeperator === '#') {
|
||||||
claimId = modVal;
|
claimId = modValue;
|
||||||
} else if (modSep === ':') {
|
} else if (modSeperator === ':') {
|
||||||
claimSequence = modVal;
|
claimSequence = modValue;
|
||||||
} else if (modSep === '$') {
|
} else if (modSeperator === '$') {
|
||||||
bidPosition = modVal;
|
bidPosition = modValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,33 +162,7 @@ export function parseURI(URI, requireProto = false) {
|
||||||
throw new Error(__('Bid position must be a number.'));
|
throw new Error(__('Bid position must be a number.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate and process path
|
return [claimId, claimSequence, bidPosition];
|
||||||
if (path) {
|
|
||||||
if (!isChannel) {
|
|
||||||
throw new Error(__('Only channel URIs may have a path.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
const pathBadChars = path.match(regexInvalidURI);
|
|
||||||
if (pathBadChars) {
|
|
||||||
throw new Error(__(`Invalid character in path: %s`, pathBadChars.join(', ')));
|
|
||||||
}
|
|
||||||
|
|
||||||
contentName = path;
|
|
||||||
} else if (pathSep) {
|
|
||||||
throw new Error(__('No path provided after /'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
claimName,
|
|
||||||
path,
|
|
||||||
isChannel,
|
|
||||||
...(contentName ? { contentName } : {}),
|
|
||||||
...(channelName ? { channelName } : {}),
|
|
||||||
...(claimSequence ? { claimSequence: parseInt(claimSequence, 10) } : {}),
|
|
||||||
...(bidPosition ? { bidPosition: parseInt(bidPosition, 10) } : {}),
|
|
||||||
...(claimId ? { claimId } : {}),
|
|
||||||
...(path ? { path } : {}),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -142,91 +170,144 @@ export function parseURI(URI, requireProto = false) {
|
||||||
*
|
*
|
||||||
* The channelName key will accept names with or without the @ prefix.
|
* The channelName key will accept names with or without the @ prefix.
|
||||||
*/
|
*/
|
||||||
export function buildURI(URIObj, includeProto = true, protoDefault = 'lbry://') {
|
export function buildURI(
|
||||||
const { claimId, claimSequence, bidPosition, contentName, channelName } = URIObj;
|
UrlObj: LbryUrlObj,
|
||||||
|
includeProto: boolean = true,
|
||||||
|
protoDefault: string = 'lbry://'
|
||||||
|
): string {
|
||||||
|
const {
|
||||||
|
streamName,
|
||||||
|
streamClaimId,
|
||||||
|
channelName,
|
||||||
|
channelClaimId,
|
||||||
|
primaryClaimSequence,
|
||||||
|
primaryBidPosition,
|
||||||
|
secondaryClaimSequence,
|
||||||
|
secondaryBidPosition,
|
||||||
|
...deprecatedParts
|
||||||
|
} = UrlObj;
|
||||||
|
const { claimId, claimName, contentName } = deprecatedParts;
|
||||||
|
|
||||||
let { claimName, path } = URIObj;
|
if (!isProduction) {
|
||||||
|
if (claimId) {
|
||||||
if (channelName) {
|
console.error(
|
||||||
const channelNameFormatted = channelName.startsWith('@') ? channelName : `@${channelName}`;
|
__("'claimId' should no longer be used. Use 'streamClaimId' or 'channelClaimId' instead")
|
||||||
if (!claimName) {
|
);
|
||||||
claimName = channelNameFormatted;
|
}
|
||||||
} else if (claimName !== channelNameFormatted) {
|
if (claimName) {
|
||||||
throw new Error(
|
console.error(
|
||||||
__(
|
__(
|
||||||
'Received a channel content URI, but claim name and channelName do not match. "name" represents the value in the name position of the URI (lbry://name...), which for channel content will be the channel name. In most cases, to construct a channel URI you should just pass channelName and contentName.'
|
"'claimName' should no longer be used. Use 'streamClaimName' or 'channelClaimName' instead"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (contentName) {
|
if (contentName) {
|
||||||
if (!claimName) {
|
console.error(__("'contentName' should no longer be used. Use 'streamName' instead"));
|
||||||
claimName = contentName;
|
|
||||||
} else if (!path) {
|
|
||||||
path = contentName;
|
|
||||||
}
|
}
|
||||||
if (path && path !== contentName) {
|
}
|
||||||
|
|
||||||
|
if (!claimName && !channelName && !streamName) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
__(
|
__(
|
||||||
'Path and contentName do not match. Only one is required; most likely you wanted contentName.'
|
"'claimName', 'channelName', and 'streamName' are all empty. One must be present to build a url."
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
const formattedChannelName =
|
||||||
|
channelName && (channelName.startsWith('@') ? channelName : `@${channelName}`);
|
||||||
|
const primaryClaimName = claimName || formattedChannelName || streamName;
|
||||||
|
const primaryClaimId = claimId || (formattedChannelName ? channelClaimId : streamClaimId);
|
||||||
|
const secondaryClaimName = !claimName && (formattedChannelName ? streamName : null);
|
||||||
|
const secondaryClaimId = secondaryClaimName && streamClaimId;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(includeProto ? protoDefault : '') +
|
(includeProto ? protoDefault : '') +
|
||||||
claimName +
|
// primaryClaimName will always exist here because we throw above if there is no "name" value passed in
|
||||||
(claimId ? `#${claimId}` : '') +
|
// $FlowFixMe
|
||||||
(claimSequence ? `:${claimSequence}` : '') +
|
primaryClaimName +
|
||||||
(bidPosition ? `${bidPosition}` : '') +
|
(primaryClaimId ? `#${primaryClaimId}` : '') +
|
||||||
(path ? `/${path}` : '')
|
(primaryClaimSequence ? `:${primaryClaimSequence}` : '') +
|
||||||
|
(primaryBidPosition ? `${primaryBidPosition}` : '') +
|
||||||
|
(secondaryClaimName ? `/${secondaryClaimName}` : '') +
|
||||||
|
(secondaryClaimId ? `#${secondaryClaimId}` : '') +
|
||||||
|
(secondaryClaimSequence ? `:${secondaryClaimSequence}` : '') +
|
||||||
|
(secondaryBidPosition ? `${secondaryBidPosition}` : '')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Takes a parseable LBRY URI and converts it to standard, canonical format */
|
/* Takes a parseable LBRY URL and converts it to standard, canonical format */
|
||||||
export function normalizeURI(URI) {
|
export function normalizeURI(URL: string) {
|
||||||
const { claimName, path, bidPosition, claimSequence, claimId } = parseURI(URI);
|
const {
|
||||||
return buildURI({ claimName, path, claimSequence, bidPosition, claimId });
|
streamName,
|
||||||
|
streamClaimId,
|
||||||
|
channelName,
|
||||||
|
channelClaimId,
|
||||||
|
primaryClaimSequence,
|
||||||
|
primaryBidPosition,
|
||||||
|
secondaryClaimSequence,
|
||||||
|
secondaryBidPosition,
|
||||||
|
} = parseURI(URL);
|
||||||
|
|
||||||
|
return buildURI({
|
||||||
|
streamName,
|
||||||
|
streamClaimId,
|
||||||
|
channelName,
|
||||||
|
channelClaimId,
|
||||||
|
primaryClaimSequence,
|
||||||
|
primaryBidPosition,
|
||||||
|
secondaryClaimSequence,
|
||||||
|
secondaryBidPosition,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isURIValid(URI) {
|
export function isURIValid(URL: string): boolean {
|
||||||
let parts;
|
|
||||||
try {
|
try {
|
||||||
parts = parseURI(normalizeURI(URI));
|
parseURI(normalizeURI(URL));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return parts && parts.claimName;
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isNameValid(claimName) {
|
export function isNameValid(claimName: string) {
|
||||||
return !regexInvalidURI.test(claimName);
|
return !regexInvalidURI.test(claimName);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isURIClaimable(URI) {
|
export function isURIClaimable(URL: string) {
|
||||||
let parts;
|
let parts;
|
||||||
try {
|
try {
|
||||||
parts = parseURI(normalizeURI(URI));
|
parts = parseURI(normalizeURI(URL));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return (
|
|
||||||
parts &&
|
return parts && parts.streamName && !parts.streamClaimId && !parts.isChannel;
|
||||||
parts.claimName &&
|
|
||||||
!parts.claimId &&
|
|
||||||
!parts.bidPosition &&
|
|
||||||
!parts.claimSequence &&
|
|
||||||
!parts.isChannel &&
|
|
||||||
!parts.path
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function convertToShareLink(URI) {
|
export function convertToShareLink(URL: string) {
|
||||||
const { claimName, path, bidPosition, claimSequence, claimId } = parseURI(URI);
|
const {
|
||||||
|
streamName,
|
||||||
|
streamClaimId,
|
||||||
|
channelName,
|
||||||
|
channelClaimId,
|
||||||
|
primaryBidPosition,
|
||||||
|
primaryClaimSequence,
|
||||||
|
secondaryBidPosition,
|
||||||
|
secondaryClaimSequence,
|
||||||
|
} = parseURI(URL);
|
||||||
return buildURI(
|
return buildURI(
|
||||||
{ claimName, path, claimSequence, bidPosition, claimId },
|
{
|
||||||
|
streamName,
|
||||||
|
streamClaimId,
|
||||||
|
channelName,
|
||||||
|
channelClaimId,
|
||||||
|
primaryBidPosition,
|
||||||
|
primaryClaimSequence,
|
||||||
|
secondaryBidPosition,
|
||||||
|
secondaryClaimSequence,
|
||||||
|
},
|
||||||
true,
|
true,
|
||||||
'https://open.lbry.com/'
|
'https://open.lbry.com/'
|
||||||
);
|
);
|
||||||
|
|
|
@ -328,10 +328,10 @@ export function doClaimSearch(
|
||||||
|
|
||||||
const success = (data: ClaimSearchResponse) => {
|
const success = (data: ClaimSearchResponse) => {
|
||||||
const resolveInfo = {};
|
const resolveInfo = {};
|
||||||
const uris = [];
|
const urls = [];
|
||||||
data.items.forEach((stream: Claim) => {
|
data.items.forEach((stream: Claim) => {
|
||||||
resolveInfo[stream.permanent_url] = { stream };
|
resolveInfo[stream.canonical_url] = { stream };
|
||||||
uris.push(stream.permanent_url);
|
urls.push(stream.canonical_url);
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
|
@ -339,7 +339,7 @@ export function doClaimSearch(
|
||||||
data: {
|
data: {
|
||||||
query,
|
query,
|
||||||
resolveInfo,
|
resolveInfo,
|
||||||
uris,
|
urls,
|
||||||
append: options.page && options.page !== 1,
|
append: options.page && options.page !== 1,
|
||||||
pageSize: options.page_size,
|
pageSize: options.page_size,
|
||||||
},
|
},
|
||||||
|
|
|
@ -112,18 +112,26 @@ export const doSearch = (
|
||||||
|
|
||||||
fetch(`${CONNECTION_STRING}search?${queryWithOptions}`)
|
fetch(`${CONNECTION_STRING}search?${queryWithOptions}`)
|
||||||
.then(handleFetchResponse)
|
.then(handleFetchResponse)
|
||||||
.then((data: Array<{ name: String, claimId: string }>) => {
|
.then((data: Array<{ name: string, claimId: string }>) => {
|
||||||
const uris = [];
|
const uris = [];
|
||||||
const actions = [];
|
const actions = [];
|
||||||
|
|
||||||
data.forEach(result => {
|
data.forEach(result => {
|
||||||
if (result.name) {
|
if (result) {
|
||||||
const uri = buildURI({
|
const { name, claimId } = result;
|
||||||
claimName: result.name,
|
const urlObj = {};
|
||||||
claimId: result.claimId,
|
|
||||||
});
|
if (name.startsWith('@')) {
|
||||||
actions.push(doResolveUri(uri));
|
urlObj.channelName = name;
|
||||||
uris.push(uri);
|
urlObj.channelClaimId = claimId;
|
||||||
|
} else {
|
||||||
|
urlObj.streamName = name;
|
||||||
|
urlObj.streamClaimId = claimId;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = buildURI(urlObj);
|
||||||
|
actions.push(doResolveUri(url));
|
||||||
|
uris.push(url);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -66,26 +66,25 @@ function handleClaimAction(state: State, action: any): State {
|
||||||
const byId = Object.assign({}, state.byId);
|
const byId = Object.assign({}, state.byId);
|
||||||
const channelClaimCounts = Object.assign({}, state.channelClaimCounts);
|
const channelClaimCounts = Object.assign({}, state.channelClaimCounts);
|
||||||
|
|
||||||
Object.entries(resolveInfo).forEach(([uri: string, resolveResponse: Claim]) => {
|
Object.entries(resolveInfo).forEach(([url: string, resolveResponse: ResolveResponse]) => {
|
||||||
// $FlowFixMe
|
// $FlowFixMe
|
||||||
if (resolveResponse.claimsInChannel) {
|
const { claimsInChannel, stream, channel } = resolveResponse;
|
||||||
// $FlowFixMe
|
if (claimsInChannel) {
|
||||||
channelClaimCounts[uri] = resolveResponse.claimsInChannel;
|
channelClaimCounts[url] = claimsInChannel;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// $FlowFixMe
|
|
||||||
Object.entries(resolveInfo).forEach(([uri, { channel, stream }]) => {
|
|
||||||
if (stream) {
|
if (stream) {
|
||||||
byId[stream.claim_id] = stream;
|
byId[stream.claim_id] = stream;
|
||||||
byUri[uri] = stream.claim_id;
|
byUri[url] = stream.claim_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (channel) {
|
if (channel) {
|
||||||
byId[channel.claim_id] = channel;
|
byId[channel.claim_id] = channel;
|
||||||
byUri[stream ? channel.permanent_url : uri] = channel.claim_id;
|
byUri[stream ? channel.canonical_url : url] = channel.claim_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!stream && !channel) {
|
if (!stream && !channel) {
|
||||||
byUri[uri] = null;
|
byUri[url] = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -305,20 +304,20 @@ reducers[ACTIONS.CLAIM_SEARCH_COMPLETED] = (state: State, action: any): State =>
|
||||||
{},
|
{},
|
||||||
state.claimSearchByQueryLastPageReached
|
state.claimSearchByQueryLastPageReached
|
||||||
);
|
);
|
||||||
const { append, query, uris, pageSize } = action.data;
|
const { append, query, urls, pageSize } = action.data;
|
||||||
|
|
||||||
if (append) {
|
if (append) {
|
||||||
// todo: check for duplicate uris when concatenating?
|
// todo: check for duplicate urls when concatenating?
|
||||||
claimSearchByQuery[query] =
|
claimSearchByQuery[query] =
|
||||||
claimSearchByQuery[query] && claimSearchByQuery[query].length
|
claimSearchByQuery[query] && claimSearchByQuery[query].length
|
||||||
? claimSearchByQuery[query].concat(uris)
|
? claimSearchByQuery[query].concat(urls)
|
||||||
: uris;
|
: urls;
|
||||||
} else {
|
} else {
|
||||||
claimSearchByQuery[query] = uris;
|
claimSearchByQuery[query] = urls;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the returned number of uris is less than the page size, so we're on the last page
|
// the returned number of urls is less than the page size, so we're on the last page
|
||||||
claimSearchByQueryLastPageReached[query] = uris.length < pageSize;
|
claimSearchByQueryLastPageReached[query] = urls.length < pageSize;
|
||||||
|
|
||||||
delete fetchingClaimSearchByQuery[query];
|
delete fetchingClaimSearchByQuery[query];
|
||||||
|
|
||||||
|
|
|
@ -62,8 +62,10 @@ export const makeSelectClaimIsPending = (uri: string) =>
|
||||||
selectPendingById,
|
selectPendingById,
|
||||||
pendingById => {
|
pendingById => {
|
||||||
let claimId;
|
let claimId;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
({ claimId } = parseURI(uri));
|
const { isChannel, channelClaimId, streamClaimId } = parseURI(uri);
|
||||||
|
claimId = isChannel ? channelClaimId : streamClaimId;
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
if (claimId) {
|
if (claimId) {
|
||||||
|
@ -76,7 +78,8 @@ export const makeSelectPendingByUri = (uri: string) =>
|
||||||
createSelector(
|
createSelector(
|
||||||
selectPendingById,
|
selectPendingById,
|
||||||
pendingById => {
|
pendingById => {
|
||||||
const { claimId } = parseURI(uri);
|
const { isChannel, channelClaimId, streamClaimId } = parseURI(uri);
|
||||||
|
const claimId = isChannel ? channelClaimId : streamClaimId;
|
||||||
return pendingById[claimId];
|
return pendingById[claimId];
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -90,13 +93,16 @@ export const makeSelectClaimForUri = (uri: string) =>
|
||||||
// It won't be in claimsByUri because resolving it will return nothing
|
// It won't be in claimsByUri because resolving it will return nothing
|
||||||
|
|
||||||
let valid;
|
let valid;
|
||||||
let claimId;
|
let channelClaimId;
|
||||||
|
let streamClaimId;
|
||||||
|
let isChannel;
|
||||||
try {
|
try {
|
||||||
({ claimId } = parseURI(uri));
|
({ isChannel, channelClaimId, streamClaimId } = parseURI(uri));
|
||||||
valid = true;
|
valid = true;
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
if (valid) {
|
if (valid) {
|
||||||
|
const claimId = isChannel ? channelClaimId : streamClaimId;
|
||||||
const pendingClaim = pendingById[claimId];
|
const pendingClaim = pendingById[claimId];
|
||||||
|
|
||||||
if (pendingClaim) {
|
if (pendingClaim) {
|
||||||
|
@ -521,23 +527,18 @@ export const selectClaimSearchByQueryLastPageReached = createSelector(
|
||||||
state => state.claimSearchByQueryLastPageReached || {}
|
state => state.claimSearchByQueryLastPageReached || {}
|
||||||
);
|
);
|
||||||
|
|
||||||
export const makeSelectClaimSearchUrisByOptions = (options: {}) =>
|
|
||||||
createSelector(
|
|
||||||
selectClaimSearchByQuery,
|
|
||||||
byQuery => {
|
|
||||||
// We don't care what options are passed to this selector. Just forward them.
|
|
||||||
// $FlowFixMe
|
|
||||||
const query = createNormalizedClaimSearchKey(options);
|
|
||||||
return byQuery[query];
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const makeSelectShortUrlForUri = (uri: string) =>
|
export const makeSelectShortUrlForUri = (uri: string) =>
|
||||||
createSelector(
|
createSelector(
|
||||||
makeSelectClaimForUri(uri),
|
makeSelectClaimForUri(uri),
|
||||||
claim => claim && claim.short_url
|
claim => claim && claim.short_url
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const makeSelectCanonicalUrlForUri = (uri: string) =>
|
||||||
|
createSelector(
|
||||||
|
makeSelectClaimForUri(uri),
|
||||||
|
claim => claim && claim.canonical_url
|
||||||
|
);
|
||||||
|
|
||||||
export const makeSelectSupportsForUri = (uri: string) =>
|
export const makeSelectSupportsForUri = (uri: string) =>
|
||||||
createSelector(
|
createSelector(
|
||||||
selectSupportsByOutpoint,
|
selectSupportsByOutpoint,
|
||||||
|
@ -548,11 +549,12 @@ export const makeSelectSupportsForUri = (uri: string) =>
|
||||||
}
|
}
|
||||||
|
|
||||||
const { claim_id: claimId } = claim;
|
const { claim_id: claimId } = claim;
|
||||||
let total = parseFloat("0.0");
|
let total = 0;
|
||||||
|
|
||||||
Object.values(byOutpoint).forEach(support => {
|
Object.values(byOutpoint).forEach(support => {
|
||||||
const { claim_id, amount } = support
|
// $FlowFixMe
|
||||||
total = (claim_id === claimId && amount) ? total + parseFloat(amount) : total;
|
const { claim_id, amount } = support;
|
||||||
|
total = claim_id === claimId && amount ? total + parseFloat(amount) : total;
|
||||||
});
|
});
|
||||||
|
|
||||||
return total;
|
return total;
|
||||||
|
|
|
@ -33,8 +33,16 @@ export const selectIsStillEditing = createSelector(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { isChannel: currentIsChannel, claimName: currentClaimName, contentName: currentContentName } = parseURI(uri);
|
const {
|
||||||
const { isChannel: editIsChannel, claimName: editClaimName, contentName: editContentName } = parseURI(editingURI);
|
isChannel: currentIsChannel,
|
||||||
|
claimName: currentClaimName,
|
||||||
|
contentName: currentContentName,
|
||||||
|
} = parseURI(uri);
|
||||||
|
const {
|
||||||
|
isChannel: editIsChannel,
|
||||||
|
claimName: editClaimName,
|
||||||
|
contentName: editContentName,
|
||||||
|
} = parseURI(editingURI);
|
||||||
|
|
||||||
// Depending on the previous/current use of a channel, we need to compare different things
|
// Depending on the previous/current use of a channel, we need to compare different things
|
||||||
// ex: going from a channel to anonymous, the new uri won't return contentName, so we need to use claimName
|
// ex: going from a channel to anonymous, the new uri won't return contentName, so we need to use claimName
|
||||||
|
@ -60,7 +68,9 @@ export const selectMyClaimForUri = createSelector(
|
||||||
return isStillEditing
|
return isStillEditing
|
||||||
? claimsById[editClaimId]
|
? claimsById[editClaimId]
|
||||||
: myClaims.find(claim =>
|
: myClaims.find(claim =>
|
||||||
!contentName ? claim.name === claimName : claim.name === contentName || claim.name === claimName
|
!contentName
|
||||||
|
? claim.name === claimName
|
||||||
|
: claim.name === contentName || claim.name === claimName
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -75,7 +85,7 @@ export const selectIsResolvingPublishUris = createSelector(
|
||||||
|
|
||||||
let isResolvingShortUri;
|
let isResolvingShortUri;
|
||||||
if (isChannel) {
|
if (isChannel) {
|
||||||
const shortUri = buildURI({ contentName: name });
|
const shortUri = buildURI({ streamName: name });
|
||||||
isResolvingShortUri = resolvingUris.includes(shortUri);
|
isResolvingShortUri = resolvingUris.includes(shortUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,15 +77,15 @@ export const selectSearchSuggestions: Array<SearchSuggestion> = createSelector(
|
||||||
let searchSuggestions = [];
|
let searchSuggestions = [];
|
||||||
try {
|
try {
|
||||||
const uri = normalizeURI(query);
|
const uri = normalizeURI(query);
|
||||||
const { claimName, isChannel } = parseURI(uri);
|
const { channelName, streamName, isChannel } = parseURI(uri);
|
||||||
searchSuggestions.push(
|
searchSuggestions.push(
|
||||||
{
|
{
|
||||||
value: claimName,
|
value: streamName,
|
||||||
type: SEARCH_TYPES.SEARCH,
|
type: SEARCH_TYPES.SEARCH,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: uri,
|
value: uri,
|
||||||
shorthand: isChannel ? claimName.slice(1) : claimName,
|
shorthand: isChannel ? channelName : streamName,
|
||||||
type: isChannel ? SEARCH_TYPES.CHANNEL : SEARCH_TYPES.FILE,
|
type: isChannel ? SEARCH_TYPES.CHANNEL : SEARCH_TYPES.FILE,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -110,11 +110,11 @@ export const selectSearchSuggestions: Array<SearchSuggestion> = createSelector(
|
||||||
// determine if it's a channel
|
// determine if it's a channel
|
||||||
try {
|
try {
|
||||||
const uri = normalizeURI(suggestion);
|
const uri = normalizeURI(suggestion);
|
||||||
const { claimName, isChannel } = parseURI(uri);
|
const { channelName, streamName, isChannel } = parseURI(uri);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
value: uri,
|
value: uri,
|
||||||
shorthand: isChannel ? claimName.slice(1) : claimName,
|
shorthand: isChannel ? channelName : streamName,
|
||||||
type: isChannel ? SEARCH_TYPES.CHANNEL : SEARCH_TYPES.FILE,
|
type: isChannel ? SEARCH_TYPES.CHANNEL : SEARCH_TYPES.FILE,
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
// @flow
|
|
||||||
import { parseURI } from 'lbryURI';
|
|
||||||
|
|
||||||
export const formatLbryUriForWeb = (uri: string) => {
|
|
||||||
const { claimName, claimId } = parseURI(uri);
|
|
||||||
|
|
||||||
let webUrl = `/${claimName}`;
|
|
||||||
if (claimId) {
|
|
||||||
webUrl += `/${claimId}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return webUrl;
|
|
||||||
};
|
|
Loading…
Reference in a new issue