diff --git a/package.json b/package.json index 11f54e6da2..37d7a7caeb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "busy", - "version": "2.5.0", + "version": "2.5.1", "engines": { "node": ">=7.10.1", "npm": "=5.3.0" @@ -50,7 +50,7 @@ "babel-register": "^6.24.1", "babel-runtime": "^6.18.0", "bs58": "^4.0.0", - "busyjs": "https://github.com/busyorg/busyjs#dev", + "busyjs": "^1.0.2", "cheerio": "^1.0.0-rc.2", "classnames": "^2.2.5", "cookie-parser": "^1.4.3", @@ -101,7 +101,7 @@ "sc2-sdk": "^1.0.1", "secure-random": "^1.1.1", "speakingurl": "^11.0.0", - "steemscript": "https://github.com/busyorg/steemscript.git", + "steemscript": "^1.0.1", "store": "^1.3.20", "striptags": "^2.1.1", "text-ellipsis": "^1.0.3", diff --git a/scripts/utils/getChangedFiles.js b/scripts/utils/getChangedFiles.js index 5388bbf508..04e9a34ff7 100644 --- a/scripts/utils/getChangedFiles.js +++ b/scripts/utils/getChangedFiles.js @@ -23,7 +23,7 @@ function execGitCmd(args) { } function getChangedFiles() { - const mergeBase = execGitCmd(['merge-base', 'HEAD', 'master']); + const mergeBase = execGitCmd(['merge-base', 'HEAD', 'develop']); return [ ...execGitCmd(['diff', '--name-only', '--diff-filter=ACMRTUB', mergeBase]), ...execGitCmd(['ls-files', '--others', '--exclude-standard']), diff --git a/src/client/Wrapper.js b/src/client/Wrapper.js index ae3d49ba6c..33686c91c1 100644 --- a/src/client/Wrapper.js +++ b/src/client/Wrapper.js @@ -218,7 +218,7 @@ export default class Wrapper extends React.PureComponent { - + diff --git a/src/client/activity/UserActionContents.js b/src/client/activity/UserActionContents.js index a3b02bff40..bb5ca1b175 100644 --- a/src/client/activity/UserActionContents.js +++ b/src/client/activity/UserActionContents.js @@ -4,7 +4,16 @@ import _ from 'lodash'; import * as accountHistory from '../../common/constants/accountHistory'; const UserActionContents = ({ actionType, actionDetails }) => { - if (_.includes(accountHistory.PARSED_PROPERTIES, actionType)) { + if (actionType === accountHistory.CUSTOM_JSON) { + // Special case: Follow, Unfollow, Reblog, Mute operations are + // a part of custom json operations with actionDetails.id as "follow". + // We have a parser for these ops, however we cannot parse + // every custom json since it's flexible and there is no + // standard for the structure of custom_json ops. + if (_.includes(accountHistory.PARSED_CUSTOM_JSON_IDS, actionDetails.id)) { + return null; + } + } else if (_.includes(accountHistory.PARSED_PROPERTIES, actionType)) { return null; } diff --git a/src/client/activity/UserActionIcon.js b/src/client/activity/UserActionIcon.js index 0bfbfdbe71..742263ba8a 100644 --- a/src/client/activity/UserActionIcon.js +++ b/src/client/activity/UserActionIcon.js @@ -34,6 +34,9 @@ class UserActionIcon extends React.Component { const customActionType = actionJSON[0]; const customActionDetails = actionJSON[1]; + if (!_.includes(accountHistoryConstants.PARSED_CUSTOM_JSON_IDS, actionDetails.id)) { + return 'icon-document'; + } if ( customActionType === accountHistoryConstants.REBLOG && currentUsername === customActionDetails.account diff --git a/src/client/activity/UserActionMessage.js b/src/client/activity/UserActionMessage.js index e996e77c33..8cdfeb453d 100644 --- a/src/client/activity/UserActionMessage.js +++ b/src/client/activity/UserActionMessage.js @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage, FormattedNumber } from 'react-intl'; import { Link } from 'react-router-dom'; +import _ from 'lodash'; import formatter from '../helpers/steemitFormatter'; import * as accountHistoryConstants from '../../common/constants/accountHistory'; import VoteActionMessage from './VoteActionMessage'; @@ -18,6 +19,10 @@ class UserActionMessage extends React.Component { currentUsername: PropTypes.string.isRequired, }; + static renderDefault(actionType) { + return ; + } + renderFormattedMessage() { const { actionType, @@ -89,6 +94,9 @@ class UserActionMessage extends React.Component { /> ); case accountHistoryConstants.CUSTOM_JSON: + if (!_.includes(accountHistoryConstants.PARSED_CUSTOM_JSON_IDS, actionDetails.id)) { + return UserActionMessage.renderDefault(actionType); + } return ; case accountHistoryConstants.ACCOUNT_UPDATE: return ; @@ -184,7 +192,7 @@ class UserActionMessage extends React.Component { /> ); default: - return ; + return UserActionMessage.renderDefault(actionType); } } render() { diff --git a/src/client/activity/UserActivityActions.less b/src/client/activity/UserActivityActions.less index 60f60bfdc8..f3f3c5dfdd 100644 --- a/src/client/activity/UserActivityActions.less +++ b/src/client/activity/UserActivityActions.less @@ -23,31 +23,41 @@ color: @grey-nightRider; display: flex; padding: 10px 15px; - border-right: @base-border; - border-left: @base-border; border-bottom: @base-border; &:nth-of-type(2) { border-top: @base-border; - border-top-left-radius: 0; - border-top-right-radius: 0; - @media @small { + } + + @media @small { + border-right: @base-border; + border-left: @base-border; + + &:nth-of-type(2), + &:last-of-type { border-top-left-radius: @border-radius-base; border-top-right-radius: @border-radius-base; } - } - - &:last-of-type { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - @media @small { + &:last-of-type { border-bottom-left-radius: @border-radius-base; border-bottom-right-radius: @border-radius-base; } } } + &__loader { + border-bottom: @base-border; + padding: 20px; + + @media @small { + border-right: @base-border; + border-left: @base-border; + border-bottom-left-radius: @border-radius-base; + border-bottom-right-radius: @border-radius-base; + } + } + &__memo { word-wrap: break-word; } diff --git a/src/client/activity/UserActivityActionsLoader.js b/src/client/activity/UserActivityActionsLoader.js index 86d7ecba1a..a9ef837b30 100644 --- a/src/client/activity/UserActivityActionsLoader.js +++ b/src/client/activity/UserActivityActionsLoader.js @@ -66,8 +66,10 @@ class UserActivityActionsLoader extends React.Component { const { loadingMoreUsersAccountHistory } = this.props; if (loadingMoreUsersAccountHistory) { return ( -
- +
+
+ +
); } diff --git a/src/client/auth/authActions.js b/src/client/auth/authActions.js index ce1073d7a5..5d3a8afb2b 100644 --- a/src/client/auth/authActions.js +++ b/src/client/auth/authActions.js @@ -1,6 +1,6 @@ import Cookie from 'js-cookie'; import { createAction } from 'redux-actions'; -import { getAuthenticatedUserName, getIsAuthenticated } from '../reducers'; +import { getAuthenticatedUserName, getIsAuthenticated, getIsLoaded } from '../reducers'; import { createAsyncActionType } from '../helpers/stateHelpers'; import { addNewNotification } from '../app/appActions'; import { getFollowing } from '../user/userActions'; @@ -24,9 +24,11 @@ export const BUSY_LOGIN = createAsyncActionType('@auth/BUSY_LOGIN'); const loginError = createAction(LOGIN_ERROR); export const login = () => (dispatch, getState, { steemConnectAPI }) => { + const state = getState(); + let promise = Promise.resolve(null); - if (getIsAuthenticated(getState())) { + if (getIsLoaded(state)) { promise = Promise.resolve(null); } else if (!steemConnectAPI.options.accessToken) { promise = Promise.reject(new Error('There is not accessToken present')); @@ -40,7 +42,7 @@ export const login = () => (dispatch, getState, { steemConnectAPI }) => { promise, }, meta: { - refresh: getIsAuthenticated(getState()), + refresh: getIsLoaded(state), }, }).catch(() => dispatch(loginError())); }; diff --git a/src/client/comments/commentsActions.js b/src/client/comments/commentsActions.js index 01931e0ea2..ed91a3203b 100644 --- a/src/client/comments/commentsActions.js +++ b/src/client/comments/commentsActions.js @@ -1,9 +1,8 @@ import { createAction } from 'redux-actions'; import { createCommentPermlink, getBodyPatchIfSmaller } from '../vendor/steemitHelpers'; import { notify } from '../app/Notification/notificationActions'; -import { jsonParse } from '../../client/helpers/formatter'; - -const version = require('../../../package.json').version; +import { jsonParse } from '../helpers/formatter'; +import { createPostMetadata } from '../helpers/postHelpers'; export const GET_COMMENTS = 'GET_COMMENTS'; export const GET_COMMENTS_START = 'GET_COMMENTS_START'; @@ -99,10 +98,11 @@ export const sendComment = (parentPost, body, isUpdating = false, originalCommen ? originalComment.permlink : createCommentPermlink(parentAuthor, parentPermlink); - const defaultJsonMetadata = { tags: [category], community: 'busy', app: `busy/${version}` }; - const jsonMetadata = isUpdating - ? jsonParse(originalComment.json_metadata) || defaultJsonMetadata - : defaultJsonMetadata; + const jsonMetadata = createPostMetadata( + body, + [category], + isUpdating && jsonParse(originalComment.json_metadata), + ); const newBody = isUpdating ? getBodyPatchIfSmaller(originalComment.body, body) : body; diff --git a/src/client/components/BBackTop.js b/src/client/components/BBackTop.js index 29b3c09e94..0dad734d9c 100644 --- a/src/client/components/BBackTop.js +++ b/src/client/components/BBackTop.js @@ -4,81 +4,28 @@ import { BackTop } from 'antd'; import classNames from 'classnames'; import './BBackTop.less'; -class BBackTop extends React.Component { - static propTypes = { - isModal: PropTypes.bool, - target: PropTypes.func, - toggleHeight: PropTypes.number, - }; - - static defaultProps = { - isModal: false, - target: undefined, - toggleHeight: 100, - }; - - static getDefaultTarget() { - return window; - } - - static getScroll(target) { - if (typeof window === 'undefined') return 0; - - return target === window ? target.pageYOffset : target.scrollTop; - } - - constructor(props) { - super(props); - this.state = { - visible: false, - }; - } - - componentDidMount() { - this.previousScroll = 0; - this.scrollEvent = this.getTarget().addEventListener('scroll', this.handleScroll); - } - - componentWillUnmount() { - if (this.scrollEvent) this.scrollEvent.remove(); - } - - getTarget = () => (this.props.target || BBackTop.getDefaultTarget)(); - - handleScroll = () => { - const currentScroll = BBackTop.getScroll(this.getTarget()); - if (currentScroll === 0) { - this.previousScroll = 0; - return; - } - - const diff = currentScroll - this.previousScroll; - if (diff > 0) { - this.previousScroll = currentScroll; - this.setState({ visible: false }); - } else if (diff < -this.props.toggleHeight) { - this.setState({ visible: true }); - } - }; - - render() { - const { isModal, toggleHeight, ...otherProps } = this.props; - return ( - this.state.visible && ( -
-
- - - -
-
- ) - ); - } +export default function BBackTop({ className, isModal, ...otherProps }) { + return ( +
+
+ + + +
+
+ ); } -export default BBackTop; +BBackTop.propTypes = { + className: PropTypes.string, + isModal: PropTypes.bool, +}; + +BBackTop.defaultProps = { + className: '', + isModal: false, +}; diff --git a/src/client/helpers/__tests__/errorMiddleware.js b/src/client/helpers/__tests__/errorMiddleware.js index 5f43e975a5..9921d9cc9a 100644 --- a/src/client/helpers/__tests__/errorMiddleware.js +++ b/src/client/helpers/__tests__/errorMiddleware.js @@ -44,6 +44,10 @@ describe('parseBlockChainError', () => { expected = 'Your voting power is too small, please accumulate more voting power or steem power.'; expect(actual).toEqual(expected); + + actual = parseBlockChainError('Cannot increase payout within last twelve hours before payout.'); + expected = "You can't increase payout within last twelve hours before payout."; + expect(actual).toEqual(expected); }); it('should extract message from unknown error', () => { diff --git a/src/client/helpers/parser.js b/src/client/helpers/parser.js index 00750e4a16..2a1c25b89e 100644 --- a/src/client/helpers/parser.js +++ b/src/client/helpers/parser.js @@ -46,7 +46,7 @@ export function extractImageTags(body) { } export function extractLinks(body) { - return extract(body, hrefRegex); + return extract(body, hrefRegex).map(_.unescape); } export default null; diff --git a/src/client/helpers/postHelpers.js b/src/client/helpers/postHelpers.js index e112284025..7997ea2cb6 100644 --- a/src/client/helpers/postHelpers.js +++ b/src/client/helpers/postHelpers.js @@ -1,11 +1,13 @@ import _ from 'lodash'; import { getHtml } from '../components/Story/Body'; -import { extractImageTags } from './parser'; +import { extractImageTags, extractLinks } from './parser'; import { categoryRegex } from './regexHelpers'; import { jsonParse } from './formatter'; import DMCA from '../../common/constants/dmca.json'; import whiteListedApps from './apps'; +const appVersion = require('../../../package.json').version; + export const isPostDeleted = post => post.title === 'deleted' && post.body === 'deleted'; export const isPostTaggedNSFW = post => { @@ -60,8 +62,42 @@ export function getContentImages(content, parsed = false) { const parsedBody = parsed ? content : getHtml(content, {}, 'text'); return extractImageTags(parsedBody).map(tag => - tag.src.replace('https://steemitimages.com/0x0/', ''), + _.unescape(tag.src.replace('https://steemitimages.com/0x0/', '')), ); } -export default null; +export function createPostMetadata(body, tags, oldMetadata = {}) { + let metaData = { + community: 'busy', + app: `busy/${appVersion}`, + format: 'markdown', + }; + + metaData = { + ...oldMetadata, + ...metaData, + }; + + const users = []; + const userRegex = /@([a-zA-Z.0-9-]+)/g; + let matches; + + // eslint-disable-next-line no-cond-assign + while ((matches = userRegex.exec(body))) { + if (users.indexOf(matches[1]) === -1) { + users.push(matches[1]); + } + } + + const parsedBody = getHtml(body, {}, 'text'); + + const images = getContentImages(parsedBody, true); + const links = extractLinks(parsedBody); + + metaData.tags = tags; + metaData.users = users; + metaData.links = links.slice(0, 10); + metaData.image = images; + + return metaData; +} diff --git a/src/client/post/PostModal.js b/src/client/post/PostModal.js index e92f41f9e3..a3e5d2ec25 100644 --- a/src/client/post/PostModal.js +++ b/src/client/post/PostModal.js @@ -50,12 +50,14 @@ class PostModal extends React.Component { } componentDidMount() { - if (document) { + if (typeof document !== 'undefined') { const modalContents = document.getElementsByClassName('ant-modal-wrap'); const modalContentElement = _.get(modalContents, 0); if (modalContentElement) { modalContentElement.scrollTop = 0; } + + document.body.classList.add('post-modal'); } const { currentShownPost } = this.props; @@ -64,6 +66,10 @@ class PostModal extends React.Component { } componentWillUnmount() { + if (typeof document !== 'undefined') { + document.body.classList.remove('post-modal'); + } + this.props.hidePostModal(); } diff --git a/src/client/post/Write/Write.js b/src/client/post/Write/Write.js index cb9f403bae..d4fc201621 100644 --- a/src/client/post/Write/Write.js +++ b/src/client/post/Write/Write.js @@ -7,10 +7,8 @@ import _ from 'lodash'; import 'url-search-params-polyfill'; import { injectIntl } from 'react-intl'; import uuidv4 from 'uuid/v4'; -import { getHtml } from '../../components/Story/Body'; import improve from '../../helpers/improve'; -import { extractLinks } from '../../helpers/parser'; -import { getContentImages } from '../../helpers/postHelpers'; +import { createPostMetadata } from '../../helpers/postHelpers'; import { rewardsValues } from '../../../common/constants/rewards'; import LastDraftsContainer from './LastDraftsContainer'; import DeleteDraftModal from './DeleteDraftModal'; @@ -28,8 +26,6 @@ import { createPost, saveDraft, newPost } from './editorActions'; import Editor from '../../components/Editor/Editor'; import Affix from '../../components/Utils/Affix'; -const version = require('../../../../package.json').version; - @injectIntl @withRouter @connect( @@ -190,25 +186,6 @@ class Write extends React.Component { data.parentAuthor = ''; data.author = this.props.user.name || ''; - const tags = form.topics; - const users = []; - const userRegex = /@([a-zA-Z.0-9-]+)/g; - let matches; - - const postBody = data.body; - - // eslint-disable-next-line - while ((matches = userRegex.exec(postBody))) { - if (users.indexOf(matches[1]) === -1) { - users.push(matches[1]); - } - } - - const parsedBody = getHtml(postBody, {}, 'text'); - - const images = getContentImages(parsedBody, true); - const links = extractLinks(parsedBody); - if (data.title && !this.permlink) { data.permlink = _.kebabCase(data.title); } else { @@ -217,36 +194,11 @@ class Write extends React.Component { if (this.state.isUpdating) data.isUpdating = this.state.isUpdating; - let metaData = { - community: 'busy', - app: `busy/${version}`, - format: 'markdown', - }; - - // Merging jsonMetadata makes sure that users don't lose any metadata when they edit post using - // Busy (like video data from DTube) - if (this.props.draftPosts[this.draftId] && this.props.draftPosts[this.draftId].jsonMetadata) { - metaData = { - ...this.props.draftPosts[this.draftId].jsonMetadata, - ...metaData, - }; - } - - if (tags.length) { - metaData.tags = tags; - } - if (users.length) { - metaData.users = users; - } - if (links.length) { - metaData.links = links.slice(0, 10); - } - if (images.length) { - metaData.image = images; - } + const oldMetadata = + this.props.draftPosts[this.draftId] && this.props.draftPosts[this.draftId].jsonMetadata; - data.parentPermlink = tags.length ? tags[0] : 'general'; - data.jsonMetadata = metaData; + data.parentPermlink = form.topics.length ? form.topics[0] : 'general'; + data.jsonMetadata = createPostMetadata(data.body, form.topics, oldMetadata); if (this.originalBody) { data.originalBody = this.originalBody; diff --git a/src/client/styles/common.less b/src/client/styles/common.less index 3445c5229c..e71116fcc6 100644 --- a/src/client/styles/common.less +++ b/src/client/styles/common.less @@ -82,6 +82,10 @@ body.white-bg .ant-tooltip { z-index: @ant-modal-tooltip-z-index !important; } +body.post-modal .primary-modal { + display: none; +} + .ant-tooltip { z-index: 1060; diff --git a/src/client/wallet/PowerUpOrDown.js b/src/client/wallet/PowerUpOrDown.js index a744b498d6..31ac16fb6b 100644 --- a/src/client/wallet/PowerUpOrDown.js +++ b/src/client/wallet/PowerUpOrDown.js @@ -59,7 +59,11 @@ export default class PowerUpOrDown extends React.Component { const { user, down, totalVestingShares, totalVestingFundSteem } = this.props; return down - ? formatter.vestToSteem(user.vesting_shares, totalVestingShares, totalVestingFundSteem) + ? formatter.vestToSteem( + parseFloat(user.vesting_shares) - parseFloat(user.delegated_vesting_shares), + totalVestingShares, + totalVestingFundSteem, + ) : parseFloat(user.balance); }; diff --git a/src/client/wallet/UserWalletTransactions.less b/src/client/wallet/UserWalletTransactions.less index 9b8a753209..0d306b7bc1 100644 --- a/src/client/wallet/UserWalletTransactions.less +++ b/src/client/wallet/UserWalletTransactions.less @@ -13,16 +13,23 @@ } &__loader { - border-bottom-left-radius: @border-radius-base; - border-bottom-right-radius: @border-radius-base; border-bottom: @base-border; - border-left: @base-border; - border-right: @base-border; padding: 20px; &:nth-of-type(2) { border-top: @base-border; - border-radius: @border-radius-base; + } + + @media @small { + border-right: @base-border; + border-left: @base-border; + border-bottom-left-radius: @border-radius-base; + border-bottom-right-radius: @border-radius-base; + + &:nth-of-type(2) { + border-top-left-radius: @border-radius-base; + border-top-right-radius: @border-radius-base; + } } } @@ -30,19 +37,21 @@ color: @grey-nightRider; display: flex; padding: 10px 15px; - border-right: @base-border; - border-left: @base-border; border-bottom: @base-border; &:nth-of-type(2) { border-top: @base-border; - border-top-left-radius: @border-radius-base; - border-top-right-radius: @border-radius-base; } - &:last-of-type { - border-bottom-left-radius: @border-radius-base; - border-bottom-right-radius: @border-radius-base; + @media @small { + border-right: @base-border; + border-left: @base-border; + + &:nth-of-type(2), + &:last-of-type { + border-top-left-radius: @border-radius-base; + border-top-right-radius: @border-radius-base; + } } } diff --git a/src/common/constants/accountHistory.js b/src/common/constants/accountHistory.js index 1ed2274dd1..f41089efed 100644 --- a/src/common/constants/accountHistory.js +++ b/src/common/constants/accountHistory.js @@ -12,6 +12,9 @@ export const AUTHOR_REWARD = 'author_reward'; export const ACCOUNT_WITNESS_VOTE = 'account_witness_vote'; export const FILL_VESTING_WITHDRAW = 'fill_vesting_withdraw'; +// Supported Custom JSON Type IDs +export const ID_FOLLOW = 'follow'; + // Wallet Action Types export const TRANSFER = 'transfer'; export const TRANSFER_TO_VESTING = 'transfer_to_vesting'; @@ -55,3 +58,5 @@ export const PARSED_PROPERTIES = [ ACCOUNT_WITNESS_VOTE, FILL_VESTING_WITHDRAW, ]; + +export const PARSED_CUSTOM_JSON_IDS = [ID_FOLLOW]; diff --git a/src/common/constants/errors.js b/src/common/constants/errors.js index 989e02cbe5..fc1dc5cbf1 100644 --- a/src/common/constants/errors.js +++ b/src/common/constants/errors.js @@ -27,4 +27,8 @@ export default { fingerprint: 'abs_rshares > STEEM_VOTE_DUST_THRESHOLD', message: 'Your voting power is too small, please accumulate more voting power or steem power.', }, + UPVOTE_LOCKOUT: { + fingerprint: 'Cannot increase payout within last twelve hours before payout.', + message: "You can't increase payout within last twelve hours before payout.", + }, }; diff --git a/yarn.lock b/yarn.lock index 019600bb1d..c86ba68077 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2107,9 +2107,9 @@ builtin-status-codes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" -"busyjs@https://github.com/busyorg/busyjs#dev": - version "0.0.3" - resolved "https://github.com/busyorg/busyjs#6ee62e6931b08dfc175cfb538f9f0f20c91f5f8e" +busyjs@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/busyjs/-/busyjs-1.0.2.tgz#d52d8310fce923ad242093566ab41b6df53bbf62" dependencies: ws "^4.0.0" @@ -9583,9 +9583,9 @@ stealthy-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" -"steemscript@https://github.com/busyorg/steemscript.git": - version "0.0.1" - resolved "https://github.com/busyorg/steemscript.git#0a2ffcba66e9ebb093a0e511e78ffa22b7b9023d" +steemscript@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/steemscript/-/steemscript-1.0.1.tgz#3739bc91420825f5c505049d33d888842eaf0bbd" store@^1.3.20: version "1.3.20"