Merge branch 'master' into md-fix
This commit is contained in:
commit
bb9e4e218d
29 changed files with 216 additions and 146 deletions
20
.travis.yml
20
.travis.yml
|
@ -1,16 +1,19 @@
|
|||
matrix:
|
||||
include:
|
||||
- os: osx
|
||||
env: TARGET=mac
|
||||
osx_image: xcode9.2
|
||||
language: node_js
|
||||
node_js: "9"
|
||||
env:
|
||||
- ELECTRON_CACHE=$HOME/.cache/electron
|
||||
- ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder
|
||||
- os: linux
|
||||
env: TARGET=windows
|
||||
services: docker
|
||||
language: node_js
|
||||
node_js: "9"
|
||||
- os: linux
|
||||
env: TARGET=linux
|
||||
language: node_js
|
||||
node_js: "9"
|
||||
cache: false
|
||||
before_install:
|
||||
- |
|
||||
|
@ -20,6 +23,7 @@ before_install:
|
|||
else
|
||||
sudo apt-get -qq update
|
||||
sudo apt-get install -y libsecret-1-dev
|
||||
sudo apt-get install --no-install-recommends -y gcc-multilib g++-multilib
|
||||
curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.6.0
|
||||
export PATH="$HOME/.yarn/bin:$PATH"
|
||||
fi
|
||||
|
@ -27,17 +31,15 @@ before_script:
|
|||
- git lfs pull
|
||||
script:
|
||||
- |
|
||||
if [ "$TRAVIS_OS_NAME" == "linux" ]; then
|
||||
if [ "$TARGET" == "windows" ]; then
|
||||
docker run --rm \
|
||||
--env-file <(env | grep -iE 'DEBUG|NODE_|ELECTRON_|YARN_|NPM_|CI|CIRCLE|TRAVIS|APPVEYOR_|CSC_|GH_|GITHUB_|BT_|AWS_|STRIP|BUILD_') \
|
||||
-v ${PWD}:/project \
|
||||
-v ~/.cache/electron:/root/.cache/electron \
|
||||
-v ~/.cache/electron-builder:/root/.cache/electron-builder \
|
||||
electronuserland/builder:wine \
|
||||
/bin/bash -c "yarn --link-duplicates --pure-lockfile && yarn build --linux --win --publish onTag"
|
||||
else
|
||||
yarn build --publish onTag
|
||||
/bin/bash -c "yarn --link-duplicates --pure-lockfile && yarn build --win --publish onTag";
|
||||
fi
|
||||
- if [ "$TARGET" == "mac" ]; then yarn build --publish onTag; fi
|
||||
- if [ "$TARGET" == "linux" ]; then yarn --link-duplicates --pure-lockfile && yarn build --linux --publish onTag; fi
|
||||
branches:
|
||||
except:
|
||||
- "/^v\\d+\\.\\d+\\.\\d+$/"
|
||||
|
|
|
@ -14,11 +14,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|||
* Pre-fill publish URL after clicking "Put something here" link ([#1303](https://github.com/lbryio/lbry-app/pull/1303))
|
||||
* Add Danger JS to automate code reviews ([#1289](https://github.com/lbryio/lbry-app/pull/1289))
|
||||
* Autoplay downloaded and free media ([#584](https://github.com/lbryio/lbry-app/pull/1453))
|
||||
* Add 'Go to page' input on channel pagination ([#1166](https://github.com/lbryio/lbry-app/pull/1166))
|
||||
* Add "View on web" button on file/channel pages with spee.ch link ([#1222](https://github.com/lbryio/lbry-app/pull/1222))
|
||||
|
||||
### Changed
|
||||
* Add flair to snackbar ([#1313](https://github.com/lbryio/lbry-app/pull/1313))
|
||||
* Made font in price badge larger ([#1420](https://github.com/lbryio/lbry-app/pull/1420))
|
||||
* Store subscriptions in internal database ([#1424](https://github.com/lbryio/lbry-app/pull/1424))
|
||||
* Move rewards logic to interal api ([#1509](https://github.com/lbryio/lbry-app/pull/1509))
|
||||
|
||||
### Fixed
|
||||
* Fix content-type not shown correctly in file description ([#863](https://github.com/lbryio/lbry-app/pull/863))
|
||||
|
@ -36,6 +39,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|||
* Fix disabled cards(grayed out) ([#1466](https://github.com/lbryio/lbry-app/issues/1466))
|
||||
* Fix markdown render ([#1179](https://github.com/lbryio/lbry-app/issues/1179))
|
||||
* Fix new lines not showing correctly after markdown changes ([#1504](https://github.com/lbryio/lbry-app/issues/1504))
|
||||
* Fix claim ID being null when reporting a claim that was not previously download ([issue#1512](https://github.com/lbryio/lbry-app/issues/1512)) ([PR#1530](https://github.com/lbryio/lbry-app/pull/1530))
|
||||
|
||||
|
||||
## [0.21.3] - 2018-04-23
|
||||
|
@ -58,7 +62,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|||
* App category for Linux ([#877](https://github.com/lbryio/lbry-app/pull/877))
|
||||
* Add YouTube Sync reward ([#1147](https://github.com/lbryio/lbry-app/pull/1147))
|
||||
* Retain previous screen sizing on startup ([#338](https://github.com/lbryio/lbry-app/issues/338))
|
||||
* Add 'Go to page' input on channel pagination ([#1166](https://github.com/lbryio/lbry-app/pull/1166))
|
||||
|
||||
|
||||
### Changed
|
||||
|
@ -373,7 +376,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|||
* Removed placeholder values from price selection form fields, which was causing confusion that these were real values (#426)
|
||||
* Fixed showing "other currency" help tip in publish form, which was caused due to not "setting" state for price
|
||||
* Publish page now properly checks for all required fields are filled
|
||||
* Fixed pagination styling for pages > 5 (#416)
|
||||
* Fixed sizing on squat videos (#419)
|
||||
* Support claims no longer show up on Published page (#384)
|
||||
* Fixed rendering of small prices (#461)
|
||||
|
@ -411,7 +413,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|||
### Added
|
||||
* Replaced horizontal scrollbars with scroll arrows
|
||||
* Featured weekly reward content shows with an orange star
|
||||
* Added pagination to channel pages
|
||||
|
||||
|
||||
### Fixed
|
||||
|
|
|
@ -146,6 +146,8 @@ There are a few tools integrated to the project that will ease the process of de
|
|||
manner and, therefore, not begin working on anything reserved (or updated) within the last 3 days.
|
||||
If someone has been officially assigned an issue via Github's assignment system, it is also not
|
||||
available. Contributors are encouraged to ask if they have any questions about issue availability.
|
||||
* The [changelog](https://github.com/lbryio/lbry-app/blob/master/CHANGELOG.md) should be updated to
|
||||
include a referene to the fix/change/addition. See previous entries for format.
|
||||
* Once the pull request is visible in the LBRY repo, a LBRY team member will review it and make sure
|
||||
it is up to our standards. At this point, the contributor may have to change his or her code based
|
||||
on our suggestions and comments.
|
||||
|
|
|
@ -50,7 +50,7 @@ distributable packages.
|
|||
|
||||
#### Resetting your Packages
|
||||
|
||||
If the app isn't building, or `yarn xxx` commands aren't working you may need to just reset your `node_modules`. To do so you can run: `rm -r node_modules && yarn` or `del node_modules && yarn` on Windows.
|
||||
If the app isn't building, or `yarn xxx` commands aren't working you may need to just reset your `node_modules`. To do so you can run: `rm -r node_modules && yarn` or `del /s /q node_modules && yarn` on Windows.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
"formik": "^0.10.4",
|
||||
"hast-util-sanitize": "^1.1.2",
|
||||
"keytar": "^4.2.1",
|
||||
"lbry-redux": "lbryio/lbry-redux#c41899e78415cae6fcb7bfca0e6ba48bb6bfe6c4",
|
||||
"lbry-redux": "lbryio/lbry-redux#fb27fb66387043be1b7962cd9439845fa73d7d8e",
|
||||
"localforage": "^1.7.1",
|
||||
"mixpanel-browser": "^2.17.1",
|
||||
"moment": "^2.22.0",
|
||||
|
|
|
@ -75,6 +75,9 @@ app.on('ready', async () => {
|
|||
await installExtensions();
|
||||
}
|
||||
rendererWindow = createWindow(appState);
|
||||
rendererWindow.webContents.on('devtools-opened', () => {
|
||||
rendererWindow.webContents.send('devtools-is-opened');
|
||||
});
|
||||
tray = createTray(rendererWindow);
|
||||
});
|
||||
|
||||
|
@ -176,11 +179,11 @@ ipcMain.on('version-info-requested', () => {
|
|||
},
|
||||
};
|
||||
let result = '';
|
||||
|
||||
const req = https.get(Object.assign(opts, url.parse(latestReleaseAPIURL)), res => {
|
||||
const onSuccess = res => {
|
||||
res.on('data', data => {
|
||||
result += data;
|
||||
});
|
||||
|
||||
res.on('end', () => {
|
||||
const tagName = JSON.parse(result).tag_name;
|
||||
const [, remoteVersion] = tagName.match(/^v([\d.]+(?:-?rc\d+)?)$/);
|
||||
|
@ -199,14 +202,27 @@ ipcMain.on('version-info-requested', () => {
|
|||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
req.on('error', err => {
|
||||
console.log('Failed to get current version from GitHub. Error:', err);
|
||||
if (rendererWindow) {
|
||||
rendererWindow.webContents.send('version-info-received', null);
|
||||
}
|
||||
});
|
||||
const requestLatestRelease = (apiUrl, alreadyRedirected = false) => {
|
||||
const req = https.get(Object.assign(opts, url.parse(apiUrl)), res => {
|
||||
if (res.statusCode === 301 || res.statusCode === 302) {
|
||||
requestLatestRelease(res.headers.location, true);
|
||||
} else {
|
||||
onSuccess(res);
|
||||
}
|
||||
});
|
||||
|
||||
if (alreadyRedirected) return;
|
||||
req.on('error', err => {
|
||||
console.log('Failed to get current version from GitHub. Error:', err);
|
||||
if (rendererWindow) {
|
||||
rendererWindow.webContents.send('version-info-received', null);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
requestLatestRelease(latestReleaseAPIURL);
|
||||
});
|
||||
|
||||
ipcMain.on('get-auth-token', event => {
|
||||
|
@ -242,8 +258,11 @@ const isSecondInstance = app.makeSingleInstance(argv => {
|
|||
// path, so we just strip it off.
|
||||
// - In a URI with a claim ID, like lbry://channel#claimid, Windows interprets the hash mark as
|
||||
// an anchor and converts it to lbry://channel/#claimid. We remove the slash here as well.
|
||||
// - ? also interpreted as an anchor, remove slash also.
|
||||
if (process.platform === 'win32') {
|
||||
URI = URI.replace(/\/$/, '').replace('/#', '#');
|
||||
URI = URI.replace(/\/$/, '')
|
||||
.replace('/#', '#')
|
||||
.replace('/?', '?');
|
||||
}
|
||||
|
||||
rendererWindow.webContents.send('open-uri-requested', URI);
|
||||
|
|
|
@ -4,7 +4,6 @@ import * as React from 'react';
|
|||
import classnames from 'classnames';
|
||||
|
||||
type Props = {
|
||||
centered?: boolean,
|
||||
children: React.Node,
|
||||
padded?: boolean,
|
||||
verticallyCentered?: boolean,
|
||||
|
@ -22,7 +21,6 @@ export class FormRow extends React.PureComponent<Props> {
|
|||
return (
|
||||
<div
|
||||
className={classnames('form-row', {
|
||||
'form-row--centered': centered,
|
||||
'form-row--padded': padded,
|
||||
'form-row--vertically-centered': verticallyCentered,
|
||||
'form-row--stretch': stretch,
|
||||
|
|
|
@ -11,6 +11,7 @@ type FileInfo = {
|
|||
|
||||
type Props = {
|
||||
uri: string,
|
||||
claimId: string,
|
||||
openModal: ({ id: string }, { uri: string }) => void,
|
||||
claimIsMine: boolean,
|
||||
fileInfo: FileInfo,
|
||||
|
@ -19,9 +20,7 @@ type Props = {
|
|||
|
||||
class FileActions extends React.PureComponent<Props> {
|
||||
render() {
|
||||
const { fileInfo, uri, openModal, claimIsMine, vertical } = this.props;
|
||||
|
||||
const claimId = fileInfo ? fileInfo.claim_id : '';
|
||||
const { fileInfo, uri, openModal, claimIsMine, vertical, claimId } = this.props;
|
||||
const showDelete = fileInfo && Object.keys(fileInfo).length > 0;
|
||||
|
||||
return (
|
||||
|
|
|
@ -35,20 +35,14 @@ class FilePrice extends React.PureComponent<Props> {
|
|||
render() {
|
||||
const { costInfo, showFullPrice } = this.props;
|
||||
|
||||
const isEstimate = costInfo ? !costInfo.includesData : false;
|
||||
|
||||
if (!costInfo) {
|
||||
return <span className="credit-amount">PRICE</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
return costInfo ? (
|
||||
<CreditAmount
|
||||
amount={costInfo.cost}
|
||||
isEstimate={isEstimate}
|
||||
isEstimate={!costInfo.includesData}
|
||||
showFree
|
||||
showFullPrice={showFullPrice}
|
||||
/>
|
||||
);
|
||||
) : null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,26 +1,17 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import InviteNew from './view';
|
||||
import {
|
||||
selectUserInvitesRemaining,
|
||||
selectUserInviteNewIsPending,
|
||||
selectUserInviteNewErrorMessage,
|
||||
} from 'redux/selectors/user';
|
||||
import rewards from 'rewards';
|
||||
import { makeSelectRewardAmountByType } from 'redux/selectors/rewards';
|
||||
|
||||
import { doUserInviteNew } from 'redux/actions/user';
|
||||
import InviteNew from './view';
|
||||
|
||||
const select = state => {
|
||||
const selectReward = makeSelectRewardAmountByType();
|
||||
|
||||
return {
|
||||
errorMessage: selectUserInviteNewErrorMessage(state),
|
||||
invitesRemaining: selectUserInvitesRemaining(state),
|
||||
isPending: selectUserInviteNewIsPending(state),
|
||||
rewardAmount: selectReward(state, { reward_type: rewards.TYPE_REFERRAL }),
|
||||
};
|
||||
};
|
||||
const select = state => ({
|
||||
errorMessage: selectUserInviteNewErrorMessage(state),
|
||||
invitesRemaining: selectUserInvitesRemaining(state),
|
||||
isPending: selectUserInviteNewIsPending(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
inviteNew: email => dispatch(doUserInviteNew(email)),
|
||||
|
|
|
@ -29,8 +29,7 @@ class FormInviteNew extends React.PureComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { errorMessage, isPending, rewardAmount } = this.props;
|
||||
const label = `${__('Get')} ${rewardAmount} LBC`;
|
||||
const { errorMessage, isPending } = this.props;
|
||||
|
||||
return (
|
||||
<Form onSubmit={this.handleSubmit}>
|
||||
|
@ -49,7 +48,7 @@ class FormInviteNew extends React.PureComponent {
|
|||
/>
|
||||
</FormRow>
|
||||
<div className="card__actions">
|
||||
<Submit label={label} disabled={isPending} />
|
||||
<Submit label="Invite" disabled={isPending} />
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
|
|
|
@ -16,7 +16,7 @@ const makeSelect = () => {
|
|||
const select = (state, props) => ({
|
||||
errorMessage: selectError(state, props),
|
||||
isPending: selectIsPending(state, props),
|
||||
reward: selectReward(state, props),
|
||||
reward: selectReward(state, props.reward_type),
|
||||
});
|
||||
|
||||
return select;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { selectCurrentPage, selectCurrentParams } from 'lbry-redux';
|
||||
import { selectCurrentPage, selectCurrentParams, doNotify } from 'lbry-redux';
|
||||
import Router from './view';
|
||||
|
||||
const select = state => ({
|
||||
|
@ -7,4 +7,4 @@ const select = state => ({
|
|||
currentPage: selectCurrentPage(state),
|
||||
});
|
||||
|
||||
export default connect(select, null)(Router);
|
||||
export default connect(select, { doNotify })(Router);
|
||||
|
|
|
@ -18,16 +18,21 @@ import InvitePage from 'page/invite';
|
|||
import BackupPage from 'page/backup';
|
||||
import SubscriptionsPage from 'page/subscriptions';
|
||||
|
||||
const route = (page, routesMap) => {
|
||||
const route = (props, page, routesMap) => {
|
||||
const component = routesMap[page];
|
||||
|
||||
return component || DiscoverPage;
|
||||
if (!component) {
|
||||
props.doNotify({
|
||||
message: __('Invalid page requested'),
|
||||
displayType: ['snackbar'],
|
||||
});
|
||||
}
|
||||
return component || routesMap.discover;
|
||||
};
|
||||
|
||||
const Router = props => {
|
||||
const { currentPage, params } = props;
|
||||
|
||||
return route(currentPage, {
|
||||
return route(props, currentPage, {
|
||||
auth: <AuthPage params={params} />,
|
||||
backup: <BackupPage params={params} />,
|
||||
channel: <ChannelPage params={params} />,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { doNotify, MODALS } from 'lbry-redux';
|
||||
import { doNavigate } from 'redux/actions/navigation';
|
||||
import { doUserIdentityVerify } from 'redux/actions/user';
|
||||
import rewards from 'rewards';
|
||||
|
@ -8,15 +9,14 @@ import {
|
|||
selectIdentityVerifyErrorMessage,
|
||||
} from 'redux/selectors/user';
|
||||
import UserVerify from './view';
|
||||
import { doNotify, MODALS } from 'lbry-redux';
|
||||
|
||||
const select = (state, props) => {
|
||||
const select = state => {
|
||||
const selectReward = makeSelectRewardByType();
|
||||
|
||||
return {
|
||||
isPending: selectIdentityVerifyIsPending(state),
|
||||
errorMessage: selectIdentityVerifyErrorMessage(state),
|
||||
reward: selectReward(state, { reward_type: rewards.TYPE_NEW_USER }),
|
||||
reward: selectReward(state, rewards.TYPE_NEW_USER),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
3
src/renderer/component/viewOnWebButton/index.js
Normal file
3
src/renderer/component/viewOnWebButton/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import ViewOnWebButton from './view';
|
||||
|
||||
export default ViewOnWebButton;
|
21
src/renderer/component/viewOnWebButton/view.jsx
Normal file
21
src/renderer/component/viewOnWebButton/view.jsx
Normal file
|
@ -0,0 +1,21 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import * as icons from 'constants/icons';
|
||||
import Button from 'component/button';
|
||||
|
||||
type Props = {
|
||||
uri: ?string,
|
||||
};
|
||||
|
||||
export default (props: Props) => {
|
||||
const { uri } = props;
|
||||
|
||||
return uri ? (
|
||||
<Button
|
||||
iconRight={icons.GLOBE}
|
||||
button="alt"
|
||||
label={__('View on Web')}
|
||||
href={`http://spee.ch/${uri}`}
|
||||
/>
|
||||
) : null;
|
||||
};
|
|
@ -25,3 +25,4 @@ export const CHECK = 'CheckCircle';
|
|||
export const HEART = 'Heart';
|
||||
export const UNLOCK = 'Unlock';
|
||||
export const CHECK_SIMPLE = 'Check';
|
||||
export const GLOBE = 'Globe';
|
||||
|
|
|
@ -8,7 +8,7 @@ import React from 'react';
|
|||
import ReactDOM from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import { doConditionalAuthNavigate, doDaemonReady, doAutoUpdate } from 'redux/actions/app';
|
||||
import { doNotify, doBlackListedOutpointsSubscribe } from 'lbry-redux';
|
||||
import { doNotify, doBlackListedOutpointsSubscribe, isURIValid } from 'lbry-redux';
|
||||
import { doNavigate } from 'redux/actions/navigation';
|
||||
import { doDownloadLanguages, doUpdateIsNightAsync } from 'redux/actions/settings';
|
||||
import { doUserEmailVerify } from 'redux/actions/user';
|
||||
|
@ -16,9 +16,11 @@ import 'scss/all.scss';
|
|||
import store from 'store';
|
||||
import app from './app';
|
||||
import analytics from './analytics';
|
||||
import doLogWarningConsoleMessage from './logWarningConsoleMessage';
|
||||
import { initContextMenu } from './util/contextMenu';
|
||||
|
||||
const { autoUpdater } = remote.require('electron-updater');
|
||||
const APPPAGEURL = 'lbry://?';
|
||||
|
||||
autoUpdater.logger = remote.require('electron-log');
|
||||
|
||||
|
@ -42,8 +44,18 @@ ipcRenderer.on('open-uri-requested', (event, uri, newSession) => {
|
|||
})
|
||||
);
|
||||
}
|
||||
} else {
|
||||
} else if (uri.startsWith(APPPAGEURL)) {
|
||||
const navpage = uri.replace(APPPAGEURL, '').toLowerCase();
|
||||
app.store.dispatch(doNavigate(`/${navpage}`));
|
||||
} else if (isURIValid(uri)) {
|
||||
app.store.dispatch(doNavigate('/show', { uri }));
|
||||
} else {
|
||||
app.store.dispatch(
|
||||
doNotify({
|
||||
message: __('Invalid LBRY URL requested'),
|
||||
displayType: ['snackbar'],
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -62,6 +74,11 @@ ipcRenderer.on('window-is-focused', () => {
|
|||
dock.setBadge('');
|
||||
});
|
||||
|
||||
ipcRenderer.on('devtools-is-opened', () => {
|
||||
const logOnDevelopment = false;
|
||||
doLogWarningConsoleMessage(logOnDevelopment);
|
||||
});
|
||||
|
||||
document.addEventListener('dragover', event => {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
|
34
src/renderer/logWarningConsoleMessage.js
Normal file
34
src/renderer/logWarningConsoleMessage.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
import isDev from 'electron-is-dev';
|
||||
|
||||
export default function doLogWarningConsoleMessage(activeOnDev = false) {
|
||||
if (isDev && !activeOnDev) return;
|
||||
const style = {
|
||||
redTitle:
|
||||
'color: red; font-size: 50px; text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black;, font-weight: bold;',
|
||||
normalText: 'font-size: 24px;',
|
||||
redText:
|
||||
'color: red; text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black; font-size: 24px;',
|
||||
};
|
||||
console.clear();
|
||||
console.log('%cScam alert!', style.redTitle);
|
||||
|
||||
console.log(
|
||||
'%cIf someone told you to copy / paste something here you have a chance of being scammed.',
|
||||
style.normalText
|
||||
);
|
||||
|
||||
console.log(
|
||||
'%cPasting anything in here could give attackers access to your LBC credits or wallet.',
|
||||
style.normalText
|
||||
);
|
||||
|
||||
console.log(
|
||||
"%cIf you don't understand what you are doing here, please close this window and keep your LBC credits/wallet safe.",
|
||||
style.redText
|
||||
);
|
||||
|
||||
console.log(
|
||||
'%cIf you do understand exactly what you are doing, you should come work with us https://lbry.io/join-us',
|
||||
style.normalText
|
||||
);
|
||||
}
|
|
@ -8,7 +8,7 @@ const select = state => {
|
|||
const selectReward = makeSelectRewardByType();
|
||||
|
||||
return {
|
||||
reward: selectReward(state, { reward_type: rewards.TYPE_NEW_USER }),
|
||||
reward: selectReward(state, rewards.TYPE_NEW_USER),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import CreditAmount from 'component/common/credit-amount';
|
|||
|
||||
class ModalFirstReward extends React.PureComponent {
|
||||
render() {
|
||||
const { closeModal, reward } = this.props;
|
||||
const { closeModal } = this.props;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
|
@ -18,10 +18,7 @@ class ModalFirstReward extends React.PureComponent {
|
|||
>
|
||||
<section>
|
||||
<h3 className="modal__header">{__('Your First Reward')}</h3>
|
||||
<p>
|
||||
{__('You just earned your first reward of')}{' '}
|
||||
<CreditAmount amount={reward.reward_amount} />.
|
||||
</p>
|
||||
<p>{__('You just earned your first reward!')}</p>
|
||||
<p>
|
||||
{__(
|
||||
"This reward will show in your Wallet in the top right momentarily (if it hasn't already)."
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import BusyIndicator from 'component/common/busy-indicator';
|
||||
import Button from 'component/button';
|
||||
import { FormField, FormRow } from 'component/common/form';
|
||||
import ReactPaginate from 'react-paginate';
|
||||
import SubscribeButton from 'component/subscribeButton';
|
||||
import ViewOnWebButton from 'component/viewOnWebButton';
|
||||
import Page from 'component/page';
|
||||
import FileList from 'component/fileList';
|
||||
import type { Claim } from 'types/claim';
|
||||
import { MODALS } from 'lbry-redux';
|
||||
|
||||
type Props = {
|
||||
uri: string,
|
||||
|
@ -19,6 +22,7 @@ type Props = {
|
|||
fetchClaims: (string, number) => void,
|
||||
fetchClaimCount: string => void,
|
||||
navigate: (string, {}) => void,
|
||||
openModal: (string, {}) => void,
|
||||
};
|
||||
|
||||
class ChannelPage extends React.PureComponent<Props> {
|
||||
|
@ -56,8 +60,8 @@ class ChannelPage extends React.PureComponent<Props> {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { fetching, claimsInChannel, claim, page, totalPages } = this.props;
|
||||
const { name, permanent_url: permanentUrl } = claim;
|
||||
const { fetching, claimsInChannel, claim, page, totalPages, uri, openModal } = this.props;
|
||||
const { name, permanent_url: permanentUrl, claim_id: claimId } = claim;
|
||||
|
||||
let contentList;
|
||||
if (fetching) {
|
||||
|
@ -76,7 +80,14 @@ class ChannelPage extends React.PureComponent<Props> {
|
|||
<section className="card__channel-info card__channel-info--large">
|
||||
<h1>{name}</h1>
|
||||
<div className="card__actions card__actions--no-margin">
|
||||
<Button
|
||||
button="alt"
|
||||
iconRight="Send"
|
||||
label={__('Enjoy this? Send a tip')}
|
||||
onClick={() => openModal(MODALS.SEND_TIP, { uri })}
|
||||
/>
|
||||
<SubscribeButton uri={permanentUrl} channelName={name} />
|
||||
<ViewOnWebButton uri={`${name}:${claimId}`} />
|
||||
</div>
|
||||
</section>
|
||||
<section>{contentList}</section>
|
||||
|
|
|
@ -13,6 +13,7 @@ import DateTime from 'component/dateTime';
|
|||
import * as icons from 'constants/icons';
|
||||
import Button from 'component/button';
|
||||
import SubscribeButton from 'component/subscribeButton';
|
||||
import ViewOnWebButton from 'component/viewOnWebButton';
|
||||
import Page from 'component/page';
|
||||
import player from 'render-media';
|
||||
import * as settings from 'constants/settings';
|
||||
|
@ -80,12 +81,7 @@ class FilePage extends React.Component<Props> {
|
|||
}
|
||||
|
||||
checkSubscription = (props: Props) => {
|
||||
if (
|
||||
props.claim.value.publisherSignature &&
|
||||
props.subscriptions
|
||||
.map(subscription => subscription.channelName)
|
||||
.indexOf(props.claim.channel_name) !== -1
|
||||
) {
|
||||
if (props.subscriptions.find(sub => sub.channelName === props.claim.channel_name)) {
|
||||
props.checkSubscription({
|
||||
channelName: props.claim.channel_name,
|
||||
uri: buildURI(
|
||||
|
@ -114,6 +110,7 @@ class FilePage extends React.Component<Props> {
|
|||
prepareEdit,
|
||||
navigate,
|
||||
autoplay,
|
||||
costInfo,
|
||||
} = this.props;
|
||||
|
||||
// File info
|
||||
|
@ -130,6 +127,11 @@ class FilePage extends React.Component<Props> {
|
|||
if (channelName && channelClaimId) {
|
||||
subscriptionUri = buildURI({ channelName, claimId: channelClaimId }, false);
|
||||
}
|
||||
const speechSharable =
|
||||
costInfo &&
|
||||
costInfo.cost === 0 &&
|
||||
contentType &&
|
||||
['video', 'image'].includes(contentType.split('/')[0]);
|
||||
|
||||
// We want to use the short form uri for editing
|
||||
// This is what the user is used to seeing, they don't care about the claim id
|
||||
|
@ -193,6 +195,14 @@ class FilePage extends React.Component<Props> {
|
|||
<SubscribeButton uri={subscriptionUri} channelName={channelName} />
|
||||
</React.Fragment>
|
||||
)}
|
||||
{speechSharable && (
|
||||
<ViewOnWebButton
|
||||
uri={buildURI({
|
||||
claimId: claim.claim_id,
|
||||
contentName: claim.name,
|
||||
}).slice(7)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<FormRow alignRight>
|
||||
|
@ -208,7 +218,7 @@ class FilePage extends React.Component<Props> {
|
|||
|
||||
<div className="card__content">
|
||||
<FileDownloadLink uri={uri} />
|
||||
<FileActions uri={uri} />
|
||||
<FileActions uri={uri} claimId={claim.claim_id} />
|
||||
</div>
|
||||
|
||||
<div className="card__content--extra-padding">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as ACTIONS from 'constants/action_types';
|
||||
import Lbryio from 'lbryio';
|
||||
import { doNotify, MODALS } from 'lbry-redux';
|
||||
import { selectUnclaimedRewardsByType } from 'redux/selectors/rewards';
|
||||
import { selectUnclaimedRewards } from 'redux/selectors/rewards';
|
||||
import { selectUserIsRewardApproved } from 'redux/selectors/user';
|
||||
import rewards from 'rewards';
|
||||
|
||||
|
@ -30,8 +30,8 @@ export function doRewardList() {
|
|||
export function doClaimRewardType(rewardType) {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const rewardsByType = selectUnclaimedRewardsByType(state);
|
||||
const reward = rewardsByType[rewardType];
|
||||
const unclaimedRewards = selectUnclaimedRewards(state);
|
||||
const reward = unclaimedRewards.find(ur => ur.reward_type === rewardType);
|
||||
const userIsRewardApproved = selectUserIsRewardApproved(state);
|
||||
|
||||
if (!reward || reward.transaction_id) {
|
||||
|
@ -84,14 +84,14 @@ export function doClaimRewardType(rewardType) {
|
|||
export function doClaimEligiblePurchaseRewards() {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const rewardsByType = selectUnclaimedRewardsByType(state);
|
||||
const unclaimedRewards = selectUnclaimedRewards(state);
|
||||
const userIsRewardApproved = selectUserIsRewardApproved(state);
|
||||
|
||||
if (!userIsRewardApproved || !Lbryio.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (rewardsByType[rewards.TYPE_FIRST_STREAM]) {
|
||||
if (unclaimedRewards.find(ur => ur.reward_type === rewards.TYPE_FIRST_STREAM)) {
|
||||
dispatch(doClaimRewardType(rewards.TYPE_FIRST_STREAM));
|
||||
} else {
|
||||
[rewards.TYPE_MANY_DOWNLOADS, rewards.TYPE_FEATURED_DOWNLOAD].forEach(type => {
|
||||
|
|
|
@ -4,7 +4,7 @@ const reducers = {};
|
|||
const defaultState = {
|
||||
fetching: false,
|
||||
claimedRewardsById: {}, // id => reward
|
||||
unclaimedRewardsByType: {},
|
||||
unclaimedRewards: [],
|
||||
claimPendingByType: {},
|
||||
claimErrorsByType: {},
|
||||
};
|
||||
|
@ -17,19 +17,19 @@ reducers[ACTIONS.FETCH_REWARDS_STARTED] = state =>
|
|||
reducers[ACTIONS.FETCH_REWARDS_COMPLETED] = (state, action) => {
|
||||
const { userRewards } = action.data;
|
||||
|
||||
const unclaimedRewards = {};
|
||||
const unclaimedRewards = [];
|
||||
const claimedRewards = {};
|
||||
userRewards.forEach(reward => {
|
||||
if (reward.transaction_id) {
|
||||
claimedRewards[reward.id] = reward;
|
||||
} else {
|
||||
unclaimedRewards[reward.reward_type] = reward;
|
||||
unclaimedRewards.push(reward);
|
||||
}
|
||||
});
|
||||
|
||||
return Object.assign({}, state, {
|
||||
claimedRewardsById: claimedRewards,
|
||||
unclaimedRewardsByType: unclaimedRewards,
|
||||
unclaimedRewards,
|
||||
fetching: false,
|
||||
});
|
||||
};
|
||||
|
@ -62,24 +62,21 @@ reducers[ACTIONS.CLAIM_REWARD_STARTED] = (state, action) => {
|
|||
|
||||
reducers[ACTIONS.CLAIM_REWARD_SUCCESS] = (state, action) => {
|
||||
const { reward } = action.data;
|
||||
const { unclaimedRewards } = state;
|
||||
|
||||
const unclaimedRewardsByType = Object.assign({}, state.unclaimedRewardsByType);
|
||||
const existingReward = unclaimedRewardsByType[reward.reward_type];
|
||||
const index = unclaimedRewards.findIndex(ur => ur.reward_type === reward.reward_type);
|
||||
unclaimedRewards.splice(index, 1);
|
||||
|
||||
const newReward = Object.assign({}, reward, {
|
||||
reward_title: existingReward.reward_title,
|
||||
reward_description: existingReward.reward_description,
|
||||
});
|
||||
const { claimedRewardsById } = state;
|
||||
claimedRewardsById[reward.id] = reward;
|
||||
|
||||
const claimedRewardsById = Object.assign({}, state.claimedRewardsById);
|
||||
claimedRewardsById[reward.id] = newReward;
|
||||
const newState = {
|
||||
...state,
|
||||
unclaimedRewards: [...unclaimedRewards],
|
||||
claimedRewardsById: { ...claimedRewardsById },
|
||||
};
|
||||
|
||||
const newState = Object.assign({}, state, {
|
||||
unclaimedRewardsByType,
|
||||
claimedRewardsById,
|
||||
});
|
||||
|
||||
return setClaimRewardState(newState, newReward, false, '');
|
||||
return setClaimRewardState(newState, reward, false, '');
|
||||
};
|
||||
|
||||
reducers[ACTIONS.CLAIM_REWARD_FAILURE] = (state, action) => {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { createSelector } from 'reselect';
|
||||
import REWARDS from 'rewards';
|
||||
|
||||
const selectState = state => state.rewards || {};
|
||||
|
||||
|
@ -26,16 +25,7 @@ export const selectClaimedRewardsByTransactionId = createSelector(selectClaimedR
|
|||
}, {})
|
||||
);
|
||||
|
||||
export const selectUnclaimedRewards = createSelector(
|
||||
selectUnclaimedRewardsByType,
|
||||
byType =>
|
||||
Object.values(byType).sort(
|
||||
(a, b) =>
|
||||
REWARDS.SORT_ORDER.indexOf(a.reward_type) < REWARDS.SORT_ORDER.indexOf(b.reward_type)
|
||||
? -1
|
||||
: 1
|
||||
) || []
|
||||
);
|
||||
export const selectUnclaimedRewards = createSelector(selectState, state => state.unclaimedRewards);
|
||||
|
||||
export const selectFetchingRewards = createSelector(selectState, state => !!state.fetching);
|
||||
|
||||
|
@ -65,7 +55,8 @@ const selectClaimRewardError = (state, props) =>
|
|||
export const makeSelectClaimRewardError = () =>
|
||||
createSelector(selectClaimRewardError, errorMessage => errorMessage);
|
||||
|
||||
const selectRewardByType = (state, props) => selectUnclaimedRewardsByType(state)[props.reward_type];
|
||||
const selectRewardByType = (state, rewardType) =>
|
||||
selectUnclaimedRewards(state).find(reward => reward.reward_type === rewardType);
|
||||
|
||||
export const makeSelectRewardByType = () => createSelector(selectRewardByType, reward => reward);
|
||||
|
||||
|
|
|
@ -1,21 +1,6 @@
|
|||
import { Lbry, doNotify } from 'lbry-redux';
|
||||
import Lbryio from 'lbryio';
|
||||
|
||||
function rewardMessage(type, amount) {
|
||||
return {
|
||||
new_developer: __('You earned %s for registering as a new developer.', amount),
|
||||
new_user: __('You earned %s LBC new user reward.', amount),
|
||||
verified_email: __('You earned %s LBC for verifying your email address.', amount),
|
||||
new_channel: __('You earned %s LBC for creating a publisher identity.', amount),
|
||||
first_stream: __('You earned %s LBC for streaming your first video.', amount),
|
||||
many_downloads: __('You earned %s LBC for downloading a bunch of things.', amount),
|
||||
first_publish: __('You earned %s LBC for making your first publication.', amount),
|
||||
featured_download: __('You earned %s LBC for watching a featured download.', amount),
|
||||
referral: __('You earned %s LBC for referring someone.', amount),
|
||||
youtube_creator: __('You earned %s LBC for syncing your YouTube channel.', amount),
|
||||
}[type];
|
||||
}
|
||||
|
||||
const rewards = {};
|
||||
|
||||
rewards.TYPE_NEW_DEVELOPER = 'new_developer';
|
||||
|
@ -28,18 +13,6 @@ rewards.TYPE_FIRST_PUBLISH = 'first_publish';
|
|||
rewards.TYPE_FEATURED_DOWNLOAD = 'featured_download';
|
||||
rewards.TYPE_REFERRAL = 'referral';
|
||||
rewards.YOUTUBE_CREATOR = 'youtube_creator';
|
||||
rewards.SORT_ORDER = [
|
||||
rewards.TYPE_NEW_USER,
|
||||
rewards.TYPE_CONFIRM_EMAIL,
|
||||
rewards.TYPE_FIRST_STREAM,
|
||||
rewards.TYPE_FIRST_CHANNEL,
|
||||
rewards.TYPE_FIRST_PUBLISH,
|
||||
rewards.TYPE_FEATURED_DOWNLOAD,
|
||||
rewards.TYPE_MANY_DOWNLOADS,
|
||||
rewards.TYPE_REFERRAL,
|
||||
rewards.TYPE_NEW_DEVELOPER,
|
||||
rewards.YOUTUBE_CREATOR,
|
||||
];
|
||||
|
||||
rewards.claimReward = type => {
|
||||
function requestReward(resolve, reject, params) {
|
||||
|
@ -48,7 +21,8 @@ rewards.claimReward = type => {
|
|||
return;
|
||||
}
|
||||
Lbryio.call('reward', 'new', params, 'post').then(reward => {
|
||||
const message = rewardMessage(type, reward.reward_amount);
|
||||
const message =
|
||||
reward.reward_notification || `You have claimed a ${reward.reward_amount} LBC reward.`;
|
||||
|
||||
// Display global notice
|
||||
const action = doNotify({
|
||||
|
|
|
@ -62,6 +62,10 @@
|
|||
width: 35px;
|
||||
}
|
||||
|
||||
input.paginate-channel {
|
||||
width: 35px;
|
||||
}
|
||||
|
||||
&.form-field--auto-height {
|
||||
height: auto;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue