Additional UX fixes #1661
16 changed files with 103 additions and 92 deletions
|
@ -82,10 +82,10 @@ class App extends React.PureComponent<Props> {
|
||||||
return (
|
return (
|
||||||
<div id="window" onContextMenu={e => openContextMenu(e)}>
|
<div id="window" onContextMenu={e => openContextMenu(e)}>
|
||||||
<Theme />
|
<Theme />
|
||||||
|
<Header />
|
||||||
<main className="page">
|
<main className="page">
|
||||||
<SideBar />
|
<SideBar />
|
||||||
<div className="content" id="content">
|
<div className="content" id="content">
|
||||||
<Header />
|
|
||||||
<Router />
|
<Router />
|
||||||
<ModalRouter />
|
<ModalRouter />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,8 +4,10 @@ import * as FeatherIcons from 'react-feather';
|
||||||
import * as icons from 'constants/icons';
|
import * as icons from 'constants/icons';
|
||||||
import Tooltip from 'component/common/tooltip';
|
import Tooltip from 'component/common/tooltip';
|
||||||
|
|
||||||
|
// It would be nice to standardize this somehow
|
||||||
|
// These are copied from `scss/vars`, can they both come from the same source?
|
||||||
const RED_COLOR = '#e2495e';
|
const RED_COLOR = '#e2495e';
|
||||||
const PURPLE_COLOR = '#8165b0';
|
const GREEN_COLOR = '#44b098';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
icon: string,
|
icon: string,
|
||||||
|
@ -29,10 +31,10 @@ class IconComponent extends React.PureComponent<Props> {
|
||||||
switch (color) {
|
switch (color) {
|
||||||
case 'red':
|
case 'red':
|
||||||
return RED_COLOR;
|
return RED_COLOR;
|
||||||
case 'purple':
|
case 'green':
|
||||||
return PURPLE_COLOR;
|
return GREEN_COLOR;
|
||||||
default:
|
default:
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ class FileDownloadLink extends React.PureComponent<Props> {
|
||||||
<Button
|
<Button
|
||||||
button="alt"
|
button="alt"
|
||||||
icon={icons.DOWNLOAD}
|
icon={icons.DOWNLOAD}
|
||||||
iconColor="purple"
|
iconColor="green"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
purchaseUri(uri);
|
purchaseUri(uri);
|
||||||
}}
|
}}
|
||||||
|
@ -87,7 +87,7 @@ class FileDownloadLink extends React.PureComponent<Props> {
|
||||||
} else if (fileInfo && fileInfo.download_path) {
|
} else if (fileInfo && fileInfo.download_path) {
|
||||||
return (
|
return (
|
||||||
<ToolTip onComponent body={__('Open file')}>
|
<ToolTip onComponent body={__('Open file')}>
|
||||||
<Button button="alt" iconColor="purple" icon={icons.LOCAL} onClick={() => openFile()} />
|
<Button button="alt" iconColor="green" icon={icons.LOCAL} onClick={() => openFile()} />
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { selectBalance } from 'lbry-redux';
|
import { selectBalance, selectIsBackDisabled, selectIsForwardDisabled } from 'lbry-redux';
|
||||||
import { formatCredits } from 'util/formatCredits';
|
import { formatCredits } from 'util/formatCredits';
|
||||||
import { doNavigate } from 'redux/actions/navigation';
|
import { doNavigate, doHistoryBack, doHistoryForward } from 'redux/actions/navigation';
|
||||||
import { selectIsUpgradeAvailable, selectAutoUpdateDownloaded } from 'redux/selectors/app';
|
import { selectIsUpgradeAvailable, selectAutoUpdateDownloaded } from 'redux/selectors/app';
|
||||||
import { doDownloadUpgradeRequested } from 'redux/actions/app';
|
import { doDownloadUpgradeRequested } from 'redux/actions/app';
|
||||||
import Header from './view';
|
import Header from './view';
|
||||||
|
@ -11,11 +11,18 @@ const select = state => ({
|
||||||
balance: selectBalance(state),
|
balance: selectBalance(state),
|
||||||
isUpgradeAvailable: selectIsUpgradeAvailable(state),
|
isUpgradeAvailable: selectIsUpgradeAvailable(state),
|
||||||
roundedBalance: formatCredits(selectBalance(state) || 0, 2),
|
roundedBalance: formatCredits(selectBalance(state) || 0, 2),
|
||||||
|
isBackDisabled: selectIsBackDisabled(state),
|
||||||
|
isForwardDisabled: selectIsForwardDisabled(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
downloadUpgradeRequested: () => dispatch(doDownloadUpgradeRequested()),
|
downloadUpgradeRequested: () => dispatch(doDownloadUpgradeRequested()),
|
||||||
navigate: path => dispatch(doNavigate(path)),
|
navigate: path => dispatch(doNavigate(path)),
|
||||||
|
back: () => dispatch(doHistoryBack()),
|
||||||
|
forward: () => dispatch(doHistoryForward()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(Header);
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(Header);
|
||||||
|
|
|
@ -7,10 +7,14 @@ import * as icons from 'constants/icons';
|
||||||
type Props = {
|
type Props = {
|
||||||
autoUpdateDownloaded: boolean,
|
autoUpdateDownloaded: boolean,
|
||||||
balance: string,
|
balance: string,
|
||||||
downloadUpgradeRequested: any => void,
|
|
||||||
isUpgradeAvailable: boolean,
|
isUpgradeAvailable: boolean,
|
||||||
navigate: any => void,
|
|
||||||
roundedBalance: string,
|
roundedBalance: string,
|
||||||
|
isBackDisabled: boolean,
|
||||||
|
isForwardDisabled: boolean,
|
||||||
|
back: () => void,
|
||||||
|
forward: () => void,
|
||||||
|
downloadUpgradeRequested: any => void,
|
||||||
|
navigate: any => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Header = (props: Props) => {
|
const Header = (props: Props) => {
|
||||||
|
@ -21,6 +25,10 @@ const Header = (props: Props) => {
|
||||||
isUpgradeAvailable,
|
isUpgradeAvailable,
|
||||||
navigate,
|
navigate,
|
||||||
roundedBalance,
|
roundedBalance,
|
||||||
|
back,
|
||||||
|
isBackDisabled,
|
||||||
|
forward,
|
||||||
|
isForwardDisabled,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const showUpgradeButton =
|
const showUpgradeButton =
|
||||||
|
@ -28,6 +36,32 @@ const Header = (props: Props) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="header">
|
<header className="header">
|
||||||
|
<div className="header__navigation">
|
||||||
|
<Button
|
||||||
|
noPadding
|
||||||
|
button="alt"
|
||||||
|
icon={icons.HOME}
|
||||||
|
className="btn--home-nav"
|
||||||
|
description={__('Home')}
|
||||||
|
onClick={() => navigate('/discover')}
|
||||||
|
/>
|
||||||
|
<div className="header__history">
|
||||||
|
<Button
|
||||||
|
className="btn--arrow"
|
||||||
|
icon={icons.ARROW_LEFT}
|
||||||
|
description={__('Navigate back')}
|
||||||
|
onClick={back}
|
||||||
|
disabled={isBackDisabled}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
className="btn--arrow"
|
||||||
|
icon={icons.ARROW_RIGHT}
|
||||||
|
description={__('Navigate forward')}
|
||||||
|
onClick={forward}
|
||||||
|
disabled={isForwardDisabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<WunderBar />
|
<WunderBar />
|
||||||
<div className="header__actions-right">
|
<div className="header__actions-right">
|
||||||
<Button
|
<Button
|
||||||
|
|
|
@ -1,20 +1,16 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { doNavigate, doHistoryBack, doHistoryForward } from 'redux/actions/navigation';
|
import { selectNavLinks } from 'lbry-redux';
|
||||||
import { selectIsBackDisabled, selectIsForwardDisabled, selectNavLinks } from 'lbry-redux';
|
|
||||||
import { selectNotifications } from 'redux/selectors/subscriptions';
|
import { selectNotifications } from 'redux/selectors/subscriptions';
|
||||||
import SideBar from './view';
|
import SideBar from './view';
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
navLinks: selectNavLinks(state),
|
navLinks: selectNavLinks(state),
|
||||||
isBackDisabled: selectIsBackDisabled(state),
|
|
||||||
isForwardDisabled: selectIsForwardDisabled(state),
|
|
||||||
notifications: selectNotifications(state),
|
notifications: selectNotifications(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = () => ({});
|
||||||
navigate: path => dispatch(doNavigate(path)),
|
|
||||||
back: () => dispatch(doHistoryBack()),
|
|
||||||
forward: () => dispatch(doHistoryForward()),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(select, perform)(SideBar);
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(SideBar);
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import * as icons from 'constants/icons';
|
|
||||||
import * as NOTIFICATION_TYPES from 'constants/notification_types';
|
import * as NOTIFICATION_TYPES from 'constants/notification_types';
|
||||||
|
|
||||||
type SideBarLink = {
|
type SideBarLink = {
|
||||||
|
@ -14,11 +13,6 @@ type SideBarLink = {
|
||||||
};
|
};
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
navigate: any => void,
|
|
||||||
back: any => void,
|
|
||||||
forward: any => void,
|
|
||||||
isBackDisabled: boolean,
|
|
||||||
isForwardDisabled: boolean,
|
|
||||||
navLinks: {
|
navLinks: {
|
||||||
primary: Array<SideBarLink>,
|
primary: Array<SideBarLink>,
|
||||||
secondary: Array<SideBarLink>,
|
secondary: Array<SideBarLink>,
|
||||||
|
@ -29,15 +23,7 @@ type Props = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const SideBar = (props: Props) => {
|
const SideBar = (props: Props) => {
|
||||||
const {
|
const { navLinks, notifications } = props;
|
||||||
navigate,
|
|
||||||
back,
|
|
||||||
forward,
|
|
||||||
isBackDisabled,
|
|
||||||
isForwardDisabled,
|
|
||||||
navLinks,
|
|
||||||
notifications,
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const badges = Object.keys(notifications).reduce(
|
const badges = Object.keys(notifications).reduce(
|
||||||
(acc, cur) => (notifications[cur].type === NOTIFICATION_TYPES.DOWNLOADING ? acc : acc + 1),
|
(acc, cur) => (notifications[cur].type === NOTIFICATION_TYPES.DOWNLOADING ? acc : acc + 1),
|
||||||
|
@ -46,33 +32,6 @@ const SideBar = (props: Props) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className="nav">
|
<nav className="nav">
|
||||||
<div className="nav__actions-top">
|
|
||||||
<Button
|
|
||||||
noPadding
|
|
||||||
button="alt"
|
|
||||||
icon={icons.HOME}
|
|
||||||
className="btn--home-nav"
|
|
||||||
description={__('Home')}
|
|
||||||
onClick={() => navigate('/discover')}
|
|
||||||
/>
|
|
||||||
<div className="nav__actions-history">
|
|
||||||
<Button
|
|
||||||
className="btn--arrow"
|
|
||||||
icon={icons.ARROW_LEFT}
|
|
||||||
description={__('Navigate back')}
|
|
||||||
onClick={back}
|
|
||||||
disabled={isBackDisabled}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
className="btn--arrow"
|
|
||||||
icon={icons.ARROW_RIGHT}
|
|
||||||
description={__('Navigate forward')}
|
|
||||||
onClick={forward}
|
|
||||||
disabled={isForwardDisabled}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="nav__links">
|
<div className="nav__links">
|
||||||
<ul className="nav__primary">
|
<ul className="nav__primary">
|
||||||
{navLinks.primary.map(({ label, path, active, icon }) => (
|
{navLinks.primary.map(({ label, path, active, icon }) => (
|
||||||
|
|
|
@ -27,3 +27,4 @@ export const UNLOCK = 'Unlock';
|
||||||
export const CHECK_SIMPLE = 'Check';
|
export const CHECK_SIMPLE = 'Check';
|
||||||
export const GLOBE = 'Globe';
|
export const GLOBE = 'Globe';
|
||||||
export const EXTERNAL_LINK = 'ExternalLink';
|
export const EXTERNAL_LINK = 'ExternalLink';
|
||||||
|
export const GIFT = 'Gift';
|
||||||
|
|
|
@ -181,7 +181,7 @@ class FilePage extends React.Component<Props> {
|
||||||
<div className="card__channel-info">
|
<div className="card__channel-info">
|
||||||
<UriIndicator uri={uri} link />
|
<UriIndicator uri={uri} link />
|
||||||
</div>
|
</div>
|
||||||
<div className="card__actions card__actions--between">
|
<div className="card__actions card__actions--no-margin card__actions--between">
|
||||||
{(claimIsMine || subscriptionUri || speechSharable) && (
|
{(claimIsMine || subscriptionUri || speechSharable) && (
|
||||||
<div className="card__actions">
|
<div className="card__actions">
|
||||||
{claimIsMine ? (
|
{claimIsMine ? (
|
||||||
|
@ -200,7 +200,7 @@ class FilePage extends React.Component<Props> {
|
||||||
{!claimIsMine && (
|
{!claimIsMine && (
|
||||||
<Button
|
<Button
|
||||||
button="alt"
|
button="alt"
|
||||||
icon="Send"
|
icon={icons.GIFT}
|
||||||
label={__('Enjoy this? Send a tip')}
|
label={__('Enjoy this? Send a tip')}
|
||||||
onClick={() => openModal({ id: MODALS.SEND_TIP }, { uri })}
|
onClick={() => openModal({ id: MODALS.SEND_TIP }, { uri })}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -157,14 +157,13 @@ p {
|
||||||
}
|
}
|
||||||
|
|
||||||
.page {
|
.page {
|
||||||
display: grid;
|
position: absolute;
|
||||||
grid-template-rows: var(--header-height) calc(100vh - var(--header-height));
|
top: var(--header-height);
|
||||||
grid-template-columns: var(--side-nav-width) auto;
|
left: 0;
|
||||||
grid-template-areas:
|
right: 0;
|
||||||
'nav content'
|
bottom: 0;
|
||||||
'nav content';
|
display: flex;
|
||||||
background-color: var(--color-bg);
|
background-color: var(--color-bg);
|
||||||
height: 100vh;
|
|
||||||
|
|
||||||
@media only screen and (min-width: $medium-breakpoint) {
|
@media only screen and (min-width: $medium-breakpoint) {
|
||||||
grid-template-columns: var(--side-nav-width-m) auto;
|
grid-template-columns: var(--side-nav-width-m) auto;
|
||||||
|
@ -179,8 +178,8 @@ p {
|
||||||
Page content
|
Page content
|
||||||
*/
|
*/
|
||||||
.content {
|
.content {
|
||||||
grid-area: content;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main {
|
.main {
|
||||||
|
|
|
@ -54,6 +54,7 @@ $large-breakpoint: 1921px;
|
||||||
--box-shadow-layer: transparent; // 0 2px 4px rgba(0,0,0,0.25);
|
--box-shadow-layer: transparent; // 0 2px 4px rgba(0,0,0,0.25);
|
||||||
--box-shadow-button: 0 10px 20px rgba(0, 0, 0, 0.1);
|
--box-shadow-button: 0 10px 20px rgba(0, 0, 0, 0.1);
|
||||||
--box-shadow-wunderbar: 0 10px 20px rgba(0, 0, 0, 0.03);
|
--box-shadow-wunderbar: 0 10px 20px rgba(0, 0, 0, 0.03);
|
||||||
|
--box-shadow-header: 0px 6px 20px 1px rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
/* Text */
|
/* Text */
|
||||||
--text-color: var(--color-black);
|
--text-color: var(--color-black);
|
||||||
|
@ -118,7 +119,7 @@ $large-breakpoint: 1921px;
|
||||||
--header-bg: var(--color-white);
|
--header-bg: var(--color-white);
|
||||||
--header-color: var(--color-text);
|
--header-color: var(--color-text);
|
||||||
--header-active-color: rgba(0, 0, 0, 0.85);
|
--header-active-color: rgba(0, 0, 0, 0.85);
|
||||||
--header-height: 75px;
|
--header-height: 60px;
|
||||||
--header-button-bg: transparent;
|
--header-button-bg: transparent;
|
||||||
--header-button-hover-bg: rgba(100, 100, 100, 0.15);
|
--header-button-hover-bg: rgba(100, 100, 100, 0.15);
|
||||||
--header-primary-color: var(--color-primary);
|
--header-primary-color: var(--color-primary);
|
||||||
|
|
|
@ -195,7 +195,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.card__content--extra-padding {
|
.card__content--extra-padding {
|
||||||
margin-top: $spacing-vertical;
|
margin-top: $spacing-vertical * 3/2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card__subtext-title {
|
.card__subtext-title {
|
||||||
|
|
|
@ -1,17 +1,37 @@
|
||||||
.header {
|
.header {
|
||||||
|
position: fixed;
|
||||||
yes yes
|
|||||||
|
height: var(--header-height);
|
||||||
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: $spacing-width $spacing-width 0 $spacing-width;
|
align-items: center;
|
||||||
|
padding: 0 $spacing-width;
|
||||||
background-color: var(--color-bg);
|
background-color: var(--color-bg);
|
||||||
|
box-shadow: var(--box-shadow-header);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header__navigation {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header__history {
|
||||||
|
display: flex;
|
||||||
|
padding: 0 $spacing-width * 1/2;
|
||||||
|
|
||||||
|
@media only screen and (min-width: $medium-breakpoint) {
|
||||||
|
.btn {
|
||||||
Is there a reason we set rules to only screen? Is there a reason we set rules to only screen?
I guess not since screen will be the only one used. I guess not since screen will be the only one used.
`Media Types` are **optional** and if **not** used it defaults to `All`. Unless targeting a **printer** or **screen reader**, there is no need for them.
|
|||||||
|
padding: 0 $spacing-width * 1/6;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.header__actions-right {
|
.header__actions-right {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
padding-left: $spacing-vertical / 2;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
margin-left: $spacing-vertical * 1/3;
|
margin-left: $spacing-width * 1/3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
.nav {
|
.nav {
|
||||||
grid-area: nav;
|
width: var(--side-nav-width);
|
||||||
background-color: var(--nav-bg-color);
|
background-color: var(--nav-bg-color);
|
||||||
padding: $spacing-width;
|
padding: $spacing-width;
|
||||||
color: var(--nav-color);
|
color: var(--nav-color);
|
||||||
|
@ -7,21 +7,11 @@
|
||||||
hr {
|
hr {
|
||||||
width: 24px;
|
width: 24px;
|
||||||
margin: 36px 0;
|
margin: 36px 0;
|
||||||
// width: 40px;
|
|
||||||
border: solid 1px var(--color-divider);
|
border: solid 1px var(--color-divider);
|
||||||
margin: $spacing-vertical $spacing-vertical * 2/3;
|
margin: $spacing-vertical $spacing-vertical * 2/3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav__actions-top {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav__actions-history {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sidebar links
|
// Sidebar links
|
||||||
.nav__primary {
|
.nav__primary {
|
||||||
padding-top: 80px;
|
padding-top: 80px;
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
height: var(--btn-height);
|
height: var(--btn-height);
|
||||||
border-radius: var(--btn-radius);
|
border-radius: var(--btn-radius);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 700px;
|
|
||||||
color: var(--search-color);
|
color: var(--search-color);
|
||||||
background-color: var(--search-bg-color);
|
background-color: var(--search-bg-color);
|
||||||
box-shadow: var(--box-shadow-wunderbar);
|
box-shadow: var(--box-shadow-wunderbar);
|
||||||
|
|
|
@ -90,4 +90,7 @@
|
||||||
/* Scrollbar */
|
/* Scrollbar */
|
||||||
--scrollbar-thumb-bg: rgba(255, 255, 255, 0.20);
|
--scrollbar-thumb-bg: rgba(255, 255, 255, 0.20);
|
||||||
--scrollbar-thumb-hover-bg: #8696AF;
|
--scrollbar-thumb-hover-bg: #8696AF;
|
||||||
|
|
||||||
|
/* Shadows */
|
||||||
|
--box-shadow-header: 0px 6px 20px 1px rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue
Now that the header is fixed, should the address bar stretch to take the remaining space available?