diff --git a/src/components/basicUIElements/view/userListItem/userListItem.js b/src/components/basicUIElements/view/userListItem/userListItem.js index e46147f03..8d4b2eb0e 100644 --- a/src/components/basicUIElements/view/userListItem/userListItem.js +++ b/src/components/basicUIElements/view/userListItem/userListItem.js @@ -17,6 +17,7 @@ const UserListItem = ({ handleOnPress, handleOnLongPress, isClickable, + text, }) => ( handleOnLongPress && handleOnLongPress()} @@ -27,7 +28,7 @@ const UserListItem = ({ {itemIndex && {itemIndex}} - {username} + {text || username} {description && {description}} {isHasRightItem && ( diff --git a/src/components/basicUIElements/view/walletLineItem/walletLineItemView.js b/src/components/basicUIElements/view/walletLineItem/walletLineItemView.js index ee6d41d76..5a4765b61 100644 --- a/src/components/basicUIElements/view/walletLineItem/walletLineItemView.js +++ b/src/components/basicUIElements/view/walletLineItem/walletLineItemView.js @@ -21,9 +21,10 @@ const WalletLineItem = ({ text, textColor, index, + style, }) => ( - - + + {iconName && ( { + if (this.alertTimer) { + clearTimeout(this.alertTimer); + this.alertTimer = 0; + } + + if (this.shareTimer) { + clearTimeout(this.shareTimer); + this.shareTimer = 0; + } + + if (this.actionSheetTimer) { + clearTimeout(this.actionSheetTimer); + this.actionSheetTimer = 0; + } + }; // Component Functions - _handleOnDropdownSelect = (index) => { - const { content } = this.props; + _handleOnDropdownSelect = async (index) => { + const { content, intl } = this.props; switch (index) { case '0': - writeToClipboard(getPostUrl(content.url)); + await writeToClipboard(getPostUrl(content.url)); + this.alertTimer = setTimeout(() => { + Alert.alert(intl.formatMessage({ + id: 'alert.copied', + })); + this.alertTimer = 0; + }, 300); break; case '1': - setTimeout(() => { + this.actionSheetTimer = setTimeout(() => { this.ActionSheet.show(); + this.actionSheetTimer = 0; }, 100); break; @@ -54,11 +76,16 @@ class PostDropdownContainer extends PureComponent { this._replyNavigation(); break; case '3': - setTimeout(() => { + this.shareTimer = setTimeout(() => { this._share(); + this.shareTimer = 0; }, 500); break; + case '4': + this._addToBookmarks(); + break; + default: break; } @@ -73,6 +100,17 @@ class PostDropdownContainer extends PureComponent { }); }; + _addToBookmarks = () => { + const { currentAccount, content, intl } = this.props; + addBookmark(currentAccount.name, content.author, content.permlink) + .then(() => { + Alert.alert(intl.formatMessage({ id: 'bookmarks.added' })); + }) + .catch(() => { + Alert.alert(intl.formatMessage({ id: 'alert.fail' })); + }); + } + _reblog = () => { const { currentAccount, content, isLoggedIn, pinCode, intl, diff --git a/src/components/postElements/body/view/postBodyView.js b/src/components/postElements/body/view/postBodyView.js index 8375f61f0..d6293e357 100644 --- a/src/components/postElements/body/view/postBodyView.js +++ b/src/components/postElements/body/view/postBodyView.js @@ -112,7 +112,7 @@ class PostBody extends PureComponent { } if (_className === 'text-justify') { - node.attribs.style = 'text-align: justify; text-justify: inter-word; letter-spacing: 1.2px;'; + node.attribs.style = 'text-align: justify; text-justify: inter-word; letter-spacing: 0px;'; } if (_className === 'phishy') { diff --git a/src/components/tabBar/view/tabBarStyles.js b/src/components/tabBar/view/tabBarStyles.js index f507d32fd..3b24f79ea 100644 --- a/src/components/tabBar/view/tabBarStyles.js +++ b/src/components/tabBar/view/tabBarStyles.js @@ -17,6 +17,5 @@ export default EStyleSheet.create({ text: { fontSize: 16, fontFamily: '$primaryFont', - marginBottom: 12, }, }); diff --git a/src/components/transaction/container/transactionContainer.js b/src/components/transaction/container/transactionContainer.js index 85058dc1f..a6d1899b6 100644 --- a/src/components/transaction/container/transactionContainer.js +++ b/src/components/transaction/container/transactionContainer.js @@ -1,5 +1,4 @@ import React, { PureComponent } from 'react'; -import { connect } from 'react-redux'; // Component import TransactionView from '../view/transactionView'; @@ -22,14 +21,10 @@ class TransactionContainer extends PureComponent { // Component Functions render() { - const { walletData, globalProps } = this.props; + const { walletData } = this.props; - return ; + return ; } } -const mapStateToProps = state => ({ - globalProps: state.account.globalProps, -}); - -export default connect(mapStateToProps)(TransactionContainer); +export default TransactionContainer; diff --git a/src/components/transaction/view/transactionStyles.js b/src/components/transaction/view/transactionStyles.js new file mode 100644 index 000000000..7a3c5b10e --- /dev/null +++ b/src/components/transaction/view/transactionStyles.js @@ -0,0 +1,7 @@ +import EStyleSheet from 'react-native-extended-stylesheet'; + +export default EStyleSheet.create({ + container: { + marginTop: 8, + }, +}); diff --git a/src/components/transaction/view/transactionView.js b/src/components/transaction/view/transactionView.js index b30bc5be8..1c306bf6c 100644 --- a/src/components/transaction/view/transactionView.js +++ b/src/components/transaction/view/transactionView.js @@ -1,5 +1,6 @@ -import React, { PureComponent, Fragment } from 'react'; +import React, { PureComponent } from 'react'; import { injectIntl } from 'react-intl'; +import { View } from 'react-native'; // Utilities import { groomingTransactionData } from '../../../utils/wallet'; @@ -12,6 +13,8 @@ import { getTimeFromNow } from '../../../utils/time'; import { WalletLineItem, Card } from '../../basicUIElements'; import { CollapsibleCard } from '../../collapsibleCard'; +import styles from './transactionStyles'; + class TransactionView extends PureComponent { /* Props * ------------------------------------------------ @@ -38,7 +41,7 @@ class TransactionView extends PureComponent { } = this.props; return ( - + {/* this feature not implemented yet */} {/* this._handleOnDropdownSelect()} rightIconName="ios-lock" iconSize={16} - if (index % 2 === 0) { /> */} {transactions && transactions.map((item, index) => { - const transactionData = groomingTransactionData( - item, - walletData, - formatNumber, - globalProps, - ); + const transactionData = groomingTransactionData(item, walletData, formatNumber); return ( - + ); } } diff --git a/src/components/wallet/container/walletContainer.js b/src/components/wallet/container/walletContainer.js index 953a084cd..7c47c2b0e 100644 --- a/src/components/wallet/container/walletContainer.js +++ b/src/components/wallet/container/walletContainer.js @@ -47,9 +47,11 @@ class WalletContainer extends Component { // Components functions _getWalletData = async (selectedUser) => { - const walletData = await groomingWalletData(selectedUser); + const { setEstimatedWalletValue, globalProps } = this.props; + const walletData = await groomingWalletData(selectedUser, globalProps); this.setState({ walletData }); + setEstimatedWalletValue(walletData.estimatedValue); }; _claimRewardBalance = async () => { @@ -131,6 +133,7 @@ const mapStateToProps = state => ({ currentAccount: state.account.currentAccount, pinCode: state.account.pin, isDarkTheme: state.application.isDarkTheme, + globalProps: state.account.globalProps, }); export default injectIntl(connect(mapStateToProps)(WalletContainer)); diff --git a/src/components/wallet/view/walletStyles.js b/src/components/wallet/view/walletStyles.js index ab786715f..b909858b3 100644 --- a/src/components/wallet/view/walletStyles.js +++ b/src/components/wallet/view/walletStyles.js @@ -35,4 +35,7 @@ export default EStyleSheet.create({ width: 24, height: 24, }, + scrollView: { + backgroundColor: '$primaryLightBackground', + }, }); diff --git a/src/components/walletDetails/view/walletDetailsStyles.js b/src/components/walletDetails/view/walletDetailsStyles.js index ec53ec3cb..c4a4dbc24 100644 --- a/src/components/walletDetails/view/walletDetailsStyles.js +++ b/src/components/walletDetails/view/walletDetailsStyles.js @@ -7,4 +7,8 @@ export default EStyleSheet.create({ blackText: { color: '$primaryBlack', }, + walletLineDetail: { + marginBottom: 15, + marginTop: 0, + }, }); diff --git a/src/components/walletDetails/view/walletDetailsView.js b/src/components/walletDetails/view/walletDetailsView.js index f291c8be1..a1bfbdc54 100644 --- a/src/components/walletDetails/view/walletDetailsView.js +++ b/src/components/walletDetails/view/walletDetailsView.js @@ -39,7 +39,7 @@ class WalletDetailsView extends PureComponent { rightText={`${Math.round(walletData.balance * 1000) / 1000} STEEM`} isBoldText /> - + )} {walletData.vestingSharesReceived > 0 && ( @@ -64,6 +65,7 @@ class WalletDetailsView extends PureComponent { rightText={`+ ${Math.round( vestsToSp(walletData.vestingSharesReceived, walletData.steemPerMVests) * 1000, ) / 1000} SP`} + style={styles.walletLineDetail} /> )} {(walletData.vestingSharesDelegated > 0 || walletData.vestingSharesReceived > 0) && ( @@ -72,6 +74,7 @@ class WalletDetailsView extends PureComponent { vestsToSp(walletData.vestingSharesTotal, walletData.steemPerMVests) * 1000, ) / 1000} SP`} rightTextColor="#357ce6" + style={styles.walletLineDetail} /> )} @@ -85,7 +88,7 @@ class WalletDetailsView extends PureComponent { rightText={`$${Math.round(walletData.sbdBalance * 1000) / 1000}`} isBoldText /> - + - + {walletData.showPowerDown && ( api.get(`/bookmarks/${username}`).then(r * @params id * @params current username */ -export const removeBookmark = (id, username) => api.delete(`/bookmarks/${username}/${id}`); +export const removeBookmark = (username, id) => api.delete(`/bookmarks/${username}/${id}`); /** * @params current username diff --git a/src/screens/application/container/applicationContainer.js b/src/screens/application/container/applicationContainer.js index a4ba848fc..03c6af162 100644 --- a/src/screens/application/container/applicationContainer.js +++ b/src/screens/application/container/applicationContainer.js @@ -185,12 +185,12 @@ class ApplicationContainer extends Component { getSettings().then((response) => { if (response) { - if (response.isDarkTheme) dispatch(isDarkTheme(response.isDarkTheme)); - if (response.language) dispatch(setLanguage(response.language)); - if (response.currency) dispatch(setCurrency(response.currency)); - if (response.notification) dispatch(isNotificationOpen(response.notification)); - if (response.server) dispatch(setApi(response.server)); - if (response.upvotePercent) dispatch(setUpvotePercent(Number(response.upvotePercent))); + if (response.isDarkTheme !== '') dispatch(isDarkTheme(response.isDarkTheme)); + if (response.language !== '') dispatch(setLanguage(response.language)); + if (response.currency !== '') dispatch(setCurrency(response.currency)); + if (response.notification !== '') dispatch(isNotificationOpen(response.notification)); + if (response.server !== '') dispatch(setApi(response.server)); + if (response.upvotePercent !== '') dispatch(setUpvotePercent(Number(response.upvotePercent))); this.setState({ isReady: true }); } diff --git a/src/screens/bookmarks/container/bookmarksContainer.js b/src/screens/bookmarks/container/bookmarksContainer.js index c46736346..cd0dffcb1 100644 --- a/src/screens/bookmarks/container/bookmarksContainer.js +++ b/src/screens/bookmarks/container/bookmarksContainer.js @@ -4,7 +4,7 @@ import { Alert } from 'react-native'; import { injectIntl } from 'react-intl'; // Services and Actions -import { getFavorites, removeFavorite } from '../../../providers/esteem/esteem'; +import { getFavorites, removeFavorite, getBookmarks, removeBookmark } from '../../../providers/esteem/esteem'; // Constants import ROUTES from '../../../constants/routeNames'; @@ -39,6 +39,7 @@ class DraftsContainer extends Component { _fetchData = () => { this._getFavorites(); + this._getBookmarks(); }; _getFavorites = () => { @@ -55,6 +56,20 @@ class DraftsContainer extends Component { }); }; + _getBookmarks = () => { + const { currentAccount, intl } = this.props; + this.setState({ isLoading: true }); + + getBookmarks(currentAccount.name) + .then((data) => { + this.setState({ bookmarks: this._sortData(data), isLoading: false }); + }) + .catch(() => { + Alert.alert(intl.formatMessage({ id: 'bookmarks.load_error' })); + this.setState({ isLoading: false }); + }); + }; + _removeFavorite = (selectedUsername) => { const { currentAccount, intl } = this.props; @@ -70,6 +85,21 @@ class DraftsContainer extends Component { }); }; + _removeBoomark = (id) => { + const { currentAccount, intl } = this.props; + + removeBookmark(currentAccount.name, id) + .then(() => { + const { bookmarks } = this.state; + const newBookmarks = [...bookmarks].filter(bookmark => bookmark._id !== id); + + this.setState({ bookmarks: this._sortData(newBookmarks) }); + }) + .catch(() => { + Alert.alert(intl.formatMessage({ id: 'alert.fail' })); + }); + }; + _handleOnFavoritePress = (username) => { const { navigation } = this.props; @@ -82,6 +112,18 @@ class DraftsContainer extends Component { }); }; + _handleOnBookarkPress = (permlink, author) => { + const { navigation } = this.props; + + navigation.navigate({ + routeName: ROUTES.SCREENS.POST, + params: { + permlink, + author, + }, + }); + }; + _sortData = data => data.sort((a, b) => { const dateA = new Date(a.created).getTime(); const dateB = new Date(b.created).getTime(); @@ -100,7 +142,9 @@ class DraftsContainer extends Component { favorites={favorites} bookmarks={bookmarks} removeFavorite={this._removeFavorite} + removeBookmark={this._removeBoomark} handleOnFavoritePress={this._handleOnFavoritePress} + handleOnBookarkPress={this._handleOnBookarkPress} /> ); } diff --git a/src/screens/bookmarks/screen/bookmarksScreen.js b/src/screens/bookmarks/screen/bookmarksScreen.js index de4382070..1829c8d95 100644 --- a/src/screens/bookmarks/screen/bookmarksScreen.js +++ b/src/screens/bookmarks/screen/bookmarksScreen.js @@ -26,25 +26,30 @@ class BookmarksScreen extends Component { constructor(props) { super(props); this.state = { - selectedUsername: null, + selectedItemId: null, + activeTab: 0, }; } // Component Life Cycles // Component Functions - _renderItem = (item, index) => { - const { handleOnFavoritePress } = this.props; + _renderItem = (item, index, itemType) => { + const { handleOnFavoritePress, handleOnBookarkPress } = this.props; + const isFavorites = itemType === 'favorites'; + const text = isFavorites ? item.account : `${item.author}/${item.permlink}`; return ( this._handleLongPress(item.account)} - handleOnPress={() => handleOnFavoritePress(item.account)} + handleOnLongPress={() => this._handleLongPress(isFavorites ? item.account : item._id)} + handleOnPress={() => (isFavorites + ? handleOnFavoritePress(item.account) + : handleOnBookarkPress(item.permlink, item.author)) + } index={index} isClickable - username={item.account} - rightText="bok" - subRightText="bok" + text={text} + username={item.author} /> ); }; @@ -71,7 +76,7 @@ class BookmarksScreen extends Component { data={data} keyExtractor={item => item._id} removeClippedSubviews={false} - renderItem={({ item, index }) => this._renderItem(item, index)} + renderItem={({ item, index }) => this._renderItem(item, index, type)} /> ) )} @@ -79,17 +84,17 @@ class BookmarksScreen extends Component { ); }; - _handleLongPress = (selectedUsername) => { - this.setState({ selectedUsername }, () => { + _handleLongPress = (selectedItemId) => { + this.setState({ selectedItemId }, () => { this.ActionSheet.show(); }); }; render() { const { - favorites, bookmarks, intl, removeFavorite, + favorites, bookmarks, intl, removeFavorite, removeBookmark, } = this.props; - const { selectedUsername } = this.state; + const { selectedItemId, activeTab } = this.state; return ( @@ -100,6 +105,7 @@ class BookmarksScreen extends Component { /> this.setState({ activeTab: event.i })} style={globalStyles.tabView} renderTabBar={() => ( { - if (index === 0) removeFavorite(selectedUsername); + if (index === 0) { + activeTab === 0 ? removeBookmark(selectedItemId) : removeFavorite(selectedItemId); + } }} /> diff --git a/src/screens/profile/screen/profileScreen.js b/src/screens/profile/screen/profileScreen.js index f14183b07..a4d3c0e17 100644 --- a/src/screens/profile/screen/profileScreen.js +++ b/src/screens/profile/screen/profileScreen.js @@ -32,6 +32,8 @@ class ProfileScreen extends PureComponent { this.state = { isSummaryOpen: true, collapsibleMoreHeight: 0, + estimatedWalletValue: 0, + oldEstimatedWalletValue: 0, }; } @@ -51,6 +53,10 @@ class ProfileScreen extends PureComponent { this.setState({ collapsibleMoreHeight: height }); }; + _setEstimatedWalletValue = (value) => { + if (value) this.setState({ estimatedWalletValue: value }); + } + render() { const { about, @@ -76,7 +82,9 @@ class ProfileScreen extends PureComponent { getReplies, } = this.props; - const { isSummaryOpen, collapsibleMoreHeight } = this.state; + const { + isSummaryOpen, collapsibleMoreHeight, estimatedWalletValue, oldEstimatedWalletValue, + } = this.state; let _about; let coverImage; @@ -154,10 +162,18 @@ class ProfileScreen extends PureComponent { )} ( )} + onChangeTab={({ i }) => { + if (i !== 2) { + this.setState({ + estimatedWalletValue: 0, + oldEstimatedWalletValue: estimatedWalletValue, + }); + } else this.setState({ estimatedWalletValue: oldEstimatedWalletValue }); + }} > - {selectedUser ? : } + {selectedUser + ? ( + + ) + : } diff --git a/src/screens/profile/screen/profileStyles.js b/src/screens/profile/screen/profileStyles.js index bab7186bf..d268553ba 100644 --- a/src/screens/profile/screen/profileStyles.js +++ b/src/screens/profile/screen/profileStyles.js @@ -3,7 +3,7 @@ import EStyleSheet from 'react-native-extended-stylesheet'; export default EStyleSheet.create({ container: { flex: 1, - backgroundColor: '$primaryLightBackground', + backgroundColor: '$primaryGrayBackground', }, content: { backgroundColor: '$primaryGrayBackground', @@ -40,6 +40,10 @@ export default EStyleSheet.create({ height: 45, backgroundColor: '$primaryBackgroundColor', borderBottomColor: '#f1f1f1', + marginTop: 8, + }, + tabView: { + backgroundColor: '$primaryGrayBackground', }, tabbarItem: { flex: 1, diff --git a/src/screens/settings/container/settingsContainer.js b/src/screens/settings/container/settingsContainer.js index 5e6a97188..cb2c18124 100644 --- a/src/screens/settings/container/settingsContainer.js +++ b/src/screens/settings/container/settingsContainer.js @@ -134,7 +134,7 @@ class SettingsContainer extends Component { }; _setPushToken = async () => { - const { notificationSettings, isLoggedIn, username } = this.props; + const { isNotificationSettingsOpen, isLoggedIn, username } = this.props; if (isLoggedIn) { const token = await AppCenter.getInstallId(); @@ -144,7 +144,7 @@ class SettingsContainer extends Component { username, token, system: Platform.OS, - allows_notify: notificationSettings, + allows_notify: isNotificationSettingsOpen, }; setPushToken(data); } @@ -169,9 +169,8 @@ const mapStateToProps = state => ({ selectedLanguage: state.application.language, selectedApi: state.application.api, selectedCurrency: state.application.currency, - isNotificationOpen: state.application.isNotificationOpen, isDarkTheme: state.application.isDarkTheme, - notificationSettings: state.application.isNotificationOpen, + isNotificationSettingsOpen: state.application.isNotificationOpen, isLoggedIn: state.application.isLoggedIn, username: state.account.currentAccount && state.account.currentAccount.name, }); diff --git a/src/screens/settings/screen/settingsScreen.js b/src/screens/settings/screen/settingsScreen.js index fe1dd1f7d..2fd6a50e6 100644 --- a/src/screens/settings/screen/settingsScreen.js +++ b/src/screens/settings/screen/settingsScreen.js @@ -36,7 +36,7 @@ class SettingsScreen extends PureComponent { selectedLanguage, selectedApi, selectedCurrency, - isNotificationOpen, + isNotificationSettingsOpen, isDarkTheme, serverList, intl, @@ -98,7 +98,7 @@ class SettingsScreen extends PureComponent { })} type="toggle" actionType="notification" - isOn={isNotificationOpen} + isOn={isNotificationSettingsOpen} handleOnChange={handleOnChange} /> {!!isLoggedIn && ( diff --git a/src/screens/voters/screen/votersScreen.js b/src/screens/voters/screen/votersScreen.js index 06258d568..745d494e6 100644 --- a/src/screens/voters/screen/votersScreen.js +++ b/src/screens/voters/screen/votersScreen.js @@ -24,6 +24,7 @@ class VotersScreen extends PureComponent { this.state = { data: props.votes, filterResult: null, + isRenderRequire: false, }; } @@ -48,7 +49,7 @@ class VotersScreen extends PureComponent { break; } - this.setState({ filterResult: _data }); + this.setState({ filterResult: _data, isRenderRequire: true }, () => this.setState({ isRenderRequire: false })); }; _handleRightIconPress = () => {}; @@ -67,7 +68,7 @@ class VotersScreen extends PureComponent { }; render() { - const { data, filterResult } = this.state; + const { data, filterResult, isRenderRequire } = this.state; const { intl } = this.props; const headerTitle = intl.formatMessage({ id: 'voters.voters_info', @@ -86,7 +87,7 @@ class VotersScreen extends PureComponent { defaultText="REWARDS" onDropdownSelect={this._handleOnDropdownSelect} /> - + {!isRenderRequire && } ); } diff --git a/src/utils/clipboard.js b/src/utils/clipboard.js index 076b411f6..2a7781298 100644 --- a/src/utils/clipboard.js +++ b/src/utils/clipboard.js @@ -7,6 +7,7 @@ const readFromClipboard = async () => { const writeToClipboard = async (text) => { await Clipboard.setString(text); + return true; }; export { writeToClipboard, readFromClipboard }; diff --git a/src/utils/wallet.js b/src/utils/wallet.js index 5916c6bda..51e5fba14 100644 --- a/src/utils/wallet.js +++ b/src/utils/wallet.js @@ -1,7 +1,8 @@ import parseDate from './parseDate'; import parseToken from './parseToken'; import { vestsToSp } from './conversions'; -import { getDynamicGlobalProperties, getState } from '../providers/steem/dsteem'; +import { getState, getFeedHistory } from '../providers/steem/dsteem'; + export const groomingTransactionData = (transaction, walletData, formatNumber) => { if (!transaction || !walletData) { @@ -101,14 +102,13 @@ export const groomingTransactionData = (transaction, walletData, formatNumber) = return result; }; -export const groomingWalletData = async (user) => { +export const groomingWalletData = async (user, globalProps) => { const walletData = {}; if (!user) { return walletData; } - const global = await getDynamicGlobalProperties(); const state = await getState(`/@${user.name}/transfers`); const { accounts } = state; @@ -129,15 +129,17 @@ export const groomingWalletData = async (user) => { walletData.savingBalance = parseToken(user.savings_balance); walletData.savingBalanceSbd = parseToken(user.savings_sbd_balance); - // const feedHistory = await getFeedHistory(); - // const base = parseToken(feedHistory.current_median_history.base); - // const quote = parseToken(feedHistory.current_median_history.quote); + const feedHistory = await getFeedHistory(); + const base = parseToken(feedHistory.current_median_history.base); + const quote = parseToken(feedHistory.current_median_history.quote); - walletData.steemPerMVests = (parseToken(global.total_vesting_fund_steem) / parseToken(global.total_vesting_shares)) * 1e6; + walletData.steemPerMVests = globalProps.steemPerMVests; - // walletData.estimatedValue = vestsToSp(walletData.vestingShares, walletData.steemPerMVests) * (base / quote) - // + walletData.balance * (base / quote) - // + walletData.sbdBalance; + walletData.estimatedValue = ( + vestsToSp(walletData.vestingShares, walletData.steemPerMVests) * (base / quote) + ) + + (walletData.balance * (base / quote)) + + walletData.sbdBalance; walletData.showPowerDown = user.next_vesting_withdrawal !== '1969-12-31T23:59:59'; const timeDiff = Math.abs(parseDate(user.next_vesting_withdrawal) - new Date());