add open in app link
This commit is contained in:
parent
d2a1852a8d
commit
2944ed3664
15 changed files with 170 additions and 27 deletions
|
@ -128,7 +128,7 @@
|
||||||
"json-loader": "^0.5.4",
|
"json-loader": "^0.5.4",
|
||||||
"lbry-format": "https://github.com/lbryio/lbry-format.git",
|
"lbry-format": "https://github.com/lbryio/lbry-format.git",
|
||||||
"lbry-redux": "lbryio/lbry-redux#9b11cfed62af7f0f8eb6fa3c88a1f8d2cce46afc",
|
"lbry-redux": "lbryio/lbry-redux#9b11cfed62af7f0f8eb6fa3c88a1f8d2cce46afc",
|
||||||
"lbryinc": "lbryio/lbryinc#2aedf5a188f028f61c45bc7ed0c747a2d4ae453a",
|
"lbryinc": "lbryio/lbryinc#1e897d2c9108848637e1915700d3ae8ce176f9f8",
|
||||||
"lint-staged": "^7.0.2",
|
"lint-staged": "^7.0.2",
|
||||||
"localforage": "^1.7.1",
|
"localforage": "^1.7.1",
|
||||||
"lodash-es": "^4.17.14",
|
"lodash-es": "^4.17.14",
|
||||||
|
|
|
@ -298,7 +298,6 @@
|
||||||
"Continue Anyway": "Continue Anyway",
|
"Continue Anyway": "Continue Anyway",
|
||||||
"This app is running with an incompatible version of the LBRY protocol. You can still use it, but there may be issues. Re-run the installation package for best results.": "This app is running with an incompatible version of the LBRY protocol. You can still use it, but there may be issues. Re-run the installation package for best results.",
|
"This app is running with an incompatible version of the LBRY protocol. You can still use it, but there may be issues. Re-run the installation package for best results.": "This app is running with an incompatible version of the LBRY protocol. You can still use it, but there may be issues. Re-run the installation package for best results.",
|
||||||
"Update ready to install": "Update ready to install",
|
"Update ready to install": "Update ready to install",
|
||||||
"Install now": "Install now",
|
|
||||||
"Upgrade available": "Upgrade available",
|
"Upgrade available": "Upgrade available",
|
||||||
"LBRY Leveled Up": "LBRY Leveled Up",
|
"LBRY Leveled Up": "LBRY Leveled Up",
|
||||||
"Upgrade": "Upgrade",
|
"Upgrade": "Upgrade",
|
||||||
|
@ -903,5 +902,6 @@
|
||||||
"Error Starting Up": "Error Starting Up",
|
"Error Starting Up": "Error Starting Up",
|
||||||
"Reach out to hello@lbry.com for help, or check out %help_link%.": "Reach out to hello@lbry.com for help, or check out %help_link%.",
|
"Reach out to hello@lbry.com for help, or check out %help_link%.": "Reach out to hello@lbry.com for help, or check out %help_link%.",
|
||||||
"You're not following any tags. Smash that %customize% button!": "You're not following any tags. Smash that %customize% button!",
|
"You're not following any tags. Smash that %customize% button!": "You're not following any tags. Smash that %customize% button!",
|
||||||
"customize": "customize"
|
"customize": "customize",
|
||||||
|
"An upgrade is available.": "An upgrade is available."
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// @flow
|
// @flow
|
||||||
import * as ICONS from 'constants/icons';
|
|
||||||
import * as PAGES from 'constants/pages';
|
import * as PAGES from 'constants/pages';
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
@ -14,8 +13,9 @@ import Yrbl from 'component/yrbl';
|
||||||
import FileViewer from 'component/fileViewer';
|
import FileViewer from 'component/fileViewer';
|
||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
import usePrevious from 'effects/use-previous';
|
import usePrevious from 'effects/use-previous';
|
||||||
import Button from 'component/button';
|
import Nag from 'component/common/nag';
|
||||||
// @if TARGET='web'
|
// @if TARGET='web'
|
||||||
|
import OpenInAppLink from 'component/openInAppLink';
|
||||||
import YoutubeWelcome from 'component/youtubeWelcome';
|
import YoutubeWelcome from 'component/youtubeWelcome';
|
||||||
// @endif
|
// @endif
|
||||||
|
|
||||||
|
@ -199,20 +199,12 @@ function App(props: Props) {
|
||||||
|
|
||||||
{/* @if TARGET='web' */}
|
{/* @if TARGET='web' */}
|
||||||
<YoutubeWelcome />
|
<YoutubeWelcome />
|
||||||
|
<OpenInAppLink uri={uri} />
|
||||||
{/* @endif */}
|
{/* @endif */}
|
||||||
|
|
||||||
{/* @if TARGET='app' */}
|
{/* @if TARGET='app' */}
|
||||||
{showUpgradeButton && (
|
{showUpgradeButton && (
|
||||||
<div className="snack-bar--upgrade">
|
<Nag message={__('An upgrade is available.')} actionText={__('Install Now')} onClick={requestDownloadUpgrade} />
|
||||||
{__('Upgrade is ready')}
|
|
||||||
<Button
|
|
||||||
className="snack-bar__action"
|
|
||||||
button="alt"
|
|
||||||
icon={ICONS.DOWNLOAD}
|
|
||||||
label={__('Install now')}
|
|
||||||
onClick={requestDownloadUpgrade}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
{/* @endif */}
|
{/* @endif */}
|
||||||
{isEnhancedLayout && <Yrbl className="yrbl--enhanced" />}
|
{isEnhancedLayout && <Yrbl className="yrbl--enhanced" />}
|
||||||
|
|
28
ui/component/common/nag.jsx
Normal file
28
ui/component/common/nag.jsx
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
// @flow
|
||||||
|
import * as ICONS from 'constants/icons';
|
||||||
|
import React from 'react';
|
||||||
|
import Button from 'component/button';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
message: string,
|
||||||
|
actionText: string,
|
||||||
|
href?: string,
|
||||||
|
onClick?: () => void,
|
||||||
|
onClose?: () => void,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function Nag(props: Props) {
|
||||||
|
const { message, actionText, href, onClick, onClose } = props;
|
||||||
|
|
||||||
|
const buttonProps = onClick ? { onClick } : { href };
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="nag">
|
||||||
|
{message}
|
||||||
|
<Button className="nag__button" {...buttonProps}>
|
||||||
|
{actionText}
|
||||||
|
</Button>
|
||||||
|
{onClose && <Button className="nag__button nag__close" icon={ICONS.REMOVE} onClick={onClose} />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
9
ui/component/openInAppLink/index.js
Normal file
9
ui/component/openInAppLink/index.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { selectUser } from 'lbryinc';
|
||||||
|
import OpenInAppLink from './view';
|
||||||
|
|
||||||
|
const select = state => ({
|
||||||
|
user: selectUser(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(select)(OpenInAppLink);
|
66
ui/component/openInAppLink/view.jsx
Normal file
66
ui/component/openInAppLink/view.jsx
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
// @flow
|
||||||
|
import React from 'react';
|
||||||
|
import { withRouter } from 'react-router';
|
||||||
|
import userPersistedState from 'effects/use-persisted-state';
|
||||||
|
import { formatWebUrlIntoLbryUrl } from 'util/url';
|
||||||
|
import Nag from 'component/common/nag';
|
||||||
|
|
||||||
|
const userAgent = navigator.userAgent.toLowerCase();
|
||||||
|
const isAndroid = userAgent.includes('android');
|
||||||
|
const isDesktop = typeof window.orientation === 'undefined';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
history: { replace: string => void, push: string => void },
|
||||||
|
location: { search: string, pathname: string },
|
||||||
|
uri: string,
|
||||||
|
user: ?User,
|
||||||
|
};
|
||||||
|
|
||||||
|
function OpenInAppLink(props: Props) {
|
||||||
|
const {
|
||||||
|
history: { replace },
|
||||||
|
location,
|
||||||
|
uri,
|
||||||
|
user,
|
||||||
|
} = props;
|
||||||
|
const { pathname, search } = location;
|
||||||
|
let params = new URLSearchParams(search);
|
||||||
|
const hasSrcParam = params.get('src');
|
||||||
|
const initialShouldShowNag = hasSrcParam && (isAndroid || isDesktop);
|
||||||
|
const isWebUserOnly =
|
||||||
|
user && !user.device_types.some(usedDevice => usedDevice === 'mobile' || usedDevice === 'desktop');
|
||||||
|
const [hideNagForGood, setHideNagForGood] = userPersistedState('open-in-app-nag', false);
|
||||||
|
const [showNag, setShowNag] = React.useState(initialShouldShowNag);
|
||||||
|
let appLink = uri;
|
||||||
|
|
||||||
|
if (!appLink) {
|
||||||
|
appLink = formatWebUrlIntoLbryUrl(pathname, search);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClose() {
|
||||||
|
if (!isWebUserOnly) {
|
||||||
|
setHideNagForGood(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
setShowNag(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (hasSrcParam) {
|
||||||
|
params.delete('src');
|
||||||
|
const newParams = params.toString();
|
||||||
|
const newUrl = `${pathname}?${newParams}`;
|
||||||
|
replace(newUrl);
|
||||||
|
}
|
||||||
|
}, [search, pathname, replace]);
|
||||||
|
|
||||||
|
if (!showNag || hideNagForGood) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Nag message={__('Want even more freedom?')} actionText={__('Use the App')} href={appLink} onClose={handleClose} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withRouter(OpenInAppLink);
|
|
@ -29,7 +29,7 @@ const YoutubeWelcome = (props: Props) => {
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
}
|
}
|
||||||
actions={
|
actions={
|
||||||
<React.Fragment>
|
<div className="card__actions">
|
||||||
<Button
|
<Button
|
||||||
button="primary"
|
button="primary"
|
||||||
label={__('Create an Account')}
|
label={__('Create an Account')}
|
||||||
|
@ -37,7 +37,7 @@ const YoutubeWelcome = (props: Props) => {
|
||||||
onClick={doHideModal}
|
onClick={doHideModal}
|
||||||
/>
|
/>
|
||||||
<Button button="link" label={__('Not Yet')} onClick={doHideModal} />
|
<Button button="link" label={__('Not Yet')} onClick={doHideModal} />
|
||||||
</React.Fragment>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
@import 'component/media';
|
@import 'component/media';
|
||||||
@import 'component/menu-button';
|
@import 'component/menu-button';
|
||||||
@import 'component/modal';
|
@import 'component/modal';
|
||||||
|
@import 'component/nag';
|
||||||
@import 'component/navigation';
|
@import 'component/navigation';
|
||||||
@import 'component/pagination';
|
@import 'component/pagination';
|
||||||
@import 'component/placeholder';
|
@import 'component/placeholder';
|
||||||
|
|
|
@ -160,10 +160,6 @@
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
margin-bottom: var(--spacing-large);
|
margin-bottom: var(--spacing-large);
|
||||||
border-top: 1px solid var(--color-border);
|
border-top: 1px solid var(--color-border);
|
||||||
|
|
||||||
.button + .button {
|
|
||||||
margin-left: var(--spacing-medium);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card__body--with-icon,
|
.card__body--with-icon,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
.splash {
|
.splash {
|
||||||
|
-webkit-app-region: drag;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
|
||||||
|
|
36
ui/scss/component/nag.scss
Normal file
36
ui/scss/component/nag.scss
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
.nag {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
padding: var(--spacing-small);
|
||||||
|
background-color: var(--color-nag);
|
||||||
|
color: var(--color-white);
|
||||||
|
font-weight: var(--font-weight-bold);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nag__button {
|
||||||
|
line-height: 1;
|
||||||
|
margin-left: var(--spacing-small);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
border: 1px solid var(--color-white);
|
||||||
|
padding: var(--spacing-miniscule);
|
||||||
|
color: var(--color-white);
|
||||||
|
font-weight: var(--font-weight-bold);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--color-white);
|
||||||
|
color: var(--color-nag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nag__close {
|
||||||
|
margin-left: auto;
|
||||||
|
right: var(--spacing-medium);
|
||||||
|
position: absolute;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
stroke-width: 4px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,10 +36,6 @@
|
||||||
content: '';
|
content: '';
|
||||||
}
|
}
|
||||||
|
|
||||||
&[data-selected] {
|
|
||||||
color: var(--color-link-active);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
--color-background--splash: #212529;
|
--color-background--splash: #212529;
|
||||||
--color-border: #ededed;
|
--color-border: #ededed;
|
||||||
--color-background-overlay: #21252980;
|
--color-background-overlay: #21252980;
|
||||||
|
--color-nag: #f26522;
|
||||||
|
|
||||||
// Text
|
// Text
|
||||||
--color-text-selection-bg: var(--color-secondary-alt);
|
--color-text-selection-bg: var(--color-secondary-alt);
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Can't use aliases here because we're doing exports/require
|
||||||
|
const PAGES = require('../constants/pages');
|
||||||
|
|
||||||
exports.formatLbryUrlForWeb = uri => {
|
exports.formatLbryUrlForWeb = uri => {
|
||||||
return uri.replace('lbry://', '/').replace(/#/g, ':');
|
return uri.replace('lbry://', '/').replace(/#/g, ':');
|
||||||
};
|
};
|
||||||
|
@ -41,3 +44,17 @@ exports.formatInAppUrl = path => {
|
||||||
// Regular claim url
|
// Regular claim url
|
||||||
return path;
|
return path;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.formatWebUrlIntoLbryUrl = (pathname, search) => {
|
||||||
|
// If there is no uri, the user is on an internal page
|
||||||
|
// pathname will either be "/" or "/$/{page}"
|
||||||
|
const path = pathname.startsWith('/$/') ? pathname.slice(3) : pathname.slice(1);
|
||||||
|
let appLink = `lbry://?${path || PAGES.DISCOVER}`;
|
||||||
|
|
||||||
|
if (search) {
|
||||||
|
// We already have a leading "?" for the query param on internal pages
|
||||||
|
appLink += search.replace('?', '&');
|
||||||
|
}
|
||||||
|
|
||||||
|
return appLink;
|
||||||
|
};
|
||||||
|
|
|
@ -7074,9 +7074,9 @@ lbry-redux@lbryio/lbry-redux#9b11cfed62af7f0f8eb6fa3c88a1f8d2cce46afc:
|
||||||
reselect "^3.0.0"
|
reselect "^3.0.0"
|
||||||
uuid "^3.3.2"
|
uuid "^3.3.2"
|
||||||
|
|
||||||
lbryinc@lbryio/lbryinc#2aedf5a188f028f61c45bc7ed0c747a2d4ae453a:
|
lbryinc@lbryio/lbryinc#1e897d2c9108848637e1915700d3ae8ce176f9f8:
|
||||||
version "0.0.1"
|
version "0.0.1"
|
||||||
resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/2aedf5a188f028f61c45bc7ed0c747a2d4ae453a"
|
resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/1e897d2c9108848637e1915700d3ae8ce176f9f8"
|
||||||
dependencies:
|
dependencies:
|
||||||
reselect "^3.0.0"
|
reselect "^3.0.0"
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue