restore that shit

This commit is contained in:
Sean Yesmunt 2019-07-17 16:49:06 -04:00
parent 155716ee1a
commit 1015abbb87
29 changed files with 329 additions and 186 deletions

View file

@ -158,6 +158,7 @@
"react-router-dom": "^5.0.0",
"react-simplemde-editor": "^4.0.0",
"react-spring": "^8.0.20",
"react-sticky-box": "^0.8.0",
"react-toggle": "^4.0.2",
"redux": "^3.6.0",
"redux-persist": "^4.8.0",

View file

@ -51,14 +51,12 @@ function App(props: Props) {
}, [userId]);
return (
<div ref={appRef} onContextMenu={e => openContextMenu(e)}>
<div className="main-wrapper" ref={appRef} onContextMenu={e => openContextMenu(e)}>
<Header />
<div className={MAIN_WRAPPER_CLASS}>
<div className="main-wrapper-inner">
<Router />
<SideBar />
</div>
<div className="main-wrapper__inner">
<Router />
<SideBar />
</div>
<ModalRouter />

View file

@ -118,14 +118,14 @@ function ChannelForm(props: Props) {
onUpdate={v => handleThumbnailChange(v)}
currentValue={params.thumbnail}
assetName={'Thumbnail'}
recommended={'(400x400)'}
recommended={'(300 x 300)'}
/>
<SelectAsset
onUpdate={v => handleCoverChange(v)}
currentValue={params.cover}
assetName={'Cover'}
recommended={'(1000x300)'}
recommended={'(1000 x 160)'}
/>
<FormField

View file

@ -39,18 +39,16 @@ export default function ClaimList(props: Props) {
type,
header,
onScrollBottom,
page,
pageSize,
} = props;
const [currentSort, setCurrentSort] = usePersistedState(persistedStorageKey, SORT_NEW);
const hasUris = uris && !!uris.length;
const sortedUris = (hasUris && (currentSort === SORT_NEW ? uris : uris.slice().reverse())) || [];
const urisLength = (uris && uris.length) || 0;
const sortedUris = (urisLength > 0 && (currentSort === SORT_NEW ? uris : uris.slice().reverse())) || [];
function handleSortChange() {
setCurrentSort(currentSort === SORT_NEW ? SORT_OLD : SORT_NEW);
}
const urisLength = uris && uris.length;
useEffect(() => {
function handleScroll(e) {
if (pageSize && onScrollBottom) {
@ -71,7 +69,7 @@ export default function ClaimList(props: Props) {
window.removeEventListener('scroll', handleScroll);
};
}
}, [loading, onScrollBottom, urisLength]);
}, [loading, onScrollBottom, urisLength, pageSize]);
return (
<section
@ -100,17 +98,17 @@ export default function ClaimList(props: Props) {
</div>
</div>
)}
{hasUris && (
{urisLength > 0 && (
<ul>
{sortedUris.map((uri, index) => (
<React.Fragment key={uri}>
<ClaimPreview uri={uri} type={type} placeholder={loading && (!page || page === 1)} />
<ClaimPreview uri={uri} type={type} />
{index === 4 && injectedItem && <li className="claim-preview--injected">{injectedItem}</li>}
</React.Fragment>
))}
</ul>
)}
{!hasUris && !loading && <h2 className="main--empty empty">{empty || __('No results')}</h2>}
{urisLength === 0 && !loading && <h2 className="main--empty empty">{empty || __('No results')}</h2>}
</section>
);
}

View file

@ -1,12 +1,12 @@
import * as SETTINGS from 'constants/settings';
import { connect } from 'react-redux';
import { doClaimSearch, selectLastClaimSearchUris, selectFetchingClaimSearch, doToggleTagFollow } from 'lbry-redux';
import { doClaimSearch, selectClaimSearchByQuery, selectFetchingClaimSearch, doToggleTagFollow } from 'lbry-redux';
import { selectSubscriptions } from 'redux/selectors/subscriptions';
import { makeSelectClientSetting } from 'redux/selectors/settings';
import ClaimListDiscover from './view';
const select = state => ({
uris: selectLastClaimSearchUris(state),
claimSearchByQuery: selectClaimSearchByQuery(state),
loading: selectFetchingClaimSearch(state),
subscribedChannels: selectSubscriptions(state),
showNsfw: makeSelectClientSetting(SETTINGS.SHOW_NSFW)(state),

View file

@ -1,13 +1,15 @@
// @flow
import type { Node } from 'react';
import React, { useEffect, useState } from 'react';
import moment from 'moment';
import usePersistedState from 'util/use-persisted-state';
import { MATURE_TAGS } from 'lbry-redux';
import React, { useEffect } from 'react';
import { withRouter } from 'react-router';
import { buildClaimSearchCacheQuery, MATURE_TAGS } from 'lbry-redux';
import { FormField } from 'component/common/form';
import moment from 'moment';
import ClaimList from 'component/claimList';
import Tag from 'component/tag';
import ClaimPreview from 'component/claimPreview';
import { updateQueryParam } from 'util/query-params';
import { toCapitalCase } from 'util/string';
const PAGE_SIZE = 20;
const TIME_DAY = 'day';
@ -33,75 +35,118 @@ type Props = {
injectedItem: any,
tags: Array<string>,
loading: boolean,
personal: boolean,
personalView: boolean,
doToggleTagFollow: string => void,
meta?: Node,
showNsfw: boolean,
history: { action: string, push: string => void, replace: string => void },
location: { search: string, pathname: string },
claimSearchByQuery: {
[string]: Array<string>,
},
};
function ClaimListDiscover(props: Props) {
const { doClaimSearch, uris, tags, loading, personal, injectedItem, meta, subscribedChannels, showNsfw } = props;
const [personalSort, setPersonalSort] = usePersistedState('claim-list-discover:personalSort', SEARCH_SORT_YOU);
const [typeSort, setTypeSort] = usePersistedState('claim-list-discover:typeSort', TYPE_TRENDING);
const [timeSort, setTimeSort] = usePersistedState('claim-list-discover:timeSort', TIME_WEEK);
const [page, setPage] = useState(1);
const {
doClaimSearch,
claimSearchByQuery,
tags,
loading,
personalView,
injectedItem,
meta,
subscribedChannels,
showNsfw,
history,
location,
} = props;
const didNavigateForward = history.action === 'PUSH';
const { search, pathname } = location;
const urlParams = new URLSearchParams(search);
const personalSort = urlParams.get('sort') || SEARCH_SORT_YOU;
const typeSort = urlParams.get('type') || TYPE_TRENDING;
const timeSort = urlParams.get('time') || TIME_WEEK;
const page = Number(urlParams.get('page')) || 1;
const tagsInUrl = urlParams.get('t') || '';
const url = `${pathname}${search}`;
const options: {
page_size: number,
page: number,
no_totals: boolean,
any_tags: Array<string>,
channel_ids: Array<string>,
not_tags: Array<string>,
order_by: Array<string>,
release_time?: string,
} = {
page_size: PAGE_SIZE,
page,
// no_totals makes it so the sdk doesn't have to calculate total number pages for pagination
// it's faster, but we will need to remove it if we start using total_pages
no_totals: true,
any_tags: (personalView && personalSort === SEARCH_SORT_YOU) || !personalView ? tags : [],
channel_ids: personalSort === SEARCH_SORT_CHANNELS ? subscribedChannels.map(sub => sub.uri.split('#')[1]) : [],
not_tags: !showNsfw ? MATURE_TAGS : [],
order_by:
typeSort === TYPE_TRENDING
? ['trending_global', 'trending_mixed']
: typeSort === TYPE_NEW
? ['release_time']
: ['effective_amount'], // Sort by top
};
if (typeSort === TYPE_TOP && timeSort !== TIME_ALL) {
options.release_time = `>${Math.floor(
moment()
.subtract(1, timeSort)
.unix()
)}`;
}
const claimSearchCacheQuery = buildClaimSearchCacheQuery(options);
const uris = claimSearchByQuery[claimSearchCacheQuery] || [];
const shouldPerformSearch = uris.length === 0 || didNavigateForward || (!loading && uris.length < PAGE_SIZE * page);
// Don't use the query from buildClaimSearchCacheQuery for the effect since that doesn't include page & release_time
const optionsStringForEffect = JSON.stringify(options);
function getSearch() {
let search = `?`;
if (!personalView) {
search += `t=${tagsInUrl}&`;
}
return search;
}
function handleTypeSort(newTypeSort) {
let url = `${getSearch()}type=${newTypeSort}&sort=${personalSort}`;
if (newTypeSort === TYPE_TOP) {
url += `&time=${timeSort}`;
}
history.push(url);
}
function handlePersonalSort(newPersonalSort) {
history.push(`${getSearch()}type=${typeSort}&sort=${newPersonalSort}`);
}
function handleTimeSort(newTimeSort) {
history.push(`${getSearch()}type=${typeSort}&sort=${personalSort}&time=${newTimeSort}`);
}
function handleScrollBottom() {
if (!loading) {
const uri = updateQueryParam(url, 'page', page + 1);
history.replace(uri);
}
}
const toCapitalCase = string => string.charAt(0).toUpperCase() + string.slice(1);
const tagsString = tags.join(',');
const channelsIdString = subscribedChannels.map(channel => channel.uri.split('#')[1]).join(',');
useEffect(() => {
const options: {
page_size: number,
any_tags?: Array<string>,
order_by?: Array<string>,
channel_ids?: Array<string>,
release_time?: string,
not_tags?: Array<string>,
} = { page_size: PAGE_SIZE, page, no_totals: true };
const newTags = tagsString.split(',');
const newChannelIds = channelsIdString.split(',');
if ((newTags && !personal) || (newTags && personal && personalSort === SEARCH_SORT_YOU)) {
options.any_tags = newTags;
} else if (personalSort === SEARCH_SORT_CHANNELS) {
options.channel_ids = newChannelIds;
if (shouldPerformSearch) {
const searchOptions = JSON.parse(optionsStringForEffect);
doClaimSearch(searchOptions);
}
if (!showNsfw) {
options.not_tags = MATURE_TAGS;
}
if (typeSort === TYPE_TRENDING) {
options.order_by = ['trending_global', 'trending_mixed'];
} else if (typeSort === TYPE_NEW) {
options.order_by = ['release_time'];
} else if (typeSort === TYPE_TOP) {
options.order_by = ['effective_amount'];
if (timeSort !== TIME_ALL) {
const time = Math.floor(
moment()
.subtract(1, timeSort)
.unix()
);
options.release_time = `>${time}`;
}
}
options.page_size = PAGE_SIZE;
doClaimSearch(options);
}, [personal, personalSort, typeSort, timeSort, doClaimSearch, page, tagsString, channelsIdString, showNsfw]);
function getLabel(type) {
if (type === SEARCH_SORT_ALL) {
return __('Everyone');
}
return type === SEARCH_SORT_YOU ? __('Tags You Follow') : __('Channels You Follow');
}
function resetList() {
setPage(1);
}
}, [doClaimSearch, shouldPerformSearch, optionsStringForEffect]);
const header = (
<h1 className="card__title--flex">
@ -110,10 +155,7 @@ function ClaimListDiscover(props: Props) {
type="select"
name="trending_sort"
value={typeSort}
onChange={e => {
resetList();
setTypeSort(e.target.value);
}}
onChange={e => handleTypeSort(e.target.value)}
>
{SEARCH_TYPES.map(type => (
<option key={type} value={type}>
@ -122,7 +164,7 @@ function ClaimListDiscover(props: Props) {
))}
</FormField>
<span>{__('For')}</span>
{!personal && tags && tags.length ? (
{!personalView && tags && tags.length ? (
tags.map(tag => <Tag key={tag} name={tag} disabled />)
) : (
<FormField
@ -131,13 +173,16 @@ function ClaimListDiscover(props: Props) {
className="claim-list__dropdown"
value={personalSort}
onChange={e => {
resetList();
setPersonalSort(e.target.value);
handlePersonalSort(e.target.value);
}}
>
{SEARCH_FILTER_TYPES.map(type => (
<option key={type} value={type}>
{getLabel(type)}
{type === SEARCH_SORT_ALL
? __('Everyone')
: type === SEARCH_SORT_YOU
? __('Tags You Follow')
: __('Channels You Follow')}
</option>
))}
</FormField>
@ -148,10 +193,7 @@ function ClaimListDiscover(props: Props) {
type="select"
name="trending_time"
value={timeSort}
onChange={e => {
resetList();
setTimeSort(e.target.value);
}}
onChange={e => handleTimeSort(e.target.value)}
>
{SEARCH_TIMES.map(time => (
<option key={time} value={time}>
@ -174,14 +216,14 @@ function ClaimListDiscover(props: Props) {
injectedItem={personalSort === SEARCH_SORT_YOU && injectedItem}
header={header}
headerAltControls={meta}
onScrollBottom={() => setPage(page + 1)}
onScrollBottom={handleScrollBottom}
page={page}
pageSize={PAGE_SIZE}
/>
{loading && page > 1 && new Array(PAGE_SIZE).fill(1).map((x, i) => <ClaimPreview key={i} placeholder />)}
{loading && new Array(PAGE_SIZE).fill(1).map((x, i) => <ClaimPreview key={i} placeholder />)}
</div>
);
}
export default ClaimListDiscover;
export default withRouter(ClaimListDiscover);

View file

@ -100,13 +100,13 @@ function ClaimPreview(props: Props) {
if (isValid && !isResolvingUri && haventFetched && uri) {
resolveUri(uri);
}
}, [isResolvingUri, uri, resolveUri, haventFetched]);
}, [isValid, isResolvingUri, uri, resolveUri, haventFetched]);
if (shouldHide) {
return null;
}
if (placeholder || isResolvingUri) {
if (placeholder || (isResolvingUri && !claim)) {
return (
<li className="claim-preview" disabled>
<div className="placeholder media__thumb" />

View file

@ -30,6 +30,7 @@ export function CommentCreate(props: Props) {
function handleCommentAck(event) {
setCommentAck(true);
}
function handleSubmit() {
if (channel !== CHANNEL_NEW && commentValue.length) createComment(commentValue, claimId, channel);
setCommentValue('');

View file

@ -15,7 +15,7 @@ type Props = {
isUpgradeAvailable: boolean,
roundedBalance: number,
downloadUpgradeRequested: any => void,
history: { push: string => void },
history: { push: string => void, goBack: () => void, goForward: () => void },
currentTheme: string,
automaticDarkModeEnabled: boolean,
setClientSetting: (string, boolean | string) => void,
@ -51,7 +51,7 @@ const Header = (props: Props) => {
<Button
className="header__navigation-item header__navigation-item--back"
description={__('Navigate back')}
onClick={() => window.history.back()}
onClick={() => history.goBack()}
icon={ICONS.ARROW_LEFT}
iconSize={18}
/>
@ -59,7 +59,7 @@ const Header = (props: Props) => {
<Button
className="header__navigation-item header__navigation-item--forward"
description={__('Navigate forward')}
onClick={() => window.history.forward()}
onClick={() => history.goForward()}
icon={ICONS.ARROW_RIGHT}
iconSize={18}
/>

View file

@ -1,2 +1,10 @@
import { connect } from 'react-redux';
import Router from './view';
export default Router;
const select = state => ({
scroll: state.app.scrollHistory[state.app.scrollHistory.length - 1],
scrollHistory: state.app.scrollHistory,
currentScroll: state.app.currentScroll || 0,
});
export default connect(select)(Router);

View file

@ -1,3 +1,4 @@
// @flow
import * as PAGES from 'constants/pages';
import React, { useEffect } from 'react';
import { Route, Redirect, Switch, withRouter } from 'react-router-dom';
@ -21,49 +22,56 @@ import NavigationHistory from 'page/navigationHistory';
import TagsPage from 'page/tags';
import FollowingPage from 'page/following';
const Scroll = withRouter(function ScrollWrapper(props) {
const { pathname } = props.location;
// Let app handle scroll
if ('scrollRestoration' in history) {
history.scrollRestoration = 'manual';
}
type Props = {
currentScroll: number,
location: { pathname: string, search: string },
};
function AppRouter(props: Props) {
const { currentScroll, location } = props;
const { pathname, search } = location;
// Don't update the scroll position if only the `page` param changes
const url = `${pathname}${search.replace(/&?\??page=\d+/, '')}`;
useEffect(() => {
// Auto scroll to the top of a window for new pages
// The browser will handle scrolling if it needs to, but
// for new pages, react-router maintains the current y scroll position
window.scrollTo(0, 0);
}, [pathname]);
window.scrollTo(0, currentScroll);
}, [currentScroll, url]);
return props.children;
});
export default function AppRouter() {
return (
<Scroll>
<Switch>
<Route path="/" exact component={DiscoverPage} />
<Route path={`/$/${PAGES.DISCOVER}`} exact component={DiscoverPage} />
<Route path={`/$/${PAGES.AUTH}`} exact component={AuthPage} />
<Route path={`/$/${PAGES.INVITE}`} exact component={InvitePage} />
<Route path={`/$/${PAGES.DOWNLOADED}`} exact component={FileListDownloaded} />
<Route path={`/$/${PAGES.PUBLISHED}`} exact component={FileListPublished} />
<Route path={`/$/${PAGES.HELP}`} exact component={HelpPage} />
<Route path={`/$/${PAGES.PUBLISH}`} exact component={PublishPage} />
<Route path={`/$/${PAGES.REPORT}`} exact component={ReportPage} />
<Route path={`/$/${PAGES.REWARDS}`} exact component={RewardsPage} />
<Route path={`/$/${PAGES.SEARCH}`} exact component={SearchPage} />
<Route path={`/$/${PAGES.SETTINGS}`} exact component={SettingsPage} />
<Route path={`/$/${PAGES.TRANSACTIONS}`} exact component={TransactionHistoryPage} />
<Route path={`/$/${PAGES.LIBRARY}`} exact component={LibraryPage} />
<Route path={`/$/${PAGES.ACCOUNT}`} exact component={AccountPage} />
<Route path={`/$/${PAGES.LIBRARY}/all`} exact component={NavigationHistory} />
<Route path={`/$/${PAGES.TAGS}`} exact component={TagsPage} />
<Route path={`/$/${PAGES.FOLLOWING}`} exact component={FollowingPage} />
<Route path={`/$/${PAGES.WALLET}`} exact component={WalletPage} />
{/* Below need to go at the end to make sure we don't match any of our pages first */}
<Route path="/:claimName" exact component={ShowPage} />
<Route path="/:claimName/:contentName" exact component={ShowPage} />
<Switch>
<Route path="/" exact component={DiscoverPage} />
<Route path={`/$/${PAGES.DISCOVER}`} exact component={DiscoverPage} />
<Route path={`/$/${PAGES.AUTH}`} exact component={AuthPage} />
<Route path={`/$/${PAGES.INVITE}`} exact component={InvitePage} />
<Route path={`/$/${PAGES.DOWNLOADED}`} exact component={FileListDownloaded} />
<Route path={`/$/${PAGES.PUBLISHED}`} exact component={FileListPublished} />
<Route path={`/$/${PAGES.HELP}`} exact component={HelpPage} />
<Route path={`/$/${PAGES.PUBLISH}`} exact component={PublishPage} />
<Route path={`/$/${PAGES.REPORT}`} exact component={ReportPage} />
<Route path={`/$/${PAGES.REWARDS}`} exact component={RewardsPage} />
<Route path={`/$/${PAGES.SEARCH}`} exact component={SearchPage} />
<Route path={`/$/${PAGES.SETTINGS}`} exact component={SettingsPage} />
<Route path={`/$/${PAGES.TRANSACTIONS}`} exact component={TransactionHistoryPage} />
<Route path={`/$/${PAGES.LIBRARY}`} exact component={LibraryPage} />
<Route path={`/$/${PAGES.ACCOUNT}`} exact component={AccountPage} />
<Route path={`/$/${PAGES.LIBRARY}/all`} exact component={NavigationHistory} />
<Route path={`/$/${PAGES.TAGS}`} exact component={TagsPage} />
<Route path={`/$/${PAGES.FOLLOWING}`} exact component={FollowingPage} />
<Route path={`/$/${PAGES.WALLET}`} exact component={WalletPage} />
{/* Below need to go at the end to make sure we don't match any of our pages first */}
<Route path="/:claimName" exact component={ShowPage} />
<Route path="/:claimName/:contentName" exact component={ShowPage} />
{/* Route not found. Mostly for people typing crazy urls into the url */}
<Route render={() => <Redirect to="/" />} />
</Switch>
</Scroll>
{/* Route not found. Mostly for people typing crazy urls into the url */}
<Route render={() => <Redirect to="/" />} />
</Switch>
);
}
export default withRouter(AppRouter);

View file

@ -1,9 +1,10 @@
// @flow
import * as PAGES from 'constants/pages';
import * as ICONS from 'constants/icons';
import * as React from 'react';
import React from 'react';
import Button from 'component/button';
import Tag from 'component/tag';
import StickyBox from 'react-sticky-box/dist/esnext';
type Props = {
subscriptions: Array<Subscription>,
@ -12,21 +13,18 @@ type Props = {
function SideBar(props: Props) {
const { subscriptions, followedTags } = props;
const buildLink = (path, label, icon, guide) => ({
navigate: path ? `$/${path}` : '/',
label,
icon,
guide,
});
const renderLink = linkProps => (
<li key={linkProps.label}>
<Button {...linkProps} className="navigation__link" activeClass="navigation__link--active" />
</li>
);
function buildLink(path, label, icon, guide) {
return {
navigate: path ? `$/${path}` : '/',
label,
icon,
guide,
};
}
return (
<div className="navigation-wrapper">
<StickyBox offsetBottom={40} offsetTop={100}>
<nav className="navigation">
<ul className="navigation__links">
{[
@ -39,7 +37,14 @@ function SideBar(props: Props) {
{
...buildLink(PAGES.PUBLISHED, __('Publishes'), ICONS.PUBLISH),
},
].map(renderLink)}
{
...buildLink(PAGES.FOLLOWING, __('Customize'), ICONS.EDIT),
},
].map(linkProps => (
<li key={linkProps.label}>
<Button {...linkProps} className="navigation__link" activeClass="navigation__link--active" />
</li>
))}
</ul>
<ul className="navigation__links tags--vertical">
{followedTags.map(({ name }, key) => (
@ -61,7 +66,7 @@ function SideBar(props: Props) {
))}
</ul>
</nav>
</div>
</StickyBox>
);
}

View file

@ -52,6 +52,8 @@ export default class SplashScreen extends React.PureComponent<Props, State> {
}
componentDidMount() {
this.props.onReadyToLaunch();
const { checkDaemonVersion } = this.props;
this.adjustErrorTimeout();
Lbry.connect()

View file

@ -63,7 +63,7 @@ class UriIndicator extends React.PureComponent<Props> {
return (
<Button className="button--uri-indicator" navigate={channelLink}>
<Tooltip label={<ClaimPreview uri={channelLink} type="small" />}>{inner}</Tooltip>
<Tooltip label={'test' || <ClaimPreview uri={channelLink} type="small" />}>{inner}</Tooltip>
</Button>
);
} else {

View file

@ -5,6 +5,7 @@ import {
makeSelectThumbnailForUri,
makeSelectCoverForUri,
selectCurrentChannelPage,
makeSelectClaimForUri,
} from 'lbry-redux';
import ChannelPage from './view';
@ -14,6 +15,7 @@ const select = (state, props) => ({
cover: makeSelectCoverForUri(props.uri)(state),
channelIsMine: makeSelectClaimIsMine(props.uri)(state),
page: selectCurrentChannelPage(state),
claim: makeSelectClaimForUri(props.uri)(state),
});
export default connect(

View file

@ -20,6 +20,7 @@ const ABOUT_PAGE = `about`;
type Props = {
uri: string,
claim: ChannelClaim,
title: ?string,
cover: ?string,
thumbnail: ?string,
@ -31,12 +32,12 @@ type Props = {
};
function ChannelPage(props: Props) {
const { uri, title, cover, history, location, page, channelIsMine, thumbnail } = props;
const { uri, title, cover, history, location, page, channelIsMine, thumbnail, claim } = props;
const { channelName } = parseURI(uri);
const { search } = location;
const urlParams = new URLSearchParams(search);
const currentView = urlParams.get(PAGE_VIEW_QUERY) || undefined;
const { permanent_url: permanentUrl } = claim;
const [editing, setEditing] = useState(false);
const [thumbPreview, setThumbPreview] = useState(thumbnail);
const [coverPreview, setCoverPreview] = useState(cover);
@ -90,7 +91,7 @@ function ChannelPage(props: Props) {
<Tab>{editing ? __('Editing Your Channel') : __('About')}</Tab>
<div className="card__actions">
<ShareButton uri={uri} />
<SubscribeButton uri={uri} />
<SubscribeButton uri={permanentUrl} />
</div>
</TabList>

View file

@ -16,7 +16,7 @@ function DiscoverPage(props: Props) {
return (
<Page>
<ClaimListDiscover
personal
personalView
tags={followedTags.map(tag => tag.name)}
meta={<Button button="link" label={__('Customize')} navigate={`/$/${PAGES.FOLLOWING}`} />}
injectedItem={<TagsSelect showClose title={__('Customize Your Homepage')} />}

View file

@ -63,6 +63,29 @@ const defaultState: AppState = {
isUpgradeSkipped: undefined,
enhancedLayout: false,
searchOptionsExpanded: false,
currentScroll: 0,
scrollHistory: [0],
};
reducers['@@router/LOCATION_CHANGE'] = (state, action) => {
const { currentScroll } = state;
const scrollHistory = state.scrollHistory.slice();
const { action: name } = action.payload;
let newCurrentScroll = currentScroll;
if (name === 'PUSH') {
scrollHistory.push(window.scrollY);
newCurrentScroll = 0;
} else if (name === 'POP') {
newCurrentScroll = scrollHistory[scrollHistory.length - 1];
scrollHistory.pop();
}
return {
...state,
scrollHistory,
currentScroll: newCurrentScroll,
};
};
reducers[ACTIONS.DAEMON_READY] = state =>

View file

@ -189,4 +189,9 @@
.claim-preview-tags {
margin-left: 0;
// change this
.tag {
max-width: 10rem;
}
}

View file

@ -1,32 +1,25 @@
.main-wrapper {
position: absolute;
min-height: 100vh;
width: 100vw;
padding-top: var(--header-height);
padding-left: var(--spacing-large);
padding-right: var(--spacing-large);
padding-bottom: var(--spacing-large);
display: flex;
[data-mode='dark'] & {
background-color: var(--dm-color-08);
}
}
.main-wrapper-inner {
.main-wrapper__inner {
display: flex;
align-items: flex-start;
min-height: 100vh;
max-width: 1200px;
width: 100%;
margin-left: auto;
margin-right: auto;
margin-top: var(--spacing-large);
position: relative;
padding-bottom: var(--spacing-main-padding);
}
.main {
min-width: 0;
width: calc(100% - var(--side-nav-width) - var(--spacing-main-padding));
position: relative;
margin-top: calc(var(--header-height) + var(--spacing-large));
margin-right: var(--spacing-main-padding);
@media (max-width: 600px) {
width: 100%;

View file

@ -1,13 +1,12 @@
.navigation-wrapper {
width: var(--side-nav-width);
left: calc(100% - var(--side-nav-width));
height: 100%;
position: absolute;
// width: var(--side-nav-width);
// left: calc(100% - var(--side-nav-width));
// height: 100%;
// position: absolute;
}
.navigation {
width: var(--side-nav-width);
padding-bottom: var(--spacing-main-padding);
font-size: 1.4rem;
@media (max-width: 600px) {

View file

@ -45,7 +45,6 @@ $main: $lbry-teal-5;
white-space: nowrap;
text-transform: lowercase;
font-size: 0.7em;
max-width: 10rem;
min-width: 0;
&:hover {

View file

@ -6,6 +6,7 @@ html {
font-size: 12px;
height: 100%;
min-height: 100%;
overflow-x: hidden;
&[data-mode='dark'] {
@ -20,7 +21,6 @@ body {
font-weight: 400;
height: 100%;
line-height: 1.5;
overflow: hidden;
background-color: mix($lbry-white, $lbry-gray-1, 70%);
[data-mode='dark'] & {

View file

@ -26,3 +26,13 @@ export function toQueryString(params) {
return parts.join('&');
}
export function updateQueryParam(uri, key, value) {
const re = new RegExp('([?&])' + key + '=.*?(&|$)', 'i');
const separator = uri.indexOf('?') !== -1 ? '&' : '?';
if (uri.match(re)) {
return uri.replace(re, '$1' + key + '=' + value + '$2');
} else {
return uri + separator + key + '=' + value;
}
}

5
src/ui/util/string.js Normal file
View file

@ -0,0 +1,5 @@
// @flow
export function toCapitalCase(string: string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}

View file

@ -10,10 +10,10 @@ export default function useHover(ref) {
const refElement = ref.current;
if (refElement) {
refElement.addEventListener('mouseover', handleHover);
refElement.addEventListener('mouseenter', handleHover);
refElement.addEventListener('mouseleave', handleHover);
return () => {
refElement.removeEventListener('mouseover', handleHover);
refElement.removeEventListener('mouseenter', handleHover);
refElement.removeEventListener('mouseleave', handleHover);
};
}

View file

@ -8,6 +8,7 @@
<body>
<div id="app"></div>
<!--
Primary definition for this is in webpack.web.config.js
We can't access it here because webpack isn't running on this file

View file

@ -542,6 +542,7 @@
"Upgrade is ready to install": "Upgrade is ready to install",
"Upgrade is ready": "Upgrade is ready",
"Abandon the claim for this URI": "Abandon the claim for this URI",
<<<<<<< HEAD
"For video content, use MP4s in H264/AAC format for best compatibility.": "For video content, use MP4s in H264/AAC format for best compatibility.",
"Read the App Basics FAQ": "Read the App Basics FAQ",
"View all LBRY FAQs": "View all LBRY FAQs",
@ -577,3 +578,23 @@
"These credits are permanently yours and can be removed at any time. Removing this support will reduce the claim's discoverability and return the LBC to your spendable balance.": "These credits are permanently yours and can be removed at any time. Removing this support will reduce the claim's discoverability and return the LBC to your spendable balance.",
"The better your tags are, the easier it will be for people to discover your channel.": "The better your tags are, the easier it will be for people to discover your channel."
}
=======
"View Tag": "View Tag",
"Invalid claim ID %s.": "Invalid claim ID %s.",
"Invalid character %s in name: %s.": "Invalid character %s in name: %s.",
"LBRY names cannot contain spaces or reserved symbols ($#@;/\"<>%{}|^~[]`)": "LBRY names cannot contain spaces or reserved symbols ($#@;/\"<>%{}|^~[]`)",
"No transactions.": "No transactions.",
"Today": "Today",
"This": "This",
"All time": "All time",
"Thumbnail (300 x 300)": "Thumbnail (300 x 300)",
"Cover (1000 x 160)": "Cover (1000 x 160)",
"No path provided after /": "No path provided after /",
"Connection Failure": "Connection Failure",
"Try closing all LBRY processes and starting again. If this still happens, your anti-virus software or firewall may be preventing LBRY from connecting. Contact hello@lbry.com if you think this is a software bug.": "Try closing all LBRY processes and starting again. If this still happens, your anti-virus software or firewall may be preventing LBRY from connecting. Contact hello@lbry.com if you think this is a software bug.",
"Unable to Authenticate": "Unable to Authenticate",
"Authentication Failure": "Authentication Failure",
"Reload": "Reload",
"If reloading does not fix this, or you see this at every start up, please email help@lbry.com.": "If reloading does not fix this, or you see this at every start up, please email help@lbry.com."
}
>>>>>>> restore that shit

View file

@ -769,6 +769,13 @@
dependencies:
regenerator-runtime "^0.13.2"
"@babel/runtime@^7.1.5":
version "7.5.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.4.tgz#cb7d1ad7c6d65676e66b47186577930465b5271b"
integrity sha512-Na84uwyImZZc3FKf4aUF1tysApzwf3p2yuFBIyBfbzT5glzKTdvYI4KVW4kcgjrzoGUjC7w3YyCHcJKaRxsr2Q==
dependencies:
regenerator-runtime "^0.13.2"
"@babel/runtime@^7.4.3":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.4.tgz#dc2e34982eb236803aa27a07fea6857af1b9171d"
@ -6646,9 +6653,9 @@ lazy-val@^1.0.3, lazy-val@^1.0.4:
yargs "^13.2.2"
zstd-codec "^0.1.1"
lbry-redux@lbryio/lbry-redux#eb3a7afc46ef69678149ad5caff3829473d75b43:
lbry-redux@lbryio/lbry-redux#6406f184e8c87d79f4adb2b8792ff3643f4527ea:
version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/eb3a7afc46ef69678149ad5caff3829473d75b43"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/6406f184e8c87d79f4adb2b8792ff3643f4527ea"
dependencies:
proxy-polyfill "0.1.6"
reselect "^3.0.0"
@ -9578,6 +9585,15 @@ react-spring@^8.0.20:
"@babel/runtime" "^7.3.1"
prop-types "^15.5.8"
react-sticky-box@^0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/react-sticky-box/-/react-sticky-box-0.8.0.tgz#1c191936af8f5420087b703ec6da4ef46060076c"
integrity sha512-al7fY+VzTKBgVrn14l21jQfhuG582Z6FD8tVbWVQDDqzcjLmUrFb+ljG2phxHhRRazg64L3yH4nOKjn78PZmag==
dependencies:
"@babel/runtime" "^7.1.5"
prop-types "^15.6.2"
resize-observer-polyfill "^1.5.1"
react-toggle@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/react-toggle/-/react-toggle-4.0.2.tgz#77f487860efb87fafd197672a2db8c885be1440f"
@ -10032,6 +10048,11 @@ reselect@^3.0.0:
resolved "https://registry.yarnpkg.com/reselect/-/reselect-3.0.1.tgz#efdaa98ea7451324d092b2b2163a6a1d7a9a2147"
integrity sha1-79qpjqdFEyTQkrKyFjpqHXqaIUc=
resize-observer-polyfill@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
resolve-cwd@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"