use same code for handling open links on lbry.tv and desktop
This commit is contained in:
parent
8839bb08db
commit
bf512e8338
21 changed files with 156 additions and 140 deletions
|
@ -2,7 +2,7 @@ module.exports = api => {
|
|||
api.cache(false);
|
||||
|
||||
return {
|
||||
presets: ['@babel/env', '@babel/react', '@babel/flow'],
|
||||
presets: [['@babel/env', { loose: true, modules: false }], '@babel/react', '@babel/flow'],
|
||||
plugins: [
|
||||
'import-glob',
|
||||
'@babel/plugin-transform-runtime',
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const config = {
|
||||
WEBPACK_WEB_PORT: 9090,
|
||||
WEBPACK_ELECTRON_PORT: 9091,
|
||||
WEB_SERVER_PORT: 80,
|
||||
WEB_SERVER_PORT: 1337,
|
||||
DOMAIN: 'lbry.tv',
|
||||
URL: 'https://lbry.tv',
|
||||
SITE_TITLE: 'lbry.tv',
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
const config = require('../../config');
|
||||
const PAGES = require('../../ui/constants/pages');
|
||||
const { formatCustomUrl } = require('../../ui/util/url');
|
||||
const { parseURI } = require('lbry-redux');
|
||||
|
||||
async function redirectMiddleware(ctx, next) {
|
||||
const requestHost = ctx.host;
|
||||
|
@ -16,22 +18,14 @@ async function redirectMiddleware(ctx, next) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (requestHost === 'open.lbry.com') {
|
||||
let redirectUrl = config.URL;
|
||||
if (requestHost === 'open.lbry.com' || requestHost === 'open.lbry.io') {
|
||||
const openQuery = '?src=open';
|
||||
const matches = /(\/\?)([a-z]*)(.*)/.exec(url);
|
||||
let redirectUrl = config.URL + formatCustomUrl(url, openQuery);
|
||||
|
||||
if (matches && matches.length) {
|
||||
[, , page, queryString] = matches;
|
||||
|
||||
// This is a lbry app page. Make sure to add the leading `/$/`
|
||||
if (page && Object.values(PAGES).includes(page)) {
|
||||
redirectUrl += '/$/' + page;
|
||||
}
|
||||
|
||||
redirectUrl += openQuery + queryString;
|
||||
if (redirectUrl.includes('?')) {
|
||||
redirectUrl = redirectUrl.replace('?', `${openQuery}&`);
|
||||
} else {
|
||||
redirectUrl += path + openQuery;
|
||||
redirectUrl += openQuery;
|
||||
}
|
||||
|
||||
ctx.redirect(redirectUrl);
|
||||
|
|
|
@ -4,7 +4,7 @@ import React, { forwardRef, useRef } from 'react';
|
|||
import Icon from 'component/common/icon';
|
||||
import classnames from 'classnames';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import { formatLbryUriForWeb } from 'util/uri';
|
||||
import { formatLbryUrlForWeb } from 'util/url';
|
||||
import { OutboundLink } from 'react-ga';
|
||||
import * as PAGES from 'constants/pages';
|
||||
import useCombinedRefs from 'effects/use-combined-refs';
|
||||
|
@ -108,7 +108,7 @@ const Button = forwardRef<any, {}>((props: Props, ref: any) => {
|
|||
let path = navigate;
|
||||
if (path) {
|
||||
if (path.startsWith('lbry://')) {
|
||||
path = formatLbryUriForWeb(path);
|
||||
path = formatLbryUrlForWeb(path);
|
||||
} else if (!path.startsWith('/')) {
|
||||
// Force a leading slash so new paths aren't appended on to the current path
|
||||
path = `/${path}`;
|
||||
|
|
|
@ -5,7 +5,7 @@ import classnames from 'classnames';
|
|||
import { parseURI, convertToShareLink } from 'lbry-redux';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { openCopyLinkMenu } from 'util/context-menu';
|
||||
import { formatLbryUriForWeb } from 'util/uri';
|
||||
import { formatLbryUrlForWeb } from 'util/url';
|
||||
import { isEmpty } from 'util/object';
|
||||
import CardMedia from 'component/cardMedia';
|
||||
import UriIndicator from 'component/uriIndicator';
|
||||
|
@ -149,7 +149,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
|||
if (onClick) {
|
||||
onClick(e);
|
||||
} else if ((isChannel || title) && !pending) {
|
||||
history.push(formatLbryUriForWeb(claim && claim.canonical_url ? claim.canonical_url : uri));
|
||||
history.push(formatLbryUrlForWeb(claim && claim.canonical_url ? claim.canonical_url : uri));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import * as React from 'react';
|
|||
import { isURIValid } from 'lbry-redux';
|
||||
import Button from 'component/button';
|
||||
import ClaimLink from 'component/claimLink';
|
||||
import { isLBRYDomain } from 'util/uri';
|
||||
import { isLBRYDomain } from 'util/url';
|
||||
|
||||
type Props = {
|
||||
href: string,
|
||||
|
|
|
@ -7,7 +7,7 @@ import ImageViewer from 'component/viewers/imageViewer';
|
|||
import AppViewer from 'component/viewers/appViewer';
|
||||
import Button from 'component/button';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { formatLbryUriForWeb } from 'util/uri';
|
||||
import { formatLbryUrlForWeb } from 'util/url';
|
||||
// @if TARGET='web'
|
||||
import { generateStreamUrl } from 'util/lbrytv';
|
||||
// @endif
|
||||
|
@ -73,7 +73,7 @@ class FileRender extends React.PureComponent<Props> {
|
|||
onEndedCb() {
|
||||
const { autoplay, nextUnplayed, history } = this.props;
|
||||
if (autoplay && nextUnplayed) {
|
||||
history.push(formatLbryUriForWeb(nextUnplayed));
|
||||
history.push(formatLbryUrlForWeb(nextUnplayed));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import classnames from 'classnames';
|
|||
import Button from 'component/button';
|
||||
import { FormField } from 'component/common/form';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { formatLbryUriForWeb } from 'util/uri';
|
||||
import { formatLbryUrlForWeb } from 'util/url';
|
||||
|
||||
type Props = {
|
||||
lastViewed: number,
|
||||
|
@ -39,7 +39,7 @@ class NavigationHistoryItem extends React.PureComponent<Props> {
|
|||
({ title } = claim.value);
|
||||
}
|
||||
|
||||
const navigatePath = formatLbryUriForWeb(uri);
|
||||
const navigatePath = formatLbryUrlForWeb(uri);
|
||||
const onClick =
|
||||
onSelect ||
|
||||
function() {
|
||||
|
|
|
@ -4,7 +4,7 @@ import UriIndicator from 'component/uriIndicator';
|
|||
import TruncatedText from 'component/common/truncated-text';
|
||||
import MarkdownPreview from 'component/common/markdown-preview';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { formatLbryUriForWeb } from 'util/uri';
|
||||
import { formatLbryUrlForWeb } from 'util/url';
|
||||
|
||||
type Props = {
|
||||
uri: string,
|
||||
|
@ -17,7 +17,7 @@ type Props = {
|
|||
class PreviewLink extends React.PureComponent<Props> {
|
||||
handleClick = () => {
|
||||
const { uri, history } = this.props;
|
||||
history.push(formatLbryUriForWeb(uri));
|
||||
history.push(formatLbryUrlForWeb(uri));
|
||||
};
|
||||
|
||||
render() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// @flow
|
||||
import * as PAGES from 'constants/pages';
|
||||
import React, { useEffect, Suspense } from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { Route, Redirect, Switch, withRouter } from 'react-router-dom';
|
||||
import SettingsPage from 'page/settings';
|
||||
import HelpPage from 'page/help';
|
||||
|
@ -69,7 +69,6 @@ function AppRouter(props: Props) {
|
|||
}, [currentScroll, pathname]);
|
||||
|
||||
return (
|
||||
<Suspense fallback={<span>LODIFJDSLKJFSLDKJFLDJ</span>}>
|
||||
<Switch>
|
||||
<Route path="/" exact component={DiscoverPage} />
|
||||
<Route path={`/$/${PAGES.DISCOVER}`} exact component={DiscoverPage} />
|
||||
|
@ -99,7 +98,6 @@ function AppRouter(props: Props) {
|
|||
<Route path="/:claimName/:streamName" exact component={ShowPage} />
|
||||
<Route path="/*" component={FourOhFourPage} />
|
||||
</Switch>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
import analytics from 'analytics';
|
||||
import Wunderbar from './view';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { formatLbryUriForWeb } from 'util/uri';
|
||||
import { formatLbryUrlForWeb } from 'util/url';
|
||||
|
||||
const select = state => ({
|
||||
suggestions: selectSearchSuggestions(state),
|
||||
|
@ -26,7 +26,7 @@ const perform = (dispatch, ownProps) => ({
|
|||
analytics.apiLogSearch();
|
||||
},
|
||||
onSubmit: uri => {
|
||||
const path = formatLbryUriForWeb(uri);
|
||||
const path = formatLbryUrlForWeb(uri);
|
||||
ownProps.history.push(path);
|
||||
dispatch(doUpdateSearchQuery(''));
|
||||
},
|
||||
|
|
33
ui/index.jsx
33
ui/index.jsx
|
@ -13,7 +13,7 @@ import * as MODALS from 'constants/modal_types';
|
|||
import React, { Fragment, useState, useEffect } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import { doConditionalAuthNavigate, doDaemonReady, doAutoUpdate, doOpenModal, doHideModal } from 'redux/actions/app';
|
||||
import { doDaemonReady, doAutoUpdate, doOpenModal, doHideModal } from 'redux/actions/app';
|
||||
import { Lbry, doToast, isURIValid, setSearchApi, apiCall } from 'lbry-redux';
|
||||
import { doSetLanguage, doUpdateIsNightAsync } from 'redux/actions/settings';
|
||||
import {
|
||||
|
@ -28,7 +28,7 @@ import pjson from 'package.json';
|
|||
import app from './app';
|
||||
import doLogWarningConsoleMessage from './logWarningConsoleMessage';
|
||||
import { ConnectedRouter, push } from 'connected-react-router';
|
||||
import { formatLbryUriForWeb } from 'util/uri';
|
||||
import { formatLbryUrlForWeb, formatCustomUrl } from 'util/url';
|
||||
import { PersistGate } from 'redux-persist/integration/react';
|
||||
import analytics from 'analytics';
|
||||
import { getAuthToken, setAuthToken } from 'util/saved-passwords';
|
||||
|
@ -70,7 +70,6 @@ Lbry.setOverride(
|
|||
const startTime = Date.now();
|
||||
analytics.startupEvent();
|
||||
|
||||
const APPPAGEURL = 'lbry://?';
|
||||
// @if TARGET='app'
|
||||
const { autoUpdater } = remote.require('electron-updater');
|
||||
autoUpdater.logger = remote.require('electron-log');
|
||||
|
@ -157,25 +156,29 @@ rewards.setCallback('claimRewardSuccess', () => {
|
|||
});
|
||||
|
||||
// @if TARGET='app'
|
||||
ipcRenderer.on('open-uri-requested', (event, uri, newSession) => {
|
||||
if (uri && uri.startsWith('lbry://')) {
|
||||
if (uri.startsWith('lbry://?verify')) {
|
||||
app.store.dispatch(doConditionalAuthNavigate(newSession));
|
||||
} else if (uri.startsWith(APPPAGEURL)) {
|
||||
const navpage = uri.replace(APPPAGEURL, '').toLowerCase();
|
||||
app.store.dispatch(push(`/$/${navpage}`));
|
||||
} else if (isURIValid(uri)) {
|
||||
const formattedUri = formatLbryUriForWeb(uri);
|
||||
app.store.dispatch(push(formattedUri));
|
||||
analytics.openUrlEvent(formattedUri);
|
||||
} else {
|
||||
ipcRenderer.on('open-uri-requested', (event, url, newSession) => {
|
||||
function handleError() {
|
||||
app.store.dispatch(
|
||||
doToast({
|
||||
message: __('Invalid LBRY URL requested'),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const path = url.slice('lbry://'.length);
|
||||
if (path.startsWith('?')) {
|
||||
const redirectUrl = formatCustomUrl(path);
|
||||
return app.store.dispatch(push(redirectUrl));
|
||||
}
|
||||
|
||||
if (isURIValid(url)) {
|
||||
const formattedUrl = formatLbryUrlForWeb(url);
|
||||
analytics.openUrlEvent(formattedUrl);
|
||||
return app.store.dispatch(push(formattedUrl));
|
||||
}
|
||||
|
||||
// If nothing redirected before here the url must be messed up
|
||||
handleError();
|
||||
});
|
||||
|
||||
ipcRenderer.on('language-set', (event, language) => {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// @flow
|
||||
import React, { useRef } from 'react';
|
||||
import { Modal } from 'modal/modal';
|
||||
import { formatPathForWeb } from 'util/uri';
|
||||
import { formatFileSystemPath } from 'util/url';
|
||||
|
||||
type Props = {
|
||||
upload: WebFile => void,
|
||||
|
@ -15,7 +15,7 @@ function ModalAutoGenerateThumbnail(props: Props) {
|
|||
const playerRef = useRef();
|
||||
let videoSrc;
|
||||
if (typeof filePath === 'string') {
|
||||
videoSrc = formatPathForWeb(filePath);
|
||||
videoSrc = formatFileSystemPath(filePath);
|
||||
} else {
|
||||
videoSrc = URL.createObjectURL(filePath);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import { Modal } from 'modal/modal';
|
||||
import { formatPathForWeb } from 'util/uri';
|
||||
import { formatFileSystemPath } from 'util/url';
|
||||
// @if TARGET='app'
|
||||
import { shell } from 'electron';
|
||||
// @endif
|
||||
|
@ -37,7 +37,7 @@ function ModalOpenExternalResource(props: Props) {
|
|||
if (uri) {
|
||||
window.open(uri);
|
||||
} else if (path) {
|
||||
window.open(formatPathForWeb(path));
|
||||
window.open(formatFileSystemPath(path));
|
||||
}
|
||||
// @endif
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import ShareButton from 'component/shareButton';
|
|||
import { Tabs, TabList, Tab, TabPanels, TabPanel } from 'component/common/tabs';
|
||||
import { withRouter } from 'react-router';
|
||||
import Button from 'component/button';
|
||||
import { formatLbryUriForWeb } from 'util/uri';
|
||||
import { formatLbryUrlForWeb } from 'util/url';
|
||||
import ChannelContent from 'component/channelContent';
|
||||
import ChannelAbout from 'component/channelAbout';
|
||||
import ChannelDiscussion from 'component/channelDiscussion';
|
||||
|
@ -93,7 +93,7 @@ function ChannelPage(props: Props) {
|
|||
const tabIndex = currentView === ABOUT_PAGE || editing ? 1 : currentView === DISCUSSION_PAGE ? 2 : 0;
|
||||
|
||||
function onTabChange(newTabIndex) {
|
||||
let url = formatLbryUriForWeb(uri);
|
||||
let url = formatLbryUrlForWeb(uri);
|
||||
let search = '?';
|
||||
|
||||
if (newTabIndex === 0) {
|
||||
|
|
|
@ -6,7 +6,6 @@ import { ipcRenderer, remote } from 'electron';
|
|||
import path from 'path';
|
||||
import * as ACTIONS from 'constants/action_types';
|
||||
import * as MODALS from 'constants/modal_types';
|
||||
import * as PAGES from 'constants/pages';
|
||||
import {
|
||||
Lbry,
|
||||
doBalanceSubscribe,
|
||||
|
@ -36,7 +35,6 @@ import {
|
|||
} from 'redux/selectors/app';
|
||||
import { doAuthenticate, doGetSync } from 'lbryinc';
|
||||
import { lbrySettings as config, version as appVersion } from 'package.json';
|
||||
import { push } from 'connected-react-router';
|
||||
import analytics from 'analytics';
|
||||
import { doSignOutCleanup, deleteSavedPassword, getSavedPassword } from 'util/saved-passwords';
|
||||
|
||||
|
@ -404,17 +402,6 @@ export function doClickCommentButton() {
|
|||
};
|
||||
}
|
||||
|
||||
export function doConditionalAuthNavigate(newSession) {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const modal = selectModal(state);
|
||||
|
||||
if (newSession || (modal && modal.id !== MODALS.EMAIL_COLLECTION)) {
|
||||
dispatch(push(`/$/${PAGES.AUTH}`));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function doToggleSearchExpanded() {
|
||||
return {
|
||||
type: ACTIONS.TOGGLE_SEARCH_EXPANDED,
|
||||
|
|
|
@ -25,7 +25,7 @@ import {
|
|||
} from 'lbry-redux';
|
||||
import { makeSelectCostInfoForUri } from 'lbryinc';
|
||||
import { makeSelectClientSetting, selectosNotificationsEnabled, selectDaemonSettings } from 'redux/selectors/settings';
|
||||
import { formatLbryUriForWeb } from 'util/uri';
|
||||
import { formatLbryUrlForWeb } from 'util/url';
|
||||
|
||||
const DOWNLOAD_POLL_INTERVAL = 250;
|
||||
|
||||
|
@ -85,7 +85,7 @@ export function doUpdateLoadStatus(uri: string, outpoint: string) {
|
|||
silent: false,
|
||||
});
|
||||
notif.onclick = () => {
|
||||
dispatch(push(formatLbryUriForWeb(uri)));
|
||||
dispatch(push(formatLbryUrlForWeb(uri)));
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import { batchActions, doError, selectMyClaims, doPublish, doCheckPendingPublish
|
|||
import { selectosNotificationsEnabled } from 'redux/selectors/settings';
|
||||
import { push } from 'connected-react-router';
|
||||
import analytics from 'analytics';
|
||||
import { formatLbryUriForWeb } from 'util/uri';
|
||||
import { formatLbryUrlForWeb } from 'util/url';
|
||||
import { doOpenModal } from './app';
|
||||
|
||||
export const doPublishDesktop = (filePath: string) => (dispatch: Dispatch, getState: () => {}) => {
|
||||
|
@ -81,7 +81,7 @@ export const doCheckPendingPublishesApp = () => (dispatch: Dispatch, getState: G
|
|||
silent: false,
|
||||
});
|
||||
notif.onclick = () => {
|
||||
dispatch(push(formatLbryUriForWeb(claim.permanent_url)));
|
||||
dispatch(push(formatLbryUrlForWeb(claim.permanent_url)));
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -6,6 +6,10 @@
|
|||
color: var(--color-text-selection);
|
||||
}
|
||||
|
||||
*:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
html {
|
||||
@include font-sans;
|
||||
height: 100%;
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
// @flow
|
||||
|
||||
const LBRY_INC_DOMAINS = ['lbry.io', 'lbry.com', 'lbry.tv', 'lbry.tech', 'lbry.fund', 'spee.ch'];
|
||||
|
||||
export const formatLbryUriForWeb = (uri: string) => {
|
||||
return uri.replace('lbry://', '/').replace(/#/g, ':');
|
||||
};
|
||||
|
||||
export const formatPathForWeb = (path: string) => {
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
|
||||
let webUrl = path.replace(/\\/g, '/');
|
||||
|
||||
if (webUrl[0] !== '/') {
|
||||
webUrl = `/${webUrl}`;
|
||||
}
|
||||
|
||||
return encodeURI(`file://${webUrl}`).replace(/[?#]/g, encodeURIComponent);
|
||||
};
|
||||
|
||||
export const isLBRYDomain = (uri: string) => {
|
||||
if (!uri) {
|
||||
return;
|
||||
}
|
||||
|
||||
const myURL = new URL(uri);
|
||||
const hostname = myURL.hostname;
|
||||
|
||||
for (let domain of LBRY_INC_DOMAINS) {
|
||||
if (hostname.endsWith(domain)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
68
ui/util/url.js
Normal file
68
ui/util/url.js
Normal file
|
@ -0,0 +1,68 @@
|
|||
const LBRY_INC_DOMAINS = ['lbry.io', 'lbry.com', 'lbry.tv', 'lbry.tech', 'lbry.fund', 'spee.ch'];
|
||||
|
||||
exports.formatLbryUrlForWeb = uri => {
|
||||
return uri.replace('lbry://', '/').replace(/#/g, ':');
|
||||
};
|
||||
|
||||
exports.formatFileSystemPath = path => {
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
|
||||
let webUrl = path.replace(/\\/g, '/');
|
||||
|
||||
if (webUrl[0] !== '/') {
|
||||
webUrl = `/${webUrl}`;
|
||||
}
|
||||
|
||||
return encodeURI(`file://${webUrl}`).replace(/[?#]/g, encodeURIComponent);
|
||||
};
|
||||
|
||||
exports.isLBRYDomain = uri => {
|
||||
if (!uri) {
|
||||
return;
|
||||
}
|
||||
|
||||
const myURL = new URL(uri);
|
||||
const hostname = myURL.hostname;
|
||||
|
||||
for (let domain of LBRY_INC_DOMAINS) {
|
||||
if (hostname.endsWith(domain)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/*
|
||||
Function that handles page redirects
|
||||
ex: lbry://?rewards
|
||||
ex: open.lbry.com/?rewards
|
||||
*/
|
||||
exports.formatCustomUrl = path => {
|
||||
// Determine if we need to add a leading "/$/" for app pages
|
||||
const APP_PAGE_REGEX = /(\?)([a-z]*)(.*)/;
|
||||
const appPageMatches = APP_PAGE_REGEX.exec(path);
|
||||
|
||||
if (appPageMatches && appPageMatches.length) {
|
||||
// Definitely an app page (or it's formatted like one)
|
||||
const [, , page, queryString] = appPageMatches;
|
||||
let actualUrl = '/$/' + page;
|
||||
|
||||
if (queryString) {
|
||||
if (queryString.startsWith('?')) {
|
||||
actualUrl += queryString;
|
||||
} else if (queryString.startsWith('&')) {
|
||||
// Replace the leading "&" with a "?" because we must have lost the "?" from the page name
|
||||
// /?rewards&a=b => /$/rewards?a=b
|
||||
actualUrl += `?${queryString.slice(1)}`;
|
||||
}
|
||||
|
||||
return actualUrl;
|
||||
}
|
||||
}
|
||||
|
||||
// Regular claim url
|
||||
return path;
|
||||
};
|
Loading…
Reference in a new issue