diff --git a/src/platforms/web/server.js b/src/platforms/web/server.js index 3e6fabe89..f3d44442b 100644 --- a/src/platforms/web/server.js +++ b/src/platforms/web/server.js @@ -74,6 +74,10 @@ app.get('*', async (req, res) => { return res.redirect(301, req.url.replace(/([^/:]+)\/([^:/]+)/, '$1:$2')); // test against urlPath, but use req.url to retain parameters } + if (urlPath.endsWith('/') && urlPath.length > 1) { + return res.redirect(301, req.url.replace(/\/$/, '')); + } + if (urlPath.length > 0 && urlPath[0] !== '$') { const { isChannel, streamName, channelName, channelClaimId, streamClaimId } = parseURI(urlPath.replace(/:/g, '#')); const claimName = isChannel ? '@' + channelName : streamName; diff --git a/src/ui/component/claimPreview/view.jsx b/src/ui/component/claimPreview/view.jsx index a5578547f..78587df54 100644 --- a/src/ui/component/claimPreview/view.jsx +++ b/src/ui/component/claimPreview/view.jsx @@ -141,7 +141,7 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { if (onClick) { onClick(e); } else if ((isChannel || title) && !pending) { - history.push(formatLbryUriForWeb(uri)); + history.push(formatLbryUriForWeb(claim && claim.canonical_url ? claim.canonical_url : uri)); } } diff --git a/src/ui/page/show/view.jsx b/src/ui/page/show/view.jsx index 0c955a3e9..85d87e5ec 100644 --- a/src/ui/page/show/view.jsx +++ b/src/ui/page/show/view.jsx @@ -29,6 +29,16 @@ class ShowPage extends React.PureComponent { componentDidUpdate() { const { isResolvingUri, resolveUri, claim, uri } = this.props; + + // @if TARGET='web' + if (claim && claim.canonical_url) { + const canonicalUrlPath = '/' + claim.canonical_url.replace(/^lbry:\/\//, '').replace(/#/g, ':'); + if (canonicalUrlPath !== window.location.pathname) { + history.replaceState(history.state, '', canonicalUrlPath); + } + } + // @endif + if (!isResolvingUri && uri && claim === undefined) { resolveUri(uri); }