This commit is contained in:
infinite-persistence 2021-07-09 22:18:17 +08:00
commit 35e4c604ef
No known key found for this signature in database
GPG key ID: B9C3252EDC3D0AA0
4 changed files with 65 additions and 31 deletions
static
ui
component/claimMenuList
util
web/src

View file

@ -701,7 +701,11 @@
"Bid position must be a number.": "Bid position must be a number.", "Bid position must be a number.": "Bid position must be a number.",
"Copy": "Copy", "Copy": "Copy",
"Copy Link": "Copy Link", "Copy Link": "Copy Link",
"Link copied.": "Link copied.",
"Failed to copy link.": "Failed to copy link.",
"Copy RSS URL": "Copy RSS URL", "Copy RSS URL": "Copy RSS URL",
"RSS URL copied.": "RSS URL copied.",
"Failed to copy RSS URL.": "Failed to copy RSS URL.",
"Text copied": "Text copied", "Text copied": "Text copied",
"Rewards Disabled": "Rewards Disabled", "Rewards Disabled": "Rewards Disabled",
"Wallet servers are used to relay data to and from the LBRY blockchain. They also determine what content shows in trending or is blocked. %learn_more%.": "Wallet servers are used to relay data to and from the LBRY blockchain. They also determine what content shows in trending or is blocked. %learn_more%.", "Wallet servers are used to relay data to and from the LBRY blockchain. They also determine what content shows in trending or is blocked. %learn_more%.": "Wallet servers are used to relay data to and from the LBRY blockchain. They also determine what content shows in trending or is blocked. %learn_more%.",

View file

@ -44,7 +44,7 @@ type Props = {
collectionName?: string, collectionName?: string,
collectionId: string, collectionId: string,
isMyCollection: boolean, isMyCollection: boolean,
doToast: ({ message: string }) => void, doToast: ({ message: string, isError?: boolean }) => void,
claimIsMine: boolean, claimIsMine: boolean,
fileInfo: FileListItem, fileInfo: FileListItem,
prepareEdit: ({}, string, {}) => void, prepareEdit: ({}, string, {}) => void,
@ -103,7 +103,7 @@ function ClaimMenuList(props: Props) {
} }
const shareUrl: string = generateShareUrl(SHARE_DOMAIN, uri); const shareUrl: string = generateShareUrl(SHARE_DOMAIN, uri);
const rssUrl: string = generateRssUrl(URL, uri); const rssUrl: string = isChannel ? generateRssUrl(URL, uri) : '';
const isCollectionClaim = claim && claim.value_type === 'collection'; const isCollectionClaim = claim && claim.value_type === 'collection';
// $FlowFixMe // $FlowFixMe
const isPlayable = const isPlayable =
@ -183,12 +183,23 @@ function ClaimMenuList(props: Props) {
} }
} }
function copyToClipboard(textToCopy, successMsg, failureMsg) {
navigator.clipboard
.writeText(textToCopy)
.then(() => {
doToast({ message: __(successMsg) });
})
.catch(() => {
doToast({ message: __(failureMsg), isError: true });
});
}
function handleCopyRssLink() { function handleCopyRssLink() {
navigator.clipboard.writeText(rssUrl); copyToClipboard(rssUrl, 'RSS URL copied.', 'Failed to copy RSS URL.');
} }
function handleCopyLink() { function handleCopyLink() {
navigator.clipboard.writeText(shareUrl); copyToClipboard(shareUrl, 'Link copied.', 'Failed to copy link.');
} }
function handleReportContent() { function handleReportContent() {

View file

@ -150,6 +150,6 @@ export const generateShareUrl = (
export const generateRssUrl = (domain, lbryUrl) => { export const generateRssUrl = (domain, lbryUrl) => {
const { channelName, channelClaimId } = parseURI(lbryUrl); const { channelName, channelClaimId } = parseURI(lbryUrl);
const url = `${domain}/$/rss/@${channelName}/${channelClaimId}`; const url = `${domain}/$/rss/@${channelName}/${channelClaimId.slice(0, 2)}`;
return url; return url;
}; };

View file

@ -6,6 +6,8 @@ const SDK_API_PATH = `${LBRY_WEB_API}/api/v1`;
const proxyURL = `${SDK_API_PATH}/proxy`; const proxyURL = `${SDK_API_PATH}/proxy`;
Lbry.setDaemonConnectionString(proxyURL); Lbry.setDaemonConnectionString(proxyURL);
const NUM_ENTRIES = 500;
async function doClaimSearch(options) { async function doClaimSearch(options) {
let results; let results;
try { try {
@ -14,15 +16,17 @@ async function doClaimSearch(options) {
return results ? results.items : undefined; return results ? results.items : undefined;
} }
async function getChannelClaim(claimId) { async function getChannelClaim(name, claimId) {
const options = { let claim;
claim_ids: [claimId], try {
page_size: 1, const url = `lbry://${name}#${claimId}`;
no_totals: true, const response = await Lbry.resolve({ urls: [url] });
};
const claims = await doClaimSearch(options); if (response && response[url] && !response[url].error) {
return claims ? claims[0] : undefined; claim = response && response[url];
}
} catch {}
return claim || 'The RSS URL is invalid or is not associated with any channel.';
} }
async function getClaimsFromChannel(claimId, count) { async function getClaimsFromChannel(claimId, count) {
@ -38,40 +42,49 @@ async function getClaimsFromChannel(claimId, count) {
return await doClaimSearch(options); return await doClaimSearch(options);
} }
async function getFeed(channelClaim) { async function getFeed(channelClaim, feedLink) {
const replaceLineFeeds = (str) => str.replace(/(?:\r\n|\r|\n)/g, '<br>'); const replaceLineFeeds = (str) => str.replace(/(?:\r\n|\r|\n)/g, '<br />');
const fmtDescription = (description) => replaceLineFeeds(description);
const sanitizeThumbsUrl = (url) => {
if (typeof url === 'string' && url.startsWith('https://')) {
return encodeURI(url).replace(/&/g, '%26');
}
return '';
};
const value = channelClaim.value; const value = channelClaim.value;
const title = value ? value.title : channelClaim.name; const title = value ? value.title : channelClaim.name;
const options = { const options = {
title: title + ' on ' + SITE_NAME,
description: value && value.description ? replaceLineFeeds(value.description) : '',
link: `${URL}/${channelClaim.name}:${channelClaim.claim_id}`,
favicon: URL + '/public/favicon.png', favicon: URL + '/public/favicon.png',
generator: SITE_NAME + ' RSS Feed', generator: SITE_NAME + ' RSS Feed',
image: value && value.thumbnail ? value.thumbnail.url : '', title: title + ' on ' + SITE_NAME,
description: fmtDescription(value && value.description ? value.description : ''),
link: encodeURI(`${URL}/${channelClaim.name}:${channelClaim.claim_id}`),
image: sanitizeThumbsUrl(value && value.thumbnail ? value.thumbnail.url : ''),
feedLinks: {
rss: encodeURI(feedLink),
},
author: { author: {
name: channelClaim.name, name: encodeURI(channelClaim.name),
link: URL + '/' + channelClaim.name + ':' + channelClaim.claim_id, link: encodeURI(URL + '/' + channelClaim.name + ':' + channelClaim.claim_id),
}, },
}; };
const feed = new Feed(options); const feed = new Feed(options);
const latestClaims = await getClaimsFromChannel(channelClaim.claim_id, NUM_ENTRIES);
const latestClaims = await getClaimsFromChannel(channelClaim.claim_id, 50);
latestClaims.forEach((c) => { latestClaims.forEach((c) => {
const meta = c.meta; const meta = c.meta;
const value = c.value; const value = c.value;
feed.addItem({ feed.addItem({
guid: c.claim_id,
id: c.claim_id, id: c.claim_id,
title: value ? value.title : c.name, guid: encodeURI(URL + '/' + c.name + ':' + c.claim_id),
description: value && value.description ? replaceLineFeeds(value.description) : '', title: value && value.title ? value.title : c.name,
image: value && value.thumbnail ? value.thumbnail.url : '', description: fmtDescription(value && value.description ? value.description : ''),
link: URL + '/' + c.name + ':' + c.claim_id, image: sanitizeThumbsUrl(value && value.thumbnail ? value.thumbnail.url : ''),
link: encodeURI(URL + '/' + c.name + ':' + c.claim_id),
date: new Date(meta ? meta.creation_timestamp * 1000 : null), date: new Date(meta ? meta.creation_timestamp * 1000 : null),
}); });
}); });
@ -79,18 +92,24 @@ async function getFeed(channelClaim) {
return feed; return feed;
} }
function postProcess(feed) {
// Handle 'Feed' creating an invalid MIME type when trying to guess
// from 'https://thumbnails.lbry.com/UCgQ8eREJzR1dO' style of URLs.
return feed.replace(/type="image\/\/.*"\/>/g, 'type="image/*"/>');
}
async function getRss(ctx) { async function getRss(ctx) {
if (!ctx.params.claimName || !ctx.params.claimId) { if (!ctx.params.claimName || !ctx.params.claimId) {
return 'Invalid URL'; return 'Invalid URL';
} }
const channelClaim = await getChannelClaim(ctx.params.claimId); const channelClaim = await getChannelClaim(ctx.params.claimName, ctx.params.claimId);
if (typeof channelClaim === 'string' || !channelClaim) { if (typeof channelClaim === 'string' || !channelClaim) {
return channelClaim; return channelClaim;
} }
const feed = await getFeed(channelClaim); const feed = await getFeed(channelClaim, `${URL}/$/rss/${ctx.params.claimName}/${ctx.params.claimId}`);
return feed.rss2(); return postProcess(feed.rss2());
} }
module.exports = { getRss }; module.exports = { getRss };