+
{content_title}
@@ -295,7 +401,7 @@ class PostSummary extends React.Component {
{thumb}
-
+
{content_title}
{content_body}
@@ -312,16 +418,18 @@ export default connect(
const content = state.global.get('content').get(post);
let pending_payout = 0;
let total_payout = 0;
+ let event_count = 0
let gray, ignore
if (content) {
pending_payout = content.get('pending_payout_value');
total_payout = content.get('total_payout_value');
+ event_count = content.get('event_count')
const stats = content.get('stats', Map()).toJS()
gray = stats.gray
ignore = stats.ignore
}
return {
- post, content, gray, ignore, pending_payout, total_payout,
+ post, content, gray, ignore, pending_payout, total_payout, event_count,
username: state.user.getIn(['current', 'username']) || state.offchain.get('account')
};
},
@@ -329,6 +437,9 @@ export default connect(
(dispatch) => ({
dispatchSubmit: data => { dispatch(user.actions.usernamePasswordLogin({...data})) },
clearError: () => { dispatch(user.actions.loginError({error: null})) },
+ unsubscribe: (account, author, permlink) => {
+ dispatch(g.actions.unsubscribePost({ account, author, permlink }))
+ },
deleteReblog: (account, author, permlink, successCallback, errorCallback) => {
const json = ['delete_reblog', {account, author, permlink}]
dispatch(transaction.actions.broadcastOperation({
@@ -343,5 +454,14 @@ export default connect(
successCallback, errorCallback,
}))
},
+ showLogin: e => {
+ if (e) e.preventDefault()
+ try {
+ document.getElementsByClassName('DialogManager__shade')[0].click()
+ } catch (err) {
+ console.error(err)
+ }
+ dispatch(user.actions.showLogin())
+ },
})
)(PostSummary)
diff --git a/app/components/cards/PostSummary.scss b/app/components/cards/PostSummary.scss
index 8ae51e31e..362c909b5 100644
--- a/app/components/cards/PostSummary.scss
+++ b/app/components/cards/PostSummary.scss
@@ -11,11 +11,8 @@ ul.PostsList__summaries {
clear: left;
@include clearfix;
- .PostSummary__nsfw-warning {
- border: 1px solid $medium-gray;
- border-radius: 0.5rem;
- padding: 0.75rem 2rem;
- min-height: 80px;
+ .nsfw-text {
+ filter: blur(3px);
.PostSummary__footer {
margin-top: 0.2rem;
.Reblog__button {display: none;}
@@ -26,6 +23,10 @@ ul.PostsList__summaries {
}
}
+ .nsfw-img {
+ filter: blur(5px);
+ }
+
.nsfw_post {
color: #C00;
border: 1px solid #C00;
@@ -60,6 +61,14 @@ ul.PostsList__summaries {
}
}
+.PostSummary__replies {
+ font-weight: bold;
+ color: red;
+ padding-left: 10px;
+ font-size: 80%;
+ text-decoration: none;
+}
+
.PostSummary__image {
float: left;
width: 130px;
@@ -171,6 +180,25 @@ ul.PostsList__summaries {
}
}
}
+ .unsubscribe {
+ > a {
+ color: $medium-gray;
+ }
+ > a:visited {
+ color: $medium-gray;
+ }
+ padding-left: 0.5rem;
+ font-size: 90%;
+ span {
+ margin-left: 0.5rem;
+ margin-bottom: 0.5rem;
+ }
+ &:hover {
+ path {
+ fill: red;
+ }
+ }
+ }
}
.PostSummary__collapse {
visibility: hidden;
diff --git a/app/components/elements/ClaimInvite.jsx b/app/components/elements/ClaimInvite.jsx
deleted file mode 100644
index 5177dd3a3..000000000
--- a/app/components/elements/ClaimInvite.jsx
+++ /dev/null
@@ -1,173 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import shouldComponentUpdate from 'app/utils/shouldComponentUpdate'
-import {countDecimals, formatAmount} from 'app/utils/ParsersAndFormatters';
-import {validate_account_name} from 'app/utils/ChainValidation'
-import g from 'app/redux/GlobalReducer'
-import {connect} from 'react-redux';
-import transaction from 'app/redux/Transaction'
-import user from 'app/redux/User';
-import tt from 'counterpart';
-import reactForm from 'app/utils/ReactForm';
-import {PrivateKey} from 'golos-lib-js/lib/auth/ecc';
-import LoadingIndicator from 'app/components/elements/LoadingIndicator';
-
-class ClaimInvite extends Component {
- static propTypes = {
- // HTML
- account: PropTypes.object.isRequired,
- // Redux
- isMyAccount: PropTypes.bool.isRequired,
- accountName: PropTypes.string.isRequired,
- }
-
- constructor(props) {
- super()
- this.shouldComponentUpdate = shouldComponentUpdate(this, 'ClaimInvite')
- this.state = {
- errorMessage: '',
- successMessage: '',
- }
- this.initForm(props)
- }
-
- initForm(props) {
- const fields = ['invite_secret'];
- const validateSecret = (secret) => {
- try {
- PrivateKey.fromWif(secret);
- return null;
- } catch (e) {
- return tt('invites_jsx.claim_wrong_secret');
- }
- };
- reactForm({
- name: 'invite',
- instance: this, fields,
- initialValues: {},
- validation: values => ({
- invite_secret:
- ! values.invite_secret ? tt('g.required') : validateSecret(values.invite_secret)
- })
- })
- this.handleSubmitForm =
- this.state.invite.handleSubmit(args => this.handleSubmit(args))
- }
-
- onChangeInviteSecret = (e) => {
- const {value} = e.target
- this.state.invite_secret.props.onChange(value.trim())
- }
-
- handleSubmit = ({updateInitialValues}) => {
- const {claimInvite, accountName} = this.props
- const {invite_secret} = this.state
- this.setState({loading: true});
- claimInvite({invite_secret, accountName,
- errorCallback: (e) => {
- if (e === 'Canceled') {
- this.setState({
- loading: false,
- errorMessage: ''
- })
- } else {
- console.log('claimInvite ERROR', e)
- this.setState({
- loading: false,
- errorMessage: e.includes('Missing') ? tt('invites_jsx.claim_wrong_secret_fatal') : tt('g.server_returned_error')
- })
- }
- },
- successCallback: () => {
- this.setState({
- loading: false,
- errorMessage: '',
- successMessage: tt('invites_jsx.success_claim'),
- })
- // remove successMessage after a while
- setTimeout(() => this.setState({successMessage: ''}), 4000)
- }})
- }
-
- render() {
- const {props: {account, isMyAccount}} = this
- const {invite_secret, loading, successMessage, errorMessage} = this.state
- const {submitting, valid} = this.state.invite
-
- return (
)
- }
-}
-
-export default connect(
- (state, ownProps) => {
- const {account} = ownProps
- const accountName = account.get('name')
- const current = state.user.get('current')
- const username = current && current.get('username')
- const isMyAccount = username === accountName
- return {...ownProps, isMyAccount, accountName}
- },
- dispatch => ({
- claimInvite: ({
- invite_secret, accountName, successCallback, errorCallback
- }) => {
- const operation = {
- initiator: accountName,
- receiver: accountName,
- invite_secret: invite_secret.value
- }
-
- const success = () => {
- dispatch(user.actions.getAccount())
- successCallback()
- }
-
- dispatch(transaction.actions.broadcastOperation({
- type: 'invite_claim',
- accountName,
- operation,
- successCallback: success,
- errorCallback
- }))
- }
- })
-)(ClaimInvite)
diff --git a/app/components/elements/ConvertToSteem.jsx b/app/components/elements/ConvertToSteem.jsx
deleted file mode 100644
index f00f04050..000000000
--- a/app/components/elements/ConvertToSteem.jsx
+++ /dev/null
@@ -1,251 +0,0 @@
-/* eslint react/prop-types: 0 */
-import React from 'react'
-import ReactDOM from 'react-dom';
-import { connect, } from 'react-redux';
-import { Formik, Field, ErrorMessage, } from 'formik';
-import transaction from 'app/redux/Transaction'
-import shouldComponentUpdate from 'app/utils/shouldComponentUpdate'
-import Icon from 'app/components/elements/Icon'
-import TransactionError from 'app/components/elements/TransactionError'
-import LoadingIndicator from 'app/components/elements/LoadingIndicator'
-import tt from 'counterpart';
-import { DEBT_TICKER, LIQUID_TOKEN, LIQUID_TICKER } from 'app/client_config';
-import { Asset } from 'golos-lib-js/lib/utils';
-
-function floatToAsset(value, from) {
- value = parseFloat(value);
- if (isNaN(value)) {
- return Asset(0, 3, from);
- }
- value = value.toFixed(3);
- return Asset(value + ' ' + from);
-}
-
-function calcFee(value, cprops) {
- const percent = cprops ? cprops.toJS().convert_fee_percent : 0;
- const fee = value.mul(parseInt(percent)).div(10000);
- return fee;
-}
-
-class ConvertToSteem extends React.Component {
- constructor(props) {
- super()
- const { from, to } = props;
- this.state = {
- fee: Asset(0, 3, from),
- toAmount: Asset(0, 3, to),
- };
- this.amtRef = React.createRef();
- }
-
- componentDidMount() {
- this.amtRef.current.focus();
- }
-
- shouldComponentUpdate = shouldComponentUpdate(this, 'ConvertToSteem')
-
- validate = (values) => {
- const { maxBalance, from, cprops, } = this.props;
- const errors = {};
- if (values.amount) {
- if (isNaN(values.amount)
- || parseFloat(values.amount) <= 0) {
- errors.amount = tt('g.required');
- } else if (parseFloat(values.amount) > maxBalance) {
- errors.amount = tt('g.insufficient_balance');
- } else if (from === 'GOLOS' && !calcFee(floatToAsset(values.amount), cprops).amount) {
- errors.amount = tt('converttosteem_jsx.too_low_amount');
- }
- } else {
- errors.amount = tt('g.required');
- }
- return errors;
- };
-
- _onSubmit = (values, { setSubmitting, }) => {
- const { convert, owner, from, to, onClose } = this.props;
- const { amount } = values;
- const success = () => {
- if (onClose) onClose();
- setSubmitting(false);
- };
- const error = () => {
- setSubmitting(false);
- };
- convert(owner, amount, from, to, success, error);
- };
-
- onAmountChange = (e, values, handle) => {
- let value = e.target.value.trim().toLowerCase();
- if (isNaN(value) || parseFloat(value) < 0) {
- e.target.value = values.amount || '';
- return;
- }
- e.target.value = value;
-
- const { from, to, feed, cprops } = this.props;
- let fee = Asset(0, 3, from);
- let toAmount = Asset(0, 3, to);
- let { base, quote } = feed;
- base = Asset(base);
- quote = Asset(quote);
- value = floatToAsset(parseFloat(value), from);
- if (from === 'GOLOS' && cprops) {
- fee = calcFee(value, cprops);
- value.amount = value.amount - fee.amount;
- }
- if (value.symbol === base.symbol) {
- toAmount = value.mul(quote).div(base);
- } else {
- toAmount = value.mul(base).div(quote);
- }
- toAmount.symbol = to;
- this.setState({ toAmount, fee, });
-
- return handle(e);
- };
-
- render() {
- const DEBT_TOKEN = tt('token_names.DEBT_TOKEN')
-
- const { from, to, cprops, onClose, } = this.props
- const { fee, toAmount, } = this.state
-
- let feePercent = 0;
- if (cprops && from === 'GOLOS') {
- feePercent = parseFloat(cprops.get('convert_fee_percent')) / 100;
- }
-
- return (
-
- {({
- handleSubmit, isSubmitting, errors, values, handleChange,
- }) => (
-
- )}
- )
- }
-}
-export default connect(
- // mapStateToProps
- (state, ownProps) => {
- const { from, to } = ownProps;
- const current = state.user.get('current');
- const username = current.get('username');
- const account = state.global.getIn(['accounts', username]);
- const balance = account.get('balance');
- const sbd_balance = account.get('sbd_balance');
- const cprops = state.global.get('cprops');
- const max = parseFloat(Asset(from === DEBT_TICKER ? sbd_balance : balance).amountFloat);
- return {
- ...ownProps,
- owner: username,
- feed: state.global.get('feed_price').toJS(),
- cprops,
- maxBalance: max,
- };
- },
- // mapDispatchToProps
- dispatch => ({
- convert: (owner, amt, from, to, success, error) => {
- const amount = [parseFloat(amt).toFixed(3), from].join(' ')
- const requestid = Math.floor(Date.now() / 1000)
- const conf = tt('postfull_jsx.in_week_convert_DEBT_TOKEN_to_LIQUID_TOKEN',
- { amount: amount.split(' ')[0], DEBT_TOKEN: from, LIQUID_TOKEN: to, })
- dispatch(transaction.actions.broadcastOperation({
- type: 'convert',
- operation: {
- owner,
- requestid,
- amount,
- __config: {title: tt('converttosteem_jsx.confirm_title')}
- },
- confirm: conf + '?',
-
- successCallback: () => {
- success()
- dispatch({type: 'ADD_NOTIFICATION', payload:
- {key: 'convert_sd_to_steem_' + Date.now(),
- message: tt('g.order_placed') + ': ' + conf,
- dismissAfter: 5000}
- })
- },
- errorCallback: () => {error()}
- }))
- },
- })
-)(ConvertToSteem)
diff --git a/app/components/elements/CreateInvite.jsx b/app/components/elements/CreateInvite.jsx
deleted file mode 100644
index 02bb13202..000000000
--- a/app/components/elements/CreateInvite.jsx
+++ /dev/null
@@ -1,370 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import shouldComponentUpdate from 'app/utils/shouldComponentUpdate'
-import {countDecimals, formatAsset, formatAmount} from 'app/utils/ParsersAndFormatters';
-import g from 'app/redux/GlobalReducer'
-import {connect} from 'react-redux';
-import transaction from 'app/redux/Transaction'
-import user from 'app/redux/User';
-import tt from 'counterpart';
-import reactForm from 'app/utils/ReactForm';
-import {PrivateKey} from 'golos-lib-js/lib/auth/ecc';
-import LoadingIndicator from 'app/components/elements/LoadingIndicator';
-import CopyToClipboard from 'react-copy-to-clipboard';
-import Icon from 'app/components/elements/Icon';
-import { authRegisterUrl, } from 'app/utils/AuthApiClient';
-
-class CreateInvite extends Component {
- static propTypes = {
- // HTML
- account: PropTypes.object.isRequired,
- // Redux
- isMyAccount: PropTypes.bool.isRequired,
- accountName: PropTypes.string.isRequired,
- }
-
- constructor(props) {
- super()
- this.shouldComponentUpdate = shouldComponentUpdate(this, 'CreateInvite')
- this.state = {
- errorMessage: '',
- successMessage: '',
- }
- this.initForm(props)
- }
-
- componentDidMount() {
- this.generateKeys()
- this.setDefaultAmount();
- }
-
- componentDidUpdate(prevProps) {
- if (this.props.min_invite_balance !== prevProps.min_invite_balance) {
- this.setDefaultAmount();
- }
- }
-
- setDefaultAmount = () => {
- const { min_invite_balance } = this.props;
- const { amount } = this.state;
- if (min_invite_balance && !amount.value) {
- const val = (parseInt(min_invite_balance) + 1).toString()
- amount.props.onChange(val)
- }
- }
-
- updatePrivateKey = (pk) => {
- this.state.private_key.props.onChange(pk);
-
- const register_link = authRegisterUrl() + `?invite=${pk}`;
- this.state.register_link.props.onChange(register_link);
- }
-
- generateKeys = () => {
- const pk = PrivateKey.fromSeed(Math.random().toString());
- this.updatePrivateKey(pk.toString())
- this.setState({
- createdInvite: '',
- });
- }
-
- initForm(props) {
- const insufficientFunds = (amount) => {
- const balanceValue = props.account.get('balance')
- if(!balanceValue) return false
- return parseFloat(amount) > parseFloat(balanceValue.split(' ')[0])
- }
-
- const meetsMinimum = (amount) => {
- const minValue = this.props.min_invite_balance
- if (!minValue) return false
- return parseFloat(amount) < parseFloat(minValue.split(' ')[0])
- }
-
- const validateSecret = (secret) => {
- try {
- PrivateKey.fromWif(secret);
- return null;
- } catch (e) {
- return tt('invites_jsx.claim_wrong_secret_format');
- }
- };
-
- const fields = ['private_key', 'register_link', 'amount', 'is_referral:checked']
- reactForm({
- name: 'invite',
- instance: this, fields,
- initialValues: {},
- validation: values => ({
- private_key:
- ! values.private_key ? tt('g.required') : validateSecret(values.private_key),
- register_link:
- ! values.register_link ? tt('g.required') : null,
- amount:
- ! parseFloat(values.amount) || /^0$/.test(values.amount) ? tt('g.required') :
- insufficientFunds(values.amount) ? tt('transfer_jsx.insufficient_funds') :
- meetsMinimum(values.amount) ? tt('invites_jsx.meet_minimum') :
- countDecimals(values.amount) > 3 ? tt('transfer_jsx.use_only_3_digits_of_precison') :
- null,
- is_referral: null,
- })
- })
- this.handleSubmitForm =
- this.state.invite.handleSubmit(args => this.handleSubmit(args))
- }
-
- showQrPriv = e => {
- this.props.showQRKey({type: 'Invite', text: this.state.private_key.value, isPrivate: true});
- }
-
- showQrRegLink = e => {
- this.props.showQRKey({type: 'Invite', text: this.state.register_link.value, title: tt('invites_jsx.register_link')});
- }
-
- balanceValue() {
- const {account} = this.props
- return formatAsset(account.get('balance'), true, false, '')
- }
-
- assetBalanceClick = e => {
- e.preventDefault()
- // Convert '9 GOLOS' to 9
- this.state.amount.props.onChange(this.balanceValue().split(' ')[0])
- }
-
- onChangePrivateKey = (e) => {
- const {value} = e.target
- let pk = value.trim()
- this.updatePrivateKey(pk)
- }
-
- onChangeAmount = (e) => {
- const {value} = e.target
- this.state.amount.props.onChange(formatAmount(value))
- }
-
- onChangeIsReferral = (e) => {
- this.state.is_referral.props.onChange(e.target.checked)
- }
-
- handleSubmit = ({updateInitialValues}) => {
- const {createInvite, accountName} = this.props
- const {private_key, amount, is_referral} = this.state
- this.setState({loading: true});
- const public_key = PrivateKey.fromWif(private_key.value).toPublicKey().toString();
- createInvite({public_key, amount, is_referral, accountName,
- errorCallback: (e) => {
- if (e === 'Canceled') {
- this.setState({
- loading: false,
- errorMessage: ''
- })
- } else {
- console.log('createInvite ERROR', e)
- this.setState({
- loading: false,
- errorMessage: tt('g.server_returned_error')
- })
- }
- },
- successCallback: () => {
- this.setState({
- loading: false,
- errorMessage: '',
- successMessage: tt('invites_jsx.success'),
- createdInvite: public_key,
- })
- // remove successMessage after a while
- setTimeout(() => this.setState({successMessage: ''}), 8000)
- }})
- }
-
- render() {
- const {props: {account, isMyAccount, cprops, min_invite_balance}} = this
- const {private_key, register_link, amount, is_referral, loading, successMessage, errorMessage, createdInvite} = this.state
- const {submitting, valid} = this.state.invite
-
- let publicKeyLink = null;
- if (createdInvite) {
- publicKeyLink = `https://gapi.golos.today/api/database_api/get_invite?invite_key=${createdInvite}`;
- publicKeyLink=(
{tt('invites_jsx.public_key_can_be_checked') + ' '}
- {tt('g.here')}
-
- );
- }
-
- return (
)
- }
-}
-const AssetBalance = ({onClick, balanceValue}) =>
-
{tt('transfer_jsx.balance') + ": " + balanceValue}
-
-export default connect(
- (state, ownProps) => {
- const {account} = ownProps
- const accountName = account.get('name')
- const current = state.user.get('current')
- const username = current && current.get('username')
- const isMyAccount = username === accountName
- const cprops = state.global.get('cprops');
- const min_invite_balance = cprops && cprops.get('min_invite_balance')
- return {...ownProps, isMyAccount, accountName, min_invite_balance}
- },
- dispatch => ({
- createInvite: ({
- public_key, amount, is_referral, accountName, successCallback, errorCallback
- }) => {
- let operation = {
- creator: accountName,
- balance: parseFloat(amount.value, 10).toFixed(3) + ' GOLOS',
- invite_key: public_key
- }
- if (is_referral.value) {
- operation.extensions = [[0, {
- is_referral: true,
- }]];
- }
-
- const success = () => {
- dispatch(user.actions.getAccount())
- successCallback()
- }
-
- dispatch(transaction.actions.broadcastOperation({
- type: 'invite',
- accountName,
- operation,
- successCallback: success,
- errorCallback
- }))
- },
-
- showQRKey: ({type, isPrivate, text, title}) => {
- dispatch(g.actions.showDialog({name: "qr_key", params: {type, isPrivate, text, title}}));
- }
- })
-)(CreateInvite)
diff --git a/app/components/elements/Icon.jsx b/app/components/elements/Icon.jsx
index 4822ef9a5..8691024b1 100644
--- a/app/components/elements/Icon.jsx
+++ b/app/components/elements/Icon.jsx
@@ -26,6 +26,7 @@ const icons = new Map([
['extlink', require('app/assets/icons/extlink.svg')],
['golos', require('app/assets/icons/golos.svg')],
['dropdown-arrow', require('app/assets/icons/dropdown-arrow.svg')],
+ ['dropdown-center', require('app/assets/icons/dropdown-center.svg')],
['printer', require('app/assets/icons/printer.svg')],
['search', require('app/assets/icons/search.svg')],
['menu', require('app/assets/icons/menu.svg')],
diff --git a/app/components/elements/Invites.jsx b/app/components/elements/Invites.jsx
deleted file mode 100644
index 7b93a2370..000000000
--- a/app/components/elements/Invites.jsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import shouldComponentUpdate from 'app/utils/shouldComponentUpdate'
-import g from 'app/redux/GlobalReducer'
-import {connect} from 'react-redux';
-import CreateInvite from 'app/components/elements/CreateInvite'
-import ClaimInvite from 'app/components/elements/ClaimInvite'
-
-class Invites extends Component {
- static propTypes = {
- // HTML
- account: PropTypes.object.isRequired,
- }
-
- constructor() {
- super()
- this.shouldComponentUpdate = shouldComponentUpdate(this, 'Invites')
- }
-
- render() {
- const {account} = this.props
-
- return (
-
-
-
)
- }
-}
-
-export default connect(
- (state, ownProps) => {
- return {...ownProps}
- },
- dispatch => ({
- })
-)(Invites)
diff --git a/app/components/elements/PostSummaryThumb.jsx b/app/components/elements/PostSummaryThumb.jsx
index 87bd6ad2d..0cb35c651 100644
--- a/app/components/elements/PostSummaryThumb.jsx
+++ b/app/components/elements/PostSummaryThumb.jsx
@@ -1,10 +1,4 @@
import React from 'react';
-// keep this in memory, no requests
-import nsfwBanner from 'app/assets/images/nsfw/light.png'
-// import * as pixelate from './utils/effects/close-pixelate'
-
-
-//todo render fallback image by react, not by canvas depending on state?
export default class PostSummaryThumb extends React.Component {
constructor(props) {
@@ -12,62 +6,25 @@ export default class PostSummaryThumb extends React.Component {
this.state = {}
}
- handleImageLoaded() {
- if (this.props.isNsfw) {
- this.cp = new window.ClosePixelation(this.img, this.canvas)
- try {
- this.cp.render([
- {
- // todo fix author's algorithm
- // either loses color channels or disappears completely )
- // only these options render well
- resolution: 10,
- alpha: 1,
- size: 10,
- }
- ])
- }
- catch (e) {
- // clear canvas before the fallback image drawing!
- this.cp.ctx.clearRect(0, 0, this.cp.width, this.cp.height);
- this.cp.ctx.drawImage(this.defaultImage,
- this.cp.width / 2 - this.defaultImage.width / 2,
- this.cp.height / 2 - this.defaultImage.height / 2,
- )
- }
- }
- }
-
- handleImageErrored() {
- this.setState({imageStatus: 'failed to load'});
- }
-
render() {
- let {visitedClassName, title, body} = this.props;
- title = title ? title : '';
- body = body ? body : '';
+ let { visitedClassName, title, body, isNsfw } = this.props
+ title = title || ''
+ body = body || ''
+ let className = this.props.mobile ? ('PostSummary__image-mobile ' + visitedClassName) : 'PostSummary__image '
+ if (isNsfw) {
+ className += ' nsfw-img'
+ }
return (
{title}
diff --git a/app/components/elements/Voting.jsx b/app/components/elements/Voting.jsx
index aae775c59..fa984def9 100644
--- a/app/components/elements/Voting.jsx
+++ b/app/components/elements/Voting.jsx
@@ -173,7 +173,7 @@ class Voting extends React.Component {
}
- const total_votes = post_obj.get('from_search') ?
+ const total_votes = (post_obj.get('from_search') || post_obj.get('from_preview')) ?
post_obj.get('net_votes') :
post_obj.getIn(['stats', 'total_votes'])
diff --git a/app/components/elements/WitnessSettings.jsx b/app/components/elements/WitnessSettings.jsx
deleted file mode 100644
index 6237dafea..000000000
--- a/app/components/elements/WitnessSettings.jsx
+++ /dev/null
@@ -1,191 +0,0 @@
-import React from 'react';
-import {connect} from 'react-redux';
-import transaction from 'app/redux/Transaction'
-import reactForm from 'app/utils/ReactForm';
-import LoadingIndicator from 'app/components/elements/LoadingIndicator';
-import tt from 'counterpart';
-import { getMetadataReliably } from 'app/utils/NormalizeProfile';
-import g from 'app/redux/GlobalReducer';
-
-class WitnessSettings extends React.Component {
-
- constructor(props) {
- super();
- this.initForm(props);
- //this.shouldComponentUpdate = shouldComponentUpdate(this, 'WitnessSettings');
- }
-
- initForm(props) {
- reactForm({
- instance: this,
- name: 'witnessSettings',
- fields: ['url', 'signing_key'],
- initialValues: {
- url: props.witness_obj.get('url'),
- signing_key: props.witness_obj.get('signing_key'),
- ...props.witness},
- validation: values => ({
- url: values.url && !/^https?:\/\//.test(values.url) ? tt('settings_jsx.invalid_url') : null,
- })
- });
- this.handleSubmitForm =
- this.state.witnessSettings.handleSubmit(args => this.handleSubmit(args));
- }
-
- handleSubmit = ({updateInitialValues}) => {
- const {url, signing_key} = this.state
-
- const {account, updateWitness} = this.props
- this.setState({loading: true})
- updateWitness({
- owner: account.name,
- url: url.value,
- fee: '1.000 GOLOS',
- block_signing_key: signing_key.value,
- props: {
- account_creation_fee: this.props.witness_obj.get('props').get('account_creation_fee'),
- maximum_block_size: this.props.witness_obj.get('props').get('maximum_block_size'),
- sbd_interest_rate: this.props.witness_obj.get('props').get('sbd_interest_rate')
- },
- errorCallback: (e) => {
- if (e === 'Canceled') {
- this.setState({
- loading: false,
- errorMessage: ''
- })
- } else {
- console.log('updateWitness ERROR', e)
- this.setState({
- loading: false,
- changed: false,
- errorMessage: tt('g.server_returned_error')
- })
- }
- },
- successCallback: () => {
- this.setState({
- loading: false,
- changed: false,
- errorMessage: '',
- successMessage: tt('g.saved') + '!',
- })
- // remove successMessage after a while
- setTimeout(() => this.setState({successMessage: ''}), 4000)
- updateInitialValues()
- }
- });
- }
-
- clearSigningKey = (e) => {
- e.preventDefault();
- this.state.signing_key.props.onChange('GLS1111111111111111111111111111111114T1Anm');
- }
-
- render() {
- const {
- props: {account},
- } = this;
-
- const {state} = this
-
- const {submitting, valid, touched} = this.state.witnessSettings
- const disabled = state.loading || submitting || !valid || !touched
-
- const {url, signing_key} = this.state
-
- const showFeedsNodes = (e) => {
- e.preventDefault()
- const name = account.name
- this.props.feedsNodes(name)
- }
-
- return ();
- }
- componentDidMount() {
- }
-}
-
-export default connect(
- // mapStateToProps
- (state, props) => {
- const { account } = props;
- let metaData = account ? getMetadataReliably(account.json_metadata) : {}
- const witness = metaData && metaData.witness ? metaData.witness : {}
-
- return {
- metaData,
- witness,
- witness_obj: state.global.getIn(['witnesses', account.name])
- };
- },
- // mapDispatchToProps
- dispatch => ({
- updateAccount: ({successCallback, errorCallback, ...operation}) => {
- const success = () => {
- //dispatch(user.actions.getAccount())
- successCallback()
- }
-
- const options = {type: 'account_metadata', operation, successCallback: success, errorCallback}
- dispatch(transaction.actions.broadcastOperation(options))
- },
- publishFeed: ({successCallback, errorCallback, ...operation}) => {
- const success = () => {
- //dispatch(user.actions.getAccount())
- successCallback()
- }
-
- const options = {type: 'feed_publish', operation, successCallback: success, errorCallback}
- dispatch(transaction.actions.broadcastOperation(options))
- },
- updateWitness: ({successCallback, errorCallback, ...operation}) => {
- const success = () => {
- //dispatch(user.actions.getAccount())
- successCallback()
- }
-
- const options = {type: 'witness_update', operation, successCallback: success, errorCallback}
- dispatch(transaction.actions.broadcastOperation(options))
- },
- feedsNodes: (username) => {
- dispatch(g.actions.showDialog({name: 'feeds_nodes', params: {username}}))
- }
- })
-)(WitnessSettings)
diff --git a/app/components/elements/WitnessSettings.scss b/app/components/elements/WitnessSettings.scss
deleted file mode 100644
index 86aa50171..000000000
--- a/app/components/elements/WitnessSettings.scss
+++ /dev/null
@@ -1,36 +0,0 @@
-.inline {
-display: inline-block;
-}
-
-input[name=url2] {
-width: 400px;
-display: inline-block;
-}
-
-input[name=url2]:not(:hover):not(:focus) {
-border: none;
-transition: none;
--webkit-transition: none;
-box-shadow: none;
--webkit-box-shadow: none;
-}
-
-.clear-table td {
- padding: 0;
-}
-
-.clear-table tr {
- background-color: inherit !important;
-}
-
-.clear-table tbody {
- border: none;
-}
-
-.no-margin-bottom {
- margin-bottom: 0rem !important;
-}
-
-.margin-bottom {
- margin-bottom: 1rem !important;
-}
\ No newline at end of file
diff --git a/app/components/elements/donate/Donate.scss b/app/components/elements/donate/Donate.scss
new file mode 100644
index 000000000..70899b537
--- /dev/null
+++ b/app/components/elements/donate/Donate.scss
@@ -0,0 +1,68 @@
+.TipBalance {
+ display: inline-block;
+ margin-left: 5px;
+ vertical-align: middle;
+ padding-left: 1em;
+ padding-right: 1em;
+ padding-bottom: 0.75rem;
+ text-align: center;
+ width: 155px;
+ font-size: 95%;
+
+ .VerticalMenu {
+ width: 155px;
+ min-width: 155px;
+ }
+
+ &.mini {
+ font-size: 80%;
+ }
+
+ &.micro {
+ font-size: 70%;
+ }
+}
+
+.PresetSelector {
+ border-radius: 0px;
+ margin-left: -1px !important;
+ margin-right: 0px !important;
+
+ border: 1px solid #0078C4 !important;
+ color: #0078C4 !important;
+ font-size: 1em;
+ min-width: 55px;
+}
+
+.PresetSelector:nth-child(n+3):nth-last-child(n+3) {
+}
+
+.PresetSelector:first-child {
+ border-top-left-radius: 10px;
+ border-bottom-left-radius: 10px;
+ margin-right: 0px !important;
+ margin-left: 0px !important;
+}
+
+.PresetSelector:last-child {
+ border-top-right-radius: 10px;
+ border-bottom-right-radius: 10px;
+}
+
+.PresetSelector:hover {
+ border: 1px solid #002080 !important;
+ color: #002080 !important;
+ position: relative;
+ z-index: 10;
+}
+
+.PresetSelector__active {
+ border: 1px solid #002080 !important;
+ color: #002080 !important;
+ position: relative;
+ z-index: 10;
+}
+
+.PresetSelector__container {
+ display: inline-block;
+}
diff --git a/app/components/modules/Dialogs.jsx b/app/components/modules/Dialogs.jsx
index 60adf5fdf..a36a3db25 100644
--- a/app/components/modules/Dialogs.jsx
+++ b/app/components/modules/Dialogs.jsx
@@ -7,14 +7,12 @@ import g from 'app/redux/GlobalReducer';
import {Map, List} from 'immutable';
import shouldComponentUpdate from 'app/utils/shouldComponentUpdate';
import QrReader from 'app/components/elements/QrReader';
-import ConvertToSteem from 'app/components/elements/ConvertToSteem';
import SuggestPassword from 'app/components/elements/SuggestPassword';
import ChangePassword from 'app/components/elements/ChangePassword';
import CheckLoginOwner from 'app/components/elements/CheckLoginOwner';
import QrKeyView from 'app/components/elements/QrKeyView';
import PromotePost from 'app/components/modules/PromotePost';
import ExplorePost from 'app/components/modules/ExplorePost';
-import FeedsNodes from 'app/components/modules/FeedsNodes';
class Dialogs extends React.Component {
static propTypes = {
@@ -48,12 +46,6 @@ class Dialogs extends React.Component {
:
- k === 'convertToSteem' ?
-
-
-
-
- :
k === 'suggestPassword' ?
@@ -83,12 +75,6 @@ class Dialogs extends React.Component {
- :
- k === 'feeds_nodes' ?
-
-
-
-
:
null
return cmp ? r.push(cmp) : r
diff --git a/app/components/modules/Header.jsx b/app/components/modules/Header.jsx
index 1eb944f6f..62e0887d7 100644
--- a/app/components/modules/Header.jsx
+++ b/app/components/modules/Header.jsx
@@ -144,8 +144,6 @@ class Header extends React.Component {
if(route.params[1] === "posts" || route.params[1] === "comments"){
page_title = tt('header_jsx.comments_by') + " " + user_title;
}
- } else if (route.page === 'ConvertAssetsLoader') {
- page_title = tt('g.convert_assets')
} else {
page_name = ''; //page_title = route.page.replace( /([a-z])([A-Z])/g, '$1 $2' ).toLowerCase();
}
diff --git a/app/components/modules/PromotePost.jsx b/app/components/modules/PromotePost.jsx
index 1c6ff39e3..f466ad81b 100644
--- a/app/components/modules/PromotePost.jsx
+++ b/app/components/modules/PromotePost.jsx
@@ -160,6 +160,7 @@ export default connect(
dispatch(transaction.actions.broadcastOperation({
type: 'transfer',
operation,
+ username,
successCallback,
errorCallback
}))
diff --git a/app/components/modules/Settings.jsx b/app/components/modules/Settings.jsx
index 951637be3..0c7fd44f3 100644
--- a/app/components/modules/Settings.jsx
+++ b/app/components/modules/Settings.jsx
@@ -267,17 +267,6 @@ class Settings extends React.Component {
})
}
- unmuteAsset = (e) => {
- const sym = e.currentTarget.dataset.sym;
- let mutedUIA = [];
- mutedUIA = localStorage.getItem('mutedUIA');
- if (mutedUIA) try { mutedUIA = JSON.parse(mutedUIA) } catch (ex) {}
- if (!mutedUIA) mutedUIA = [];
- mutedUIA = mutedUIA.filter(o => o !== sym)
- localStorage.setItem('mutedUIA', JSON.stringify(mutedUIA));
- window.location.reload()
- }
-
onNotifyPresetChange = e => {
let notifyPresets = {...this.state.notifyPresets};
notifyPresets[e.target.dataset.type] = e.target.checked;
@@ -308,16 +297,6 @@ class Settings extends React.Component {
const following = follow && follow.getIn(['getFollowingAsync', account.name]);
const ignores = isOwnAccount && block && block.getIn(['blocking', account.name, 'result'])
const mutedInNew = isOwnAccount && props.mutedInNew;
- let mutedUIA = [];
- if (process.env.BROWSER) {
- mutedUIA = localStorage.getItem('mutedUIA');
- if (mutedUIA) try { mutedUIA = JSON.parse(mutedUIA) } catch (ex) {}
- if (!mutedUIA) mutedUIA = [];
- }
- let mutedUIAlist = [];
- for (let sym of mutedUIA) {
- mutedUIAlist.push({sym} X
)
- }
const {pImageUploading, cImageUploading} = this.state;
const languageSelectBox =
}
- {mutedUIA && mutedUIA.length > 0 &&
-
-
-
-
{tt('settings_jsx.muted_uia')}
- {mutedUIAlist}
-
-
}
-
{ignores && ignores.size > 0 &&
diff --git a/app/components/modules/TopRightMenu.jsx b/app/components/modules/TopRightMenu.jsx
index 21387ad46..3d7988eb5 100644
--- a/app/components/modules/TopRightMenu.jsx
+++ b/app/components/modules/TopRightMenu.jsx
@@ -79,6 +79,7 @@ function TopRightMenu({account, savings_withdraws, price_per_golos, globalprops,
;
const feedLink = `/@${username}/feed`;
const repliesLink = `/@${username}/recent-replies`;
+ const discussionsLink = `/@${username}/discussions`
const walletLink = walletUrl(`/@${username}/transfers`)
const settingsLink = `/@${username}/settings`;
const accountLink = `/@${username}`;
@@ -124,7 +125,7 @@ function TopRightMenu({account, savings_withdraws, price_per_golos, globalprops,
}
additional_menu.push(
{ link: '#', onClick: toggleNightmode, icon: 'editor/eye', value: tt('g.night_mode') },
- { link: walletUrl('/market/GOLOS/GBG'), target: walletTarget(), icon: 'trade', value: tt("navigation.market") },
+ { link: walletUrl('/market'), target: walletTarget(), icon: 'trade', value: tt("navigation.market") },
{ link: '/services', icon: 'new/monitor', value: tt("navigation.services") },
{ link: '/search', icon: 'new/search', value: tt("navigation.search") },
{ link: walletUrl('/exchanges'), target: walletTarget(), icon: 'editor/coin', value: tt("navigation.buy_sell") },
@@ -154,13 +155,13 @@ function TopRightMenu({account, savings_withdraws, price_per_golos, globalprops,
{link: feedLink, icon: 'new/home', value: tt('g.feed'), addon:
},
{link: accountLink, icon: 'new/blogging', value: tt('g.blog')},
{link: repliesLink, icon: 'new/answer', value: tt('g.replies'), addon:
},
+ {link: discussionsLink, icon: 'new/bell', value: tt('g.discussions'), addon:
},
+ {link: mentionsLink, icon: 'new/mention', value: tt('g.mentions'), addon:
},
+ {link: walletLink, target: walletTarget(), icon: 'new/wallet', value: tt('g.wallet'), addon:
},
+ {link: donatesLink, target: walletTarget(), icon: 'hf/hf8', value: tt('g.rewards'), addon:
},
(messagesLink ?
{link: messagesLink, icon: 'new/envelope', value: tt('g.messages'), target: '_blank', addon:
} :
null),
- {link: mentionsLink, icon: 'new/mention', value: tt('g.mentions'), addon:
},
- {link: donatesLink, target: walletTarget(), icon: 'editor/coin', value: tt('g.rewards'), addon:
},
- {link: walletLink, target: walletTarget(), icon: 'new/wallet', value: tt('g.wallet'), addon:
},
- {link: ordersLink, target: walletTarget(), icon: 'trade', value: tt('navigation.market2'), addon:
},
{link: settingsLink, icon: 'new/setting', value: tt('g.settings')},
loggedIn ?
{link: '#', icon: 'new/logout', onClick: goChangeAccount, value: tt('g.change_acc')} :
diff --git a/app/components/modules/WitnessProps.jsx b/app/components/modules/WitnessProps.jsx
deleted file mode 100644
index 331a8ae34..000000000
--- a/app/components/modules/WitnessProps.jsx
+++ /dev/null
@@ -1,263 +0,0 @@
-import React from 'react';
-import {connect} from 'react-redux';
-import transaction from 'app/redux/Transaction'
-import reactForm from 'app/utils/ReactForm';
-import LoadingIndicator from 'app/components/elements/LoadingIndicator';
-import tt from 'counterpart';
-import WitnessSettings from 'app/components/elements/WitnessSettings';
-
-class WitnessProps extends React.Component {
-
- constructor(props) {
- super();
- this.initForm(props);
- //this.shouldComponentUpdate = shouldComponentUpdate(this, 'WitnessProps');
- }
-
- wprops_19 = [
- [
- ['account_creation_fee', 'golos'],
- ['create_account_min_golos_fee', 'golos'],
- ['create_account_min_delegation', 'golos'],
- ['create_account_delegation_time', 'raw'],
- ],
- [
- ['max_referral_interest_rate'],
- ['max_referral_term_sec', 'time'],
- ['min_referral_break_fee', 'golos'],
- ['max_referral_break_fee', 'golos'],
- ],
- [
- ['maximum_block_size', 'raw'],
- ['worker_emission_percent'],
- ['vesting_of_remain_percent'],
- ],
- [
- ['sbd_interest_rate'],
- ['convert_fee_percent'],
- ['sbd_debt_convert_rate'],
- ],
- [
- ['asset_creation_fee', 'gbg'],
- ['min_delegation', 'golos'],
- ['max_delegated_vesting_interest_rate'],
- ],
- [
- ['posts_window', 'raw'],
- ['posts_per_window', 'raw'],
- ],
- [
- ['comments_window', 'raw'],
- ['comments_per_window', 'raw'],
- ],
- [
- ['votes_window', 'raw'],
- ['votes_per_window', 'raw'],
- ['vote_regeneration_per_day', 'raw'],
- ],
- [
- ['negrep_posting_window', 'dropped', 0],
- ['negrep_posting_per_window', 'dropped', 0],
- ['custom_ops_bandwidth_multiplier', 'raw'],
- ['unwanted_operation_cost', 'golos'],
- ['unlimit_operation_cost', 'golos'],
- ],
- [
- ['min_golos_power_to_curate', 'golos'],
- ['curation_reward_curve', ['bounded','linear','square_root']],
- ['min_curation_percent'],
- ['max_curation_percent'],
- ],
- [
- ['min_invite_balance', 'golos'],
- ['invite_transfer_interval_sec', 'time'],
- ['worker_request_creation_fee', 'gbg'],
- ['worker_request_approve_min_percent'],
- ],
- [
- ['witness_skipping_reset_time', 'time'],
- ['witness_idleness_time', 'time'],
- ['account_idleness_time', 'time'],
- ['claim_idleness_time', 'dropped', 0],
- ],
- [
- ['worker_reward_percent', 'dropped', 0],
- ['witness_reward_percent', 'dropped', 0],
- ['vesting_reward_percent', 'dropped', 0],
- ],
- [
- ['auction_window_size', 'dropped', 0],
- ['allow_distribute_auction_reward', 'dropped', 'true'],
- ['allow_return_auction_reward_to_fund', 'dropped', 'true'],
- ],
- ];
-
- wprops_22 = [
- ];
-
- initForm(props) {
- this.wprops = [...this.wprops_19, ...this.wprops_22];
- this.wp_flat = this.wprops.flat();
- this.prop_names = this.wp_flat.map(p => p[0]);
- reactForm({
- instance: this,
- name: 'witnessProps',
- fields: this.prop_names,
- initialValues: props.witness_obj.toJS().props,
- validation: values => ({
- })
- });
- this.handleSubmitForm =
- this.state.witnessProps.handleSubmit(args => this.handleSubmit(args));
- }
-
- handleSubmit = ({updateInitialValues}) => {
- const {account, updateChainProperties} = this.props;
- this.setState({loading: true});
-
- let props = {};
- for (let prop of this.wp_flat) {
- if (prop.length === 1 || prop[1] == 'raw' || prop[1] == 'time') {
- props[prop[0]] = parseInt(this.state[prop[0]].value);
- } else if (prop[1] === 'dropped') {
- props[prop[0]] = prop[2];
- } else {
- props[prop[0]] = this.state[prop[0]].value;
- }
- }
- if (props.curation_reward_curve == 'bounded') {
- props.curation_reward_curve = 0;
- } else if (props.curation_reward_curve == 'linear') {
- props.curation_reward_curve = 1;
- } else if (props.curation_reward_curve == 'square_root') {
- props.curation_reward_curve = 2;
- }
- props.create_account_delegation_time = parseInt(props.create_account_delegation_time);
- props.custom_ops_bandwidth_multiplier = parseInt(props.custom_ops_bandwidth_multiplier);
- props.maximum_block_size = parseInt(props.maximum_block_size);
- props.sbd_interest_rate = parseInt(props.sbd_interest_rate);
- props.sbd_debt_convert_rate = parseInt(props.sbd_debt_convert_rate);
- props.max_delegated_vesting_interest_rate = parseInt(props.max_delegated_vesting_interest_rate);
- props.max_referral_term_sec = parseInt(props.max_referral_term_sec);
- props.max_referral_interest_rate = parseInt(props.max_referral_interest_rate);
- props.posts_window = parseInt(props.posts_window);
- props.comments_window = parseInt(props.comments_window);
- props.posts_per_window = parseInt(props.posts_per_window);
- props.comments_per_window = parseInt(props.comments_per_window);
- updateChainProperties({
- owner: account.name,
- props: [7, props],
- errorCallback: (e) => {
- if (e === 'Canceled') {
- this.setState({
- loading: false,
- errorMessage: ''
- })
- } else {
- console.error('updateChainProperties ERROR', e)
- this.setState({
- loading: false,
- changed: false,
- errorMessage: tt('g.server_returned_error')
- })
- }
- },
- successCallback: () => {
- this.setState({
- loading: false,
- changed: false,
- errorMessage: '',
- successMessage: tt('g.saved') + '!',
- })
- // remove successMessage after a while
- setTimeout(() => this.setState({successMessage: ''}), 4000)
- updateInitialValues()
- }
- });
- }
-
- render() {
- const {
- props: {current_user, json_metadata},
- } = this;
- //const username = current_user ? current_user.get('username') : null
-
- const {state} = this
-
- const {submitting, valid, touched} = this.state.witnessProps;
- const disabled = state.loading || submitting || !valid || !touched
-
- let groups = this.wprops.map((wp) => {
- let fields = wp.map((f) => {
- const field = this.state[f[0]];
-
- let input = null;
- if (f[1] === 'bool') {
- input =
- } else if (f[1] === 'raw') {
- input =
- } else if (f[1] === 'dropped') {
- return null;
- } else {
- input =
- }
-
- return (
-
- {field.touched && field.error}
- | );
- });
- return (
{fields}
);
- });
-
- return (
);
- }
- componentDidMount() {
- }
-}
-
-export default connect(
- // mapStateToProps
- (state, props) => {
- const { account } = props;
-
- return {
- witness_obj: state.global.getIn(['witnesses', account.name])
- };
- },
- // mapDispatchToProps
- dispatch => ({
- updateChainProperties: ({successCallback, errorCallback, ...operation}) => {
- const success = () => {
- //dispatch(user.actions.getAccount())
- successCallback()
- }
-
- const options = {type: 'chain_properties_update', operation, successCallback: success, errorCallback}
- dispatch(transaction.actions.broadcastOperation(options))
- }
- })
-)(WitnessProps)
diff --git a/app/components/modules/WitnessProps.scss b/app/components/modules/WitnessProps.scss
deleted file mode 100644
index 7ac46ef15..000000000
--- a/app/components/modules/WitnessProps.scss
+++ /dev/null
@@ -1,15 +0,0 @@
-.WitnessPropsTable label {
- text-transform: none;
-}
-
-.WitnessPropsTable input {
- height: 1.5rem;
-}
-
-.WitnessPropsTable input[type="checkbox"] {
- display: block;
-}
-
-.WitnessPropsTable td {
- padding: 0 5px;
-}
diff --git a/app/components/pages/Post.jsx b/app/components/pages/Post.jsx
index 658c8cfbc..2e437500f 100644
--- a/app/components/pages/Post.jsx
+++ b/app/components/pages/Post.jsx
@@ -1,19 +1,25 @@
import React from 'react';
import PropTypes from 'prop-types'
-import Comment from 'app/components/cards/Comment';
-import PostFull from 'app/components/cards/PostFull';
-import {connect} from 'react-redux';
-import {sortComments} from 'app/utils/comments';
+import {connect} from 'react-redux'
+import {Set} from 'immutable'
+import tt from 'counterpart'
+
+import Comment from 'app/components/cards/Comment'
+import PostFull from 'app/components/cards/PostFull'
import Follow from 'app/components/elements/Follow'
+import FoundationDropdownMenu from 'app/components/elements/FoundationDropdownMenu'
+import Icon from 'app/components/elements/Icon'
+import IllegalContentMessage from 'app/components/elements/IllegalContentMessage'
import LoadingIndicator from 'app/components/elements/LoadingIndicator';
-import FoundationDropdownMenu from 'app/components/elements/FoundationDropdownMenu';
-import IllegalContentMessage from 'app/components/elements/IllegalContentMessage';
-import {Set} from 'immutable'
-import tt from 'counterpart';
-import shouldComponentUpdate from 'app/utils/shouldComponentUpdate';
-import { authRegisterUrl, } from 'app/utils/AuthApiClient'
+import g from 'app/redux/GlobalReducer'
import user from 'app/redux/User'
+import { authRegisterUrl, } from 'app/utils/AuthApiClient'
+import { isBlocked } from 'app/utils/blacklist'
+import { sortComments } from 'app/utils/comments'
+import { subscribePost, unsubscribePost, getSubs } from 'app/utils/NotifyApiClient'
+import shouldComponentUpdate from 'app/utils/shouldComponentUpdate'
import session from 'app/utils/session'
+import { isHighlight, markCommentsRead, notifyPageView } from 'app/utils/NotifyApiClient'
class Post extends React.Component {
static propTypes = {
@@ -31,12 +37,97 @@ class Post extends React.Component {
showNegativeComments: false
};
this.shouldComponentUpdate = shouldComponentUpdate(this, 'Post')
+ this.commentsRef = React.createRef()
}
- componentDidMount() {
- if (window.location.hash.indexOf('comments') !== -1) {
- const comments_el = document.getElementById('comments');
- if (comments_el) comments_el.scrollIntoView();
+ async componentDidMount() {
+ if (process.env.BROWSER) {
+ const dis = this.getDiscussion()
+ if (!dis) {
+ return
+ }
+
+ const author = dis.get('author')
+ const permlink = dis.get('permlink')
+
+ const account = session.load().currentName
+ if (account) {
+ let found = false
+ const res = await getSubs(account)
+ if (res.result) {
+ for (const sub of res.result.subs) {
+ const [ subAuthor, subPermlink ] = sub.entityId.split('|')
+ if (subAuthor === author && subPermlink === permlink) {
+ found = true
+ break
+ }
+ }
+ }
+ this.setState({ subscribed: found })
+
+ this.processHighlight()
+ }
+
+ try {
+ notifyPageView(author, permlink)
+ } catch (err) {}
+ }
+ }
+
+ async componentDidUpdate() {
+ this.processHighlight()
+ }
+
+ processHighlight() {
+ const curUser = session.load().currentName
+ if (!curUser) {
+ return
+ }
+ const dis = this.getDiscussion()
+ if (!dis) {
+ return null
+ }
+ const replies = dis.get('replies').toJS()
+ const loaded = replies.length || !dis.get('children')
+ if (loaded) {
+ const author = dis.get('author')
+ const permlink = dis.get('permlink')
+
+ const highlight = isHighlight()
+ if (!this.state.highlight)
+ this.setState({ highlight })
+
+ if (highlight) {
+ if (!dis.get('highlighted')) {
+ return null
+ }
+ markCommentsRead(curUser, author, permlink)
+ this.props.markSubRead(author, permlink)
+ this.readen = true
+ let counter = 0
+ const scroller = setInterval(() => {
+ let notYet = false
+ for (const img of document.getElementsByTagName('img')) {
+ if (!img.complete) {
+ notYet = true
+ break
+ }
+ }
+ ++counter
+ if ((notYet && counter < 2000) || !this.commentsRef.current) return
+
+ const proceed = () => {
+ clearInterval(scroller)
+ this.commentsRef.current.scrollIntoView()
+ document.scrollingElement.scrollTop -= 200
+ }
+ if (counter > 100) {
+ setTimeout(proceed, 500)
+ } else {
+ proceed()
+ }
+ }, 1)
+ }
}
}
@@ -104,19 +195,52 @@ class Post extends React.Component {
return this._renderStub(children)
}
+ subscribe = async (e, dis) => {
+ e.preventDefault()
+ try {
+ const { current_user, } = this.props
+ const account = current_user && current_user.get('username')
+ if (!account) return
+ if (this.state.subscribed) {
+ await unsubscribePost(account, dis.get('author'), dis.get('permlink'))
+ this.setState({subscribed: false})
+ return
+ }
+ await subscribePost(account, dis.get('author'), dis.get('permlink'))
+ this.setState({subscribed: true})
+ } catch (err) {
+ alert(err.message || err)
+ }
+ }
+
+ getDiscussion = (postGetter = () => {}) => {
+ let { content, post, routeParams } = this.props
+ if (!post) {
+ post = routeParams.username + '/' + routeParams.slug
+ }
+ postGetter(post)
+ const dis = content.get(post)
+ return dis
+ }
+
render() {
const {following, content, current_user} = this.props
const {showNegativeComments, commentHidden, showAnyway} = this.state
let { post } = this.props;
const { aiPosts } = this.props;
- if (!post) {
- const route_params = this.props.routeParams;
- post = route_params.username + '/' + route_params.slug;
- }
- const dis = content.get(post);
+ const dis = this.getDiscussion(p => post = p)
if (!dis) return null;
+ if (process.env.BROWSER) {
+ const author = dis.get('author')
+ const permlink = dis.get('permlink')
+ const highlight = isHighlight()
+ if (highlight && !dis.get('highlighted') && !this.readen) {
+ return this._renderLoadingStub()
+ }
+ }
+
const stats = dis.get('stats').toJS()
if(!showAnyway) {
@@ -134,7 +258,7 @@ class Post extends React.Component {
let replies = dis.get('replies').toJS();
- let sort_order = 'trending';
+ let sort_order = this.state.highlight ? 'new' : 'trending';
if( this.props.location && this.props.location.query.sort )
sort_order = this.props.location.query.sort;
@@ -194,7 +318,7 @@ class Post extends React.Component {
return this._renderOnlyApp()
}
if (stats.isOnlyblog) {
- if (!following && (typeof(localStorage) === 'undefined' || !session.load().currentName)) {
+ if (!following && (typeof(localStorage) === 'undefined' || session.load().currentName)) {
return this._renderLoadingStub()
} else if (!following ||
(!following.includes(dis.get('author')) &&
@@ -221,9 +345,11 @@ class Post extends React.Component {
- if($STM_Config.blocked_users.includes(post.split("/")[0])) {
+ if (isBlocked(post.split('/')[0], $STM_Config.blocked_users)) {
return (
@@ -242,19 +368,31 @@ class Post extends React.Component {
Golos Blogs
-Основные веб-клиенты блогов golos.id и golos.in, альтернативные клиенты, а также Desktop (для Windows и Linux)
+Основные веб-клиенты блогов golos.id и golos.in, альтернативные клиенты, а также Desktop (для Windows и Linux)