Merge branch 'infinite-scroll'
This commit is contained in:
commit
f226b19b65
19 changed files with 316 additions and 65 deletions
16
CHANGELOG.md
16
CHANGELOG.md
|
@ -8,12 +8,20 @@ Web UI version numbers should always match the corresponding version of LBRY App
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
### Added
|
### Added
|
||||||
*
|
* Identity verification for new reward participants
|
||||||
*
|
* Support rich markup in publishing descriptions and show pages.
|
||||||
|
* Release past publishing claims (and recover LBC) via the UI
|
||||||
|
* Added transition to card hovers to smooth animation
|
||||||
|
* Use randomly colored tiles when image is missing from metadata
|
||||||
|
* Added a loading message to file actions
|
||||||
|
* URL is auto suggested in Publish Page
|
||||||
|
* Added infinite scroll to channel pages
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
*
|
* Publishing revamped. Editing claims is much easier.
|
||||||
*
|
* Publish page now use `claim_list` rather than `file_list`
|
||||||
|
* Daemon updated to [v0.14.2](https://github.com/lbryio/lbry/releases/tag/v0.14.2)
|
||||||
|
* Made channel claim storage more efficient
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
*
|
*
|
||||||
|
|
|
@ -22,7 +22,7 @@ const { download } = remote.require("electron-dl");
|
||||||
const fs = remote.require("fs");
|
const fs = remote.require("fs");
|
||||||
const { lbrySettings: config } = require("../../../app/package.json");
|
const { lbrySettings: config } = require("../../../app/package.json");
|
||||||
|
|
||||||
export function doNavigate(path, params = {}) {
|
export function doNavigate(path, params = {}, options = {}) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
let url = path;
|
let url = path;
|
||||||
if (params) url = `${url}?${toQueryString(params)}`;
|
if (params) url = `${url}?${toQueryString(params)}`;
|
||||||
|
|
|
@ -262,6 +262,7 @@ export function doLoadVideo(uri) {
|
||||||
type: types.LOADING_VIDEO_FAILED,
|
type: types.LOADING_VIDEO_FAILED,
|
||||||
data: { uri },
|
data: { uri },
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch(doOpenModal("timedOut"));
|
dispatch(doOpenModal("timedOut"));
|
||||||
} else {
|
} else {
|
||||||
dispatch(doDownloadFile(uri, streamInfo));
|
dispatch(doDownloadFile(uri, streamInfo));
|
||||||
|
@ -313,22 +314,28 @@ export function doPurchaseUri(uri, purchaseModalName) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doFetchClaimsByChannel(uri, page = 1) {
|
export function doFetchClaimsByChannel(uri, page) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.FETCH_CHANNEL_CLAIMS_STARTED,
|
type: types.FETCH_CHANNEL_CLAIMS_STARTED,
|
||||||
data: { uri },
|
data: { uri, page },
|
||||||
});
|
});
|
||||||
|
|
||||||
lbry.claim_list_by_channel({ uri, page }).then(result => {
|
lbry.claim_list_by_channel({ uri, page }).then(result => {
|
||||||
const claimResult = result[uri],
|
const claimResult = result[uri],
|
||||||
claims = claimResult ? claimResult.claims_in_channel : [];
|
claims = claimResult ? claimResult.claims_in_channel : [],
|
||||||
|
totalPages = claimResult
|
||||||
|
? claimResult.claims_in_channel_pages
|
||||||
|
: undefined,
|
||||||
|
currentPage = claimResult ? claimResult.returned_page : undefined;
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.FETCH_CHANNEL_CLAIMS_COMPLETED,
|
type: types.FETCH_CHANNEL_CLAIMS_COMPLETED,
|
||||||
data: {
|
data: {
|
||||||
uri,
|
uri,
|
||||||
claims: claims,
|
claims,
|
||||||
|
totalPages,
|
||||||
|
page: currentPage,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -25,23 +25,23 @@ const Router = props => {
|
||||||
const { currentPage, params } = props;
|
const { currentPage, params } = props;
|
||||||
|
|
||||||
return route(currentPage, {
|
return route(currentPage, {
|
||||||
auth: <AuthPage {...params} />,
|
auth: <AuthPage params={params} />,
|
||||||
channel: <ChannelPage {...params} />,
|
channel: <ChannelPage params={params} />,
|
||||||
developer: <DeveloperPage {...params} />,
|
developer: <DeveloperPage params={params} />,
|
||||||
discover: <DiscoverPage {...params} />,
|
discover: <DiscoverPage params={params} />,
|
||||||
downloaded: <FileListDownloaded {...params} />,
|
downloaded: <FileListDownloaded params={params} />,
|
||||||
help: <HelpPage {...params} />,
|
help: <HelpPage params={params} />,
|
||||||
publish: <PublishPage {...params} />,
|
publish: <PublishPage params={params} />,
|
||||||
published: <FileListPublished {...params} />,
|
published: <FileListPublished params={params} />,
|
||||||
receive: <WalletPage {...params} />,
|
receive: <WalletPage params={params} />,
|
||||||
report: <ReportPage {...params} />,
|
report: <ReportPage params={params} />,
|
||||||
rewards: <RewardsPage {...params} />,
|
rewards: <RewardsPage params={params} />,
|
||||||
search: <SearchPage {...params} />,
|
search: <SearchPage params={params} />,
|
||||||
send: <WalletPage {...params} />,
|
send: <WalletPage params={params} />,
|
||||||
settings: <SettingsPage {...params} />,
|
settings: <SettingsPage params={params} />,
|
||||||
show: <ShowPage {...params} />,
|
show: <ShowPage params={params} />,
|
||||||
start: <StartPage {...params} />,
|
start: <StartPage params={params} />,
|
||||||
wallet: <WalletPage {...params} />,
|
wallet: <WalletPage params={params} />,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,10 @@ const select = state => ({
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
onSearch: query => dispatch(doNavigate("/search", { query })),
|
onSearch: query => dispatch(doNavigate("/search", { query })),
|
||||||
onSubmit: query =>
|
onSubmit: (query, extraParams) =>
|
||||||
dispatch(doNavigate("/show", { uri: lbryuri.normalize(query) })),
|
dispatch(
|
||||||
|
doNavigate("/show", { uri: lbryuri.normalize(query), ...extraParams })
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(Wunderbar);
|
export default connect(select, perform)(Wunderbar);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import lbryuri from "lbryuri.js";
|
import lbryuri from "lbryuri.js";
|
||||||
import { Icon } from "component/common.js";
|
import { Icon } from "component/common.js";
|
||||||
|
import { parseQueryParams } from "util/query_params";
|
||||||
|
|
||||||
class WunderBar extends React.PureComponent {
|
class WunderBar extends React.PureComponent {
|
||||||
static TYPING_TIMEOUT = 800;
|
static TYPING_TIMEOUT = 800;
|
||||||
|
@ -120,12 +121,15 @@ class WunderBar extends React.PureComponent {
|
||||||
onKeyPress(event) {
|
onKeyPress(event) {
|
||||||
if (event.charCode == 13 && this._input.value) {
|
if (event.charCode == 13 && this._input.value) {
|
||||||
let uri = null,
|
let uri = null,
|
||||||
method = "onSubmit";
|
method = "onSubmit",
|
||||||
|
extraParams = {};
|
||||||
|
|
||||||
this._resetOnNextBlur = false;
|
this._resetOnNextBlur = false;
|
||||||
clearTimeout(this._userTypingTimer);
|
clearTimeout(this._userTypingTimer);
|
||||||
|
|
||||||
const value = this._input.value.trim();
|
const parts = this._input.value.trim().split("?");
|
||||||
|
const value = parts.shift();
|
||||||
|
if (parts.length > 0) extraParams = parseQueryParams(parts.join(""));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
uri = lbryuri.normalize(value);
|
uri = lbryuri.normalize(value);
|
||||||
|
@ -136,7 +140,7 @@ class WunderBar extends React.PureComponent {
|
||||||
method = "onSearch";
|
method = "onSearch";
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props[method](uri);
|
this.props[method](uri, extraParams);
|
||||||
this._input.blur();
|
this._input.blur();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,24 +3,34 @@ import { connect } from "react-redux";
|
||||||
import { doFetchClaimsByChannel } from "actions/content";
|
import { doFetchClaimsByChannel } from "actions/content";
|
||||||
import {
|
import {
|
||||||
makeSelectClaimForUri,
|
makeSelectClaimForUri,
|
||||||
makeSelectClaimsInChannelForUri,
|
makeSelectClaimsInChannelForCurrentPage,
|
||||||
|
makeSelectFetchingChannelClaims,
|
||||||
} from "selectors/claims";
|
} from "selectors/claims";
|
||||||
|
import { selectCurrentParams } from "selectors/app";
|
||||||
|
import { doNavigate } from "actions/app";
|
||||||
|
import { makeSelectTotalPagesForChannel } from "selectors/content";
|
||||||
import ChannelPage from "./view";
|
import ChannelPage from "./view";
|
||||||
|
|
||||||
const makeSelect = () => {
|
const makeSelect = () => {
|
||||||
const selectClaim = makeSelectClaimForUri(),
|
const selectClaim = makeSelectClaimForUri(),
|
||||||
selectClaimsInChannel = makeSelectClaimsInChannelForUri();
|
selectClaimsInChannel = makeSelectClaimsInChannelForCurrentPage(),
|
||||||
|
selectFetchingChannelClaims = makeSelectFetchingChannelClaims(),
|
||||||
|
selectTotalPagesForChannel = makeSelectTotalPagesForChannel();
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
claim: selectClaim(state, props),
|
claim: selectClaim(state, props),
|
||||||
claimsInChannel: selectClaimsInChannel(state, props),
|
claimsInChannel: selectClaimsInChannel(state, props),
|
||||||
|
fetching: selectFetchingChannelClaims(state, props),
|
||||||
|
totalPages: selectTotalPagesForChannel(state, props),
|
||||||
|
params: selectCurrentParams(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
return select;
|
return select;
|
||||||
};
|
};
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
fetchClaims: uri => dispatch(doFetchClaimsByChannel(uri)),
|
fetchClaims: (uri, page) => dispatch(doFetchClaimsByChannel(uri, page)),
|
||||||
|
navigate: (path, params) => dispatch(doNavigate(path, params)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(makeSelect, perform)(ChannelPage);
|
export default connect(makeSelect, perform)(ChannelPage);
|
||||||
|
|
|
@ -2,24 +2,41 @@ import React from "react";
|
||||||
import lbryuri from "lbryuri";
|
import lbryuri from "lbryuri";
|
||||||
import { BusyMessage } from "component/common";
|
import { BusyMessage } from "component/common";
|
||||||
import FileTile from "component/fileTile";
|
import FileTile from "component/fileTile";
|
||||||
|
import Link from "component/link";
|
||||||
|
import ReactPaginate from "react-paginate";
|
||||||
|
|
||||||
class ChannelPage extends React.PureComponent {
|
class ChannelPage extends React.PureComponent {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.fetchClaims(this.props);
|
const { uri, params, fetchClaims } = this.props;
|
||||||
|
|
||||||
|
fetchClaims(uri, params.page || 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
this.fetchClaims(nextProps);
|
const { params, fetching, fetchClaims } = this.props;
|
||||||
|
const nextParams = nextProps.params;
|
||||||
|
|
||||||
|
if (fetching !== nextParams.page && params.page !== nextParams.page)
|
||||||
|
fetchClaims(nextProps.uri, nextParams.page);
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchClaims(props) {
|
changePage(pageNumber) {
|
||||||
if (props.claimsInChannel === undefined) {
|
const { params, currentPage } = this.props;
|
||||||
props.fetchClaims(props.uri);
|
const newParams = Object.assign({}, params, { page: pageNumber });
|
||||||
}
|
|
||||||
|
this.props.navigate("/show", newParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { claimsInChannel, claim, uri } = this.props;
|
const {
|
||||||
|
fetching,
|
||||||
|
claimsInChannel,
|
||||||
|
claim,
|
||||||
|
uri,
|
||||||
|
params,
|
||||||
|
totalPages,
|
||||||
|
} = this.props;
|
||||||
|
const { page } = params;
|
||||||
|
|
||||||
let contentList;
|
let contentList;
|
||||||
if (claimsInChannel === undefined) {
|
if (claimsInChannel === undefined) {
|
||||||
|
@ -29,14 +46,17 @@ class ChannelPage extends React.PureComponent {
|
||||||
? claimsInChannel.map(claim =>
|
? claimsInChannel.map(claim =>
|
||||||
<FileTile
|
<FileTile
|
||||||
key={claim.claim_id}
|
key={claim.claim_id}
|
||||||
uri={lbryuri.build({ name: claim.name, claimId: claim.claim_id })}
|
uri={lbryuri.build({
|
||||||
|
name: claim.name,
|
||||||
|
claimId: claim.claim_id,
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
: <span className="empty">{__("No content found.")}</span>;
|
: <span className="empty">{__("No content found.")}</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="main--single-column">
|
<div>
|
||||||
<section className="card">
|
<section className="card">
|
||||||
<div className="card__inner">
|
<div className="card__inner">
|
||||||
<div className="card__title-identity"><h1>{uri}</h1></div>
|
<div className="card__title-identity"><h1>{uri}</h1></div>
|
||||||
|
@ -51,7 +71,24 @@ class ChannelPage extends React.PureComponent {
|
||||||
</section>
|
</section>
|
||||||
<h3 className="card-row__header">{__("Published Content")}</h3>
|
<h3 className="card-row__header">{__("Published Content")}</h3>
|
||||||
{contentList}
|
{contentList}
|
||||||
</main>
|
<div />
|
||||||
|
{(!fetching || (claimsInChannel && claimsInChannel.length)) &&
|
||||||
|
totalPages > 1 &&
|
||||||
|
<ReactPaginate
|
||||||
|
pageCount={totalPages}
|
||||||
|
pageRangeDisplayed={2}
|
||||||
|
previousLabel="‹"
|
||||||
|
nextLabel="›"
|
||||||
|
activeClassName="pagination__item--selected"
|
||||||
|
pageClassName="pagination__item"
|
||||||
|
previousClassName="pagination__item pagination__item--previous"
|
||||||
|
nextClassName="pagination__item pagination__item--next"
|
||||||
|
marginPagesDisplayed={2}
|
||||||
|
onPageChange={e => this.changePage(e.selected + 1)}
|
||||||
|
initialPage={parseInt(page - 1)}
|
||||||
|
containerClassName="pagination"
|
||||||
|
/>}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,8 @@ const makeSelect = () => {
|
||||||
selectIsResolving = makeSelectIsResolvingForUri();
|
selectIsResolving = makeSelectIsResolvingForUri();
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
claim: selectClaim(state, props),
|
claim: selectClaim(state, props.params),
|
||||||
isResolvingUri: selectIsResolving(state, props),
|
isResolvingUri: selectIsResolving(state, props.params),
|
||||||
});
|
});
|
||||||
|
|
||||||
return select;
|
return select;
|
||||||
|
|
|
@ -6,13 +6,15 @@ import FilePage from "page/filePage";
|
||||||
|
|
||||||
class ShowPage extends React.PureComponent {
|
class ShowPage extends React.PureComponent {
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
const { isResolvingUri, resolveUri, uri } = this.props;
|
const { isResolvingUri, resolveUri, params } = this.props;
|
||||||
|
const { uri } = params;
|
||||||
|
|
||||||
if (!isResolvingUri) resolveUri(uri);
|
if (!isResolvingUri) resolveUri(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
const { isResolvingUri, resolveUri, claim, uri } = nextProps;
|
const { isResolvingUri, resolveUri, claim, params } = nextProps;
|
||||||
|
const { uri } = params;
|
||||||
|
|
||||||
if (!isResolvingUri && claim === undefined && uri) {
|
if (!isResolvingUri && claim === undefined && uri) {
|
||||||
resolveUri(uri);
|
resolveUri(uri);
|
||||||
|
@ -20,7 +22,8 @@ class ShowPage extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { claim, uri, isResolvingUri } = this.props;
|
const { claim, params, isResolvingUri } = this.props;
|
||||||
|
const { uri } = params;
|
||||||
|
|
||||||
let innerContent = "";
|
let innerContent = "";
|
||||||
|
|
||||||
|
|
|
@ -101,17 +101,44 @@ reducers[types.FETCH_CHANNEL_LIST_MINE_COMPLETED] = function(state, action) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
reducers[types.FETCH_CHANNEL_CLAIMS_COMPLETED] = function(state, action) {
|
reducers[types.FETCH_CHANNEL_CLAIMS_STARTED] = function(state, action) {
|
||||||
const { uri, claims } = action.data;
|
const { uri, page } = action.data;
|
||||||
|
const fetchingChannelClaims = Object.assign({}, state.fetchingChannelClaims);
|
||||||
|
|
||||||
const newClaims = Object.assign({}, state.claimsByChannel);
|
fetchingChannelClaims[uri] = page;
|
||||||
|
|
||||||
if (claims !== undefined) {
|
|
||||||
newClaims[uri] = claims;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
claimsByChannel: newClaims,
|
fetchingChannelClaims,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
reducers[types.FETCH_CHANNEL_CLAIMS_COMPLETED] = function(state, action) {
|
||||||
|
const { uri, claims, page } = action.data;
|
||||||
|
|
||||||
|
const claimsByChannel = Object.assign({}, state.claimsByChannel);
|
||||||
|
const byChannel = Object.assign({}, claimsByChannel[uri]);
|
||||||
|
const allClaimIds = new Set(byChannel["all"]);
|
||||||
|
const currentPageClaimIds = [];
|
||||||
|
const byId = Object.assign({}, state.byId);
|
||||||
|
const fetchingChannelClaims = Object.assign({}, state.fetchingChannelClaims);
|
||||||
|
|
||||||
|
if (claims !== undefined) {
|
||||||
|
claims.forEach(claim => {
|
||||||
|
allClaimIds.add(claim.claim_id);
|
||||||
|
currentPageClaimIds.push(claim.claim_id);
|
||||||
|
byId[claim.claim_id] = claim;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
byChannel["all"] = allClaimIds;
|
||||||
|
byChannel[page] = currentPageClaimIds;
|
||||||
|
claimsByChannel[uri] = byChannel;
|
||||||
|
delete fetchingChannelClaims[uri];
|
||||||
|
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
claimsByChannel,
|
||||||
|
byId,
|
||||||
|
fetchingChannelClaims,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,17 @@ reducers[types.RESOLVE_URI_CANCELED] = reducers[
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
reducers[types.FETCH_CHANNEL_CLAIMS_COMPLETED] = function(state, action) {
|
||||||
|
const channelPages = Object.assign({}, state.channelPages);
|
||||||
|
const { uri, totalPages } = action.data;
|
||||||
|
|
||||||
|
channelPages[uri] = totalPages;
|
||||||
|
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
channelPages,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export default function reducer(state = defaultState, action) {
|
export default function reducer(state = defaultState, action) {
|
||||||
const handler = reducers[action.type];
|
const handler = reducers[action.type];
|
||||||
if (handler) return handler(state, action);
|
if (handler) return handler(state, action);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { createSelector } from "reselect";
|
import { createSelector } from "reselect";
|
||||||
import { parseQueryParams } from "util/query_params";
|
import { parseQueryParams, toQueryString } from "util/query_params";
|
||||||
import lbry from "lbry";
|
import lbry from "lbry";
|
||||||
import lbryuri from "lbryuri";
|
import lbryuri from "lbryuri";
|
||||||
|
|
||||||
|
@ -55,8 +55,14 @@ export const selectPageTitle = createSelector(
|
||||||
return params.query
|
return params.query
|
||||||
? __("Search results for %s", params.query)
|
? __("Search results for %s", params.query)
|
||||||
: __("Search");
|
: __("Search");
|
||||||
case "show":
|
case "show": {
|
||||||
return lbryuri.normalize(params.uri);
|
const parts = [lbryuri.normalize(params.uri)];
|
||||||
|
// If the params has any keys other than "uri"
|
||||||
|
if (Object.keys(params).length > 1) {
|
||||||
|
parts.push(toQueryString(Object.assign({}, params, { uri: null })));
|
||||||
|
}
|
||||||
|
return parts.join("?");
|
||||||
|
}
|
||||||
case "downloaded":
|
case "downloaded":
|
||||||
return __("Downloads & Purchases");
|
return __("Downloads & Purchases");
|
||||||
case "published":
|
case "published":
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { createSelector } from "reselect";
|
import { createSelector } from "reselect";
|
||||||
|
import { selectCurrentParams } from "selectors/app";
|
||||||
import lbryuri from "lbryuri";
|
import lbryuri from "lbryuri";
|
||||||
|
|
||||||
const _selectState = state => state.claims || {};
|
const _selectState = state => state.claims || {};
|
||||||
|
@ -60,14 +61,59 @@ export const makeSelectClaimForUriIsMine = () => {
|
||||||
return createSelector(selectClaimForUriIsMine, isMine => isMine);
|
return createSelector(selectClaimForUriIsMine, isMine => isMine);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const selectAllFetchingChannelClaims = createSelector(
|
||||||
|
_selectState,
|
||||||
|
state => state.fetchingChannelClaims || {}
|
||||||
|
);
|
||||||
|
|
||||||
|
const selectFetchingChannelClaims = (state, props) => {
|
||||||
|
const allFetchingChannelClaims = selectAllFetchingChannelClaims(state);
|
||||||
|
|
||||||
|
return allFetchingChannelClaims[props.uri];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const makeSelectFetchingChannelClaims = (state, props) => {
|
||||||
|
return createSelector(selectFetchingChannelClaims, fetching => fetching);
|
||||||
|
};
|
||||||
|
|
||||||
export const selectClaimsInChannelForUri = (state, props) => {
|
export const selectClaimsInChannelForUri = (state, props) => {
|
||||||
return selectAllClaimsByChannel(state)[props.uri];
|
const byId = selectClaimsById(state);
|
||||||
|
const byChannel = selectAllClaimsByChannel(state)[props.uri] || {};
|
||||||
|
const claimIds = byChannel["all"];
|
||||||
|
|
||||||
|
if (!claimIds) return claimIds;
|
||||||
|
|
||||||
|
const claims = [];
|
||||||
|
|
||||||
|
claimIds.forEach(claimId => claims.push(byId[claimId]));
|
||||||
|
|
||||||
|
return claims;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const makeSelectClaimsInChannelForUri = () => {
|
export const makeSelectClaimsInChannelForUri = () => {
|
||||||
return createSelector(selectClaimsInChannelForUri, claims => claims);
|
return createSelector(selectClaimsInChannelForUri, claims => claims);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const selectClaimsInChannelForCurrentPage = (state, props) => {
|
||||||
|
const byId = selectClaimsById(state);
|
||||||
|
const byChannel = selectAllClaimsByChannel(state)[props.uri] || {};
|
||||||
|
const params = selectCurrentParams(state);
|
||||||
|
const page = params && params.page ? params.page : 1;
|
||||||
|
const claimIds = byChannel[page];
|
||||||
|
|
||||||
|
if (!claimIds) return claimIds;
|
||||||
|
|
||||||
|
const claims = [];
|
||||||
|
|
||||||
|
claimIds.forEach(claimId => claims.push(byId[claimId]));
|
||||||
|
|
||||||
|
return claims;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const makeSelectClaimsInChannelForCurrentPage = () => {
|
||||||
|
return createSelector(selectClaimsInChannelForCurrentPage, claims => claims);
|
||||||
|
};
|
||||||
|
|
||||||
const selectMetadataForUri = (state, props) => {
|
const selectMetadataForUri = (state, props) => {
|
||||||
const claim = selectClaimForUri(state, props);
|
const claim = selectClaimForUri(state, props);
|
||||||
const metadata =
|
const metadata =
|
||||||
|
|
|
@ -24,3 +24,16 @@ const selectResolvingUri = (state, props) => {
|
||||||
export const makeSelectIsResolvingForUri = () => {
|
export const makeSelectIsResolvingForUri = () => {
|
||||||
return createSelector(selectResolvingUri, resolving => resolving);
|
return createSelector(selectResolvingUri, resolving => resolving);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const selectChannelPages = createSelector(
|
||||||
|
_selectState,
|
||||||
|
state => state.channelPages || {}
|
||||||
|
);
|
||||||
|
|
||||||
|
const selectTotalPagesForChannel = (state, props) => {
|
||||||
|
return selectChannelPages(state)[props.uri];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const makeSelectTotalPagesForChannel = () => {
|
||||||
|
return createSelector(selectTotalPagesForChannel, totalPages => totalPages);
|
||||||
|
};
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
"react-dom": "^15.4.0",
|
"react-dom": "^15.4.0",
|
||||||
"react-markdown": "^2.5.0",
|
"react-markdown": "^2.5.0",
|
||||||
"react-modal": "^1.5.2",
|
"react-modal": "^1.5.2",
|
||||||
|
"react-paginate": "^4.4.3",
|
||||||
"react-redux": "^5.0.3",
|
"react-redux": "^5.0.3",
|
||||||
"react-simplemde-editor": "^3.6.11",
|
"react-simplemde-editor": "^3.6.11",
|
||||||
"redux": "^3.6.0",
|
"redux": "^3.6.0",
|
||||||
|
@ -54,8 +55,8 @@
|
||||||
"babel-preset-es2015": "^6.24.1",
|
"babel-preset-es2015": "^6.24.1",
|
||||||
"babel-preset-react": "^6.24.1",
|
"babel-preset-react": "^6.24.1",
|
||||||
"babel-preset-stage-2": "^6.18.0",
|
"babel-preset-stage-2": "^6.18.0",
|
||||||
"electron-rebuild": "^1.5.11",
|
|
||||||
"css-loader": "^0.28.4",
|
"css-loader": "^0.28.4",
|
||||||
|
"electron-rebuild": "^1.5.11",
|
||||||
"eslint": "^3.10.2",
|
"eslint": "^3.10.2",
|
||||||
"eslint-config-airbnb": "^13.0.0",
|
"eslint-config-airbnb": "^13.0.0",
|
||||||
"eslint-loader": "^1.6.1",
|
"eslint-loader": "^1.6.1",
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
@import "component/_modal.scss";
|
@import "component/_modal.scss";
|
||||||
@import "component/_snack-bar.scss";
|
@import "component/_snack-bar.scss";
|
||||||
@import "component/_video.scss";
|
@import "component/_video.scss";
|
||||||
|
@import "component/_pagination.scss";
|
||||||
@import "page/_developer.scss";
|
@import "page/_developer.scss";
|
||||||
@import "page/_reward.scss";
|
@import "page/_reward.scss";
|
||||||
@import "page/_show.scss";
|
@import "page/_show.scss";
|
||||||
|
|
32
ui/scss/component/_pagination.scss
Normal file
32
ui/scss/component/_pagination.scss
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
@import "../global";
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
display: block;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0 auto;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination__item {
|
||||||
|
display: inline-block;
|
||||||
|
line-height: $spacing-vertical * 1.5;
|
||||||
|
height: $spacing-vertical * 1.5;
|
||||||
|
border-radius: 2px;
|
||||||
|
&:not(.pagination__item--selected):hover {
|
||||||
|
background: rgba(0, 0, 0, 0.2);
|
||||||
|
> a { cursor: hand }
|
||||||
|
}
|
||||||
|
> a {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0 $spacing-vertical * 2 / 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination__item--previous, .pagination__item--next {
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination__item--selected {
|
||||||
|
color: white;
|
||||||
|
background: $color-primary;
|
||||||
|
}
|
49
ui/yarn.lock
49
ui/yarn.lock
|
@ -1204,6 +1204,10 @@ clap@^1.0.9:
|
||||||
dependencies:
|
dependencies:
|
||||||
chalk "^1.1.3"
|
chalk "^1.1.3"
|
||||||
|
|
||||||
|
classnames@^2.2.5:
|
||||||
|
version "2.2.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d"
|
||||||
|
|
||||||
cli-cursor@^1.0.1, cli-cursor@^1.0.2:
|
cli-cursor@^1.0.1, cli-cursor@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987"
|
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987"
|
||||||
|
@ -2329,6 +2333,14 @@ faye-websocket@~0.11.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
websocket-driver ">=0.5.1"
|
websocket-driver ">=0.5.1"
|
||||||
|
|
||||||
|
fbjs@^0.2.1:
|
||||||
|
version "0.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.2.1.tgz#622061630a43e11f845017b9044aaa648ed3f731"
|
||||||
|
dependencies:
|
||||||
|
core-js "^1.0.0"
|
||||||
|
promise "^7.0.3"
|
||||||
|
whatwg-fetch "^0.9.0"
|
||||||
|
|
||||||
fbjs@^0.6.1:
|
fbjs@^0.6.1:
|
||||||
version "0.6.1"
|
version "0.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.6.1.tgz#9636b7705f5ba9684d44b72f78321254afc860f7"
|
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.6.1.tgz#9636b7705f5ba9684d44b72f78321254afc860f7"
|
||||||
|
@ -2339,7 +2351,7 @@ fbjs@^0.6.1:
|
||||||
ua-parser-js "^0.7.9"
|
ua-parser-js "^0.7.9"
|
||||||
whatwg-fetch "^0.9.0"
|
whatwg-fetch "^0.9.0"
|
||||||
|
|
||||||
fbjs@^0.8.9:
|
fbjs@^0.8.4, fbjs@^0.8.9:
|
||||||
version "0.8.12"
|
version "0.8.12"
|
||||||
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.12.tgz#10b5d92f76d45575fd63a217d4ea02bea2f8ed04"
|
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.12.tgz#10b5d92f76d45575fd63a217d4ea02bea2f8ed04"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -4771,10 +4783,26 @@ rc@^1.1.7:
|
||||||
minimist "^1.2.0"
|
minimist "^1.2.0"
|
||||||
strip-json-comments "~2.0.1"
|
strip-json-comments "~2.0.1"
|
||||||
|
|
||||||
|
react-addons-create-fragment@^0.14.7:
|
||||||
|
version "0.14.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-addons-create-fragment/-/react-addons-create-fragment-0.14.8.tgz#e83240d1cba49249690fcc6f148710baa11d2b7a"
|
||||||
|
|
||||||
|
react-addons-create-fragment@^15.0.0:
|
||||||
|
version "15.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-addons-create-fragment/-/react-addons-create-fragment-15.6.0.tgz#af91a22b1fb095dd01f1afba43bfd0ef589d8b20"
|
||||||
|
dependencies:
|
||||||
|
fbjs "^0.8.4"
|
||||||
|
loose-envify "^1.3.1"
|
||||||
|
object-assign "^4.1.0"
|
||||||
|
|
||||||
react-dom-factories@^1.0.0:
|
react-dom-factories@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/react-dom-factories/-/react-dom-factories-1.0.0.tgz#f43c05e5051b304f33251618d5bc859b29e46b6d"
|
resolved "https://registry.yarnpkg.com/react-dom-factories/-/react-dom-factories-1.0.0.tgz#f43c05e5051b304f33251618d5bc859b29e46b6d"
|
||||||
|
|
||||||
|
react-dom@^0.14.7:
|
||||||
|
version "0.14.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-0.14.9.tgz#05064a3dcf0fb1880a3b2bfc9d58c55d8d9f6293"
|
||||||
|
|
||||||
react-dom@^15.4.0:
|
react-dom@^15.4.0:
|
||||||
version "15.6.1"
|
version "15.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.6.1.tgz#2cb0ed4191038e53c209eb3a79a23e2a4cf99470"
|
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.6.1.tgz#2cb0ed4191038e53c209eb3a79a23e2a4cf99470"
|
||||||
|
@ -4804,6 +4832,15 @@ react-modal@^1.5.2:
|
||||||
prop-types "^15.5.7"
|
prop-types "^15.5.7"
|
||||||
react-dom-factories "^1.0.0"
|
react-dom-factories "^1.0.0"
|
||||||
|
|
||||||
|
react-paginate@^4.4.3:
|
||||||
|
version "4.4.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-paginate/-/react-paginate-4.4.3.tgz#11817ece628fa59c54a2df7968c854ed64b99077"
|
||||||
|
dependencies:
|
||||||
|
classnames "^2.2.5"
|
||||||
|
prop-types "^15.5.7"
|
||||||
|
react "^15.0.0"
|
||||||
|
react-addons-create-fragment "^15.0.0"
|
||||||
|
|
||||||
react-redux@^5.0.3:
|
react-redux@^5.0.3:
|
||||||
version "5.0.5"
|
version "5.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.5.tgz#f8e8c7b239422576e52d6b7db06439469be9846a"
|
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.5.tgz#f8e8c7b239422576e52d6b7db06439469be9846a"
|
||||||
|
@ -4823,14 +4860,20 @@ react-simplemde-editor@^3.6.11:
|
||||||
react "^0.14.2"
|
react "^0.14.2"
|
||||||
simplemde "^1.11.2"
|
simplemde "^1.11.2"
|
||||||
|
|
||||||
react@^0.14.2:
|
react-tap-event-plugin@^0.2.2:
|
||||||
|
version "0.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-tap-event-plugin/-/react-tap-event-plugin-0.2.2.tgz#4f6f257851654f6c2b1c213a1d3ff21b353ae4e1"
|
||||||
|
dependencies:
|
||||||
|
fbjs "^0.2.1"
|
||||||
|
|
||||||
|
react@^0.14.2, react@^0.14.7:
|
||||||
version "0.14.9"
|
version "0.14.9"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-0.14.9.tgz#9110a6497c49d44ba1c0edd317aec29c2e0d91d1"
|
resolved "https://registry.yarnpkg.com/react/-/react-0.14.9.tgz#9110a6497c49d44ba1c0edd317aec29c2e0d91d1"
|
||||||
dependencies:
|
dependencies:
|
||||||
envify "^3.0.0"
|
envify "^3.0.0"
|
||||||
fbjs "^0.6.1"
|
fbjs "^0.6.1"
|
||||||
|
|
||||||
react@^15.4.0:
|
react@^15.0.0, react@^15.4.0:
|
||||||
version "15.6.1"
|
version "15.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-15.6.1.tgz#baa8434ec6780bde997cdc380b79cd33b96393df"
|
resolved "https://registry.yarnpkg.com/react/-/react-15.6.1.tgz#baa8434ec6780bde997cdc380b79cd33b96393df"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
Loading…
Reference in a new issue