Merge branch 'master' into bugfix/lint-fix

This commit is contained in:
uğur erdal 2019-02-22 14:01:32 +03:00 committed by GitHub
commit 25260a321c
20 changed files with 314 additions and 117 deletions

View File

@ -11,8 +11,6 @@ import { getComments } from '../../../providers/steem/dsteem';
// Constants
import { default as ROUTES } from '../../../constants/routeNames';
// Utilities
// Component
import { CommentsView } from '..';
@ -36,14 +34,89 @@ class CommentsContainer extends Component {
}
componentWillReceiveProps(nextProps) {
const { commentCount } = this.props;
const { commentCount, selectedFilter } = this.props;
if (nextProps.commentCount > commentCount) {
this._getComments();
}
if (selectedFilter !== nextProps.selectedFilter && nextProps.selectedFilter) {
const shortedComments = this._shortComments(nextProps.selectedFilter);
this.setState({ comments: shortedComments });
}
}
// Component Functions
_shortComments = (sortOrder) => {
const { comments: parent } = this.state;
const allPayout = c => parseFloat(c.pending_payout_value.split(' ')[0])
+ parseFloat(c.total_payout_value.split(' ')[0])
+ parseFloat(c.curator_payout_value.split(' ')[0]);
const absNegative = a => a.net_rshares < 0;
const sortOrders = {
TRENDING: (a, b) => {
if (absNegative(a)) {
return 1;
}
if (absNegative(b)) {
return -1;
}
const apayout = allPayout(a);
const bpayout = allPayout(b);
if (apayout !== bpayout) {
return bpayout - apayout;
}
return 0;
},
REPUTATION: (a, b) => {
const keyA = a.author_reputation;
const keyB = b.author_reputation;
if (keyA > keyB) return -1;
if (keyA < keyB) return 1;
return 0;
},
VOTES: (a, b) => {
const keyA = a.net_votes;
const keyB = b.net_votes;
if (keyA > keyB) return -1;
if (keyA < keyB) return 1;
return 0;
},
AGE: (a, b) => {
if (absNegative(a)) {
return 1;
}
if (absNegative(b)) {
return -1;
}
const keyA = Date.parse(a.created);
const keyB = Date.parse(b.created);
if (keyA > keyB) return -1;
if (keyA < keyB) return 1;
return 0;
},
};
parent.sort(sortOrders[sortOrder]);
return parent;
};
_getComments = () => {
const { author, permlink } = this.props;
@ -87,18 +160,19 @@ class CommentsContainer extends Component {
isLoggedIn,
commentCount,
author,
permlink,
currentAccount,
commentNumber,
comments,
fetchPost,
isShowMoreButton,
selectedFilter,
selectedPermlink: _selectedPermlink,
} = this.props;
return (
<CommentsView
key={permlink}
key={selectedFilter}
selectedFilter={selectedFilter}
selectedPermlink={_selectedPermlink || selectedPermlink}
author={author}
isShowMoreButton={isShowMoreButton}
@ -109,7 +183,6 @@ class CommentsContainer extends Component {
handleOnEditPress={this._handleOnEditPress}
handleOnReplyPress={this._handleOnReplyPress}
isLoggedIn={isLoggedIn}
permlink={permlink}
fetchPost={fetchPost}
{...this.props}
/>

View File

@ -1,5 +1,5 @@
import React, { PureComponent } from 'react';
import { View, FlatList } from 'react-native';
import { FlatList } from 'react-native';
// Components
import { Comment } from '../../comment';
@ -21,49 +21,46 @@ class CommentsView extends PureComponent {
// Component Life Cycles
// Component Functions
_handleOnDropdownSelect = () => {};
_keyExtractor = item => item.permlink;
render() {
const {
avatarSize,
commentCount,
commentNumber,
comments,
currentAccountUsername,
fetchPost,
handleOnEditPress,
handleOnReplyPress,
handleOnUserPress,
isLoggedIn,
marginLeft,
isShowSubComments,
fetchPost,
commentCount,
marginLeft,
} = this.props;
return (
<FlatList
data={comments}
keyExtractor={this._keyExtractor}
renderItem={({ item, index }) => (
<View key={index}>
<Comment
isShowMoreButton={commentNumber === 1 && item.children > 0}
comment={item}
marginLeft={marginLeft}
commentNumber={commentNumber}
fetchPost={fetchPost}
commentCount={commentCount || item.children}
isShowSubComments={isShowSubComments}
avatarSize={avatarSize}
currentAccountUsername={currentAccountUsername}
handleOnReplyPress={handleOnReplyPress}
handleOnEditPress={handleOnEditPress}
handleOnUserPress={handleOnUserPress}
isLoggedIn={isLoggedIn}
showComentsToggle={this._showComentsToggle}
/>
</View>
renderItem={({ item }) => (
<Comment
avatarSize={avatarSize}
comment={item}
commentCount={commentCount || item.children}
commentNumber={commentNumber}
currentAccountUsername={currentAccountUsername}
fetchPost={fetchPost}
handleOnEditPress={handleOnEditPress}
handleOnReplyPress={handleOnReplyPress}
handleOnUserPress={handleOnUserPress}
isLoggedIn={isLoggedIn}
isShowMoreButton={commentNumber === 1 && item.children > 0}
isShowSubComments={isShowSubComments}
key={item.permlink}
marginLeft={marginLeft}
/>
)}
/>
);

View File

@ -3,6 +3,5 @@ import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
commentWrapper: {
padding: 16,
paddingBottom: 50,
},
});

View File

@ -1,9 +1,11 @@
import React, { PureComponent, Fragment } from 'react';
import { View } from 'react-native';
import { injectIntl } from 'react-intl';
// Components
import { FilterBar } from '../../filterBar';
import { Comments } from '../../comments';
import COMMENT_FILTER from '../../../constants/options/comment';
// Styles
import styles from './commentDisplayStyles';
@ -16,31 +18,39 @@ class CommentsDisplayView extends PureComponent {
constructor(props) {
super(props);
this.state = {};
this.state = {
selectedFilter: null,
};
}
// Component Life Cycles
// Component Functions
_handleOnDropdownSelect = () => {};
_handleOnDropdownSelect = (index, option) => {
this.setState({ selectedFilter: option });
};
render() {
const {
author, permlink, commentCount, fetchPost,
author, commentCount, fetchPost, intl, permlink,
} = this.props;
// TODO: implement comments filtering
const { selectedFilter } = this.state;
return (
<Fragment>
{commentCount > 0 && (
<Fragment>
<FilterBar
dropdownIconName="arrow-drop-down"
options={['TRENDING']}
defaultText="TRENDING"
options={COMMENT_FILTER.map(item => intl.formatMessage({ id: `comment_filter.${item}` }).toUpperCase())}
defaultText={intl
.formatMessage({ id: `comment_filter.${COMMENT_FILTER[0]}` })
.toUpperCase()}
onDropdownSelect={this._handleOnDropdownSelect}
/>
<View style={styles.commentWrapper}>
<Comments
selectedFilter={selectedFilter}
fetchPost={fetchPost}
commentCount={commentCount}
author={author}
@ -54,4 +64,4 @@ class CommentsDisplayView extends PureComponent {
}
}
export default CommentsDisplayView;
export default injectIntl(CommentsDisplayView);

View File

@ -1,9 +1,10 @@
import React, { PureComponent } from 'react';
import { withNavigation } from 'react-navigation';
// Services and Actions
import { getLeaderboard } from '../../../providers/esteem/esteem';
// Utilities
// Constants
import ROUTES from '../../../constants/routeNames';
// Component
import LeaderboardView from '../view/leaderboardView';
@ -19,21 +20,46 @@ class LeaderboardContainer extends PureComponent {
super(props);
this.state = {
users: null,
refreshing: false,
};
}
// Component Life Cycle Functions
async componentDidMount() {
const users = await getLeaderboard();
this.setState({ users });
componentDidMount() {
this._fetchLeaderBoard();
}
render() {
const { users } = this.state;
_handleOnUserPress = (username) => {
const { navigation } = this.props;
return <LeaderboardView users={users} handleOnUserPress={this._handleOnUserPress} />;
navigation.navigate({
routeName: ROUTES.SCREENS.PROFILE,
params: {
username,
},
});
};
_fetchLeaderBoard = async () => {
this.setState({ refreshing: true });
const users = await getLeaderboard();
this.setState({ users, refreshing: false });
};
render() {
const { users, refreshing } = this.state;
return (
<LeaderboardView
users={users}
refreshing={refreshing}
fetchLeaderBoard={this._fetchLeaderBoard}
handleOnUserPress={this._handleOnUserPress}
/>
);
}
}
export default LeaderboardContainer;
export default withNavigation(LeaderboardContainer);

View File

@ -2,8 +2,6 @@ import React, { PureComponent } from 'react';
import { View, FlatList, Text } from 'react-native';
import { injectIntl } from 'react-intl';
// Constants
// Components
import { UserListItem } from '../../basicUIElements';
@ -17,20 +15,28 @@ class LeaderboardView extends PureComponent {
*/
// Component Functions
_renderItem = (item, index) => (
<UserListItem
index={index}
username={item._id}
description={item.created}
isHasRightItem
isBlackRightColor
rightText={item.count}
itemIndex={index + 1}
/>
);
_renderItem = (item, index) => {
const { handleOnUserPress } = this.props;
return (
<UserListItem
index={index}
username={item._id}
description={item.created}
isHasRightItem
isClickable
isBlackRightColor
rightText={item.count}
itemIndex={index + 1}
handleOnPress={() => handleOnUserPress(item._id)}
/>
);
};
render() {
const { users, intl } = this.props;
const {
users, intl, fetchLeaderBoard, refreshing,
} = this.props;
return (
<View style={styles.container}>
@ -41,8 +47,10 @@ class LeaderboardView extends PureComponent {
</Text>
<FlatList
data={users}
refreshing={refreshing}
keyExtractor={item => item.voter}
removeClippedSubviews={false}
onRefresh={() => fetchLeaderBoard()}
renderItem={({ item, index }) => this._renderItem(item, index)}
/>
</View>

View File

@ -30,10 +30,17 @@ export default ({
}
} else {
newText = replaceBetween(text, selection, `${imagePrefix}[${itemText}](${itemUrl})`);
newSelection = {
start: selection.start + 1,
end: selection.start + 1 + itemText && itemText.length,
};
if (isImage) {
newSelection = {
start: newText && newText.length,
end: newText && newText.length,
};
} else {
newSelection = {
start: selection.start + 1,
end: selection.start + 1 + (itemText && itemText.length),
};
}
}
setState({ text: newText }, () => {
setState({ newSelection });

View File

@ -32,7 +32,7 @@ class PostBody extends PureComponent {
// Component Functions
_handleOnLinkPress = (evt, href, hrefatr) => {
const { handleOnUserPress, handleOnPostPress, intl } = this.props;
const { handleOnUserPress, handleOnPostPress } = this.props;
if (hrefatr.class === 'markdown-author-link') {
if (!handleOnUserPress) {
@ -47,9 +47,38 @@ class PostBody extends PureComponent {
handleOnPostPress(href);
}
} else {
Linking.canOpenURL(href).then((supported) => {
this._handleBrowserLink(href);
}
};
_handleBrowserLink = async (url) => {
if (!url) return;
let author;
let permlink;
const { intl } = this.props;
if (
url.indexOf('esteem') > -1
|| url.indexOf('steemit') > -1
|| url.indexOf('busy') > -1
|| url.indexOf('steempeak') > -1
) {
url = url.substring(url.indexOf('@'), url.length);
const routeParams = url.indexOf('/') > -1 ? url.split('/') : [url];
[, permlink] = routeParams;
author = routeParams[0].indexOf('@') > -1 ? routeParams[0].replace('@', '') : routeParams[0];
}
if (author && permlink) {
this._handleOnPostPress(permlink, author);
} else if (author) {
this._handleOnUserPress(author);
} else {
Linking.canOpenURL(url).then((supported) => {
if (supported) {
Linking.openURL(href);
Linking.openURL(url);
} else {
Alert.alert(intl.formatMessage({ id: 'alert.failed_to_open' }));
}

View File

@ -20,6 +20,7 @@ export default EStyleSheet.create({
scroll: {
height: '$deviceHeight / 1.135',
backgroundColor: '$primaryBackgroundColor',
marginBottom: 50,
},
footer: {
flexDirection: 'column',

View File

@ -115,7 +115,7 @@ class PostDisplayView extends PureComponent {
const { post, fetchPost, parentPost } = this.props;
const { postHeight, scrollHeight, isLoadedComments } = this.state;
const isPostEnd = scrollHeight > postHeight;
// const isPostEnd = scrollHeight > postHeight;
const isGetComment = scrollHeight + 300 > postHeight;
const formatedTime = post && getTimeFromNow(post.created);
@ -140,7 +140,7 @@ class PostDisplayView extends PureComponent {
size={16}
/>
<PostBody body={post.body} />
<View style={[styles.footer, !isPostEnd && styles.marginFooter]}>
<View style={styles.footer}>
<Tags tags={post.json_metadata && post.json_metadata.tags} />
<Text style={styles.footerText}>
Posted by

View File

@ -34,6 +34,7 @@ class SearchModalContainer extends PureComponent {
// Component Functions
_handleCloseButton = () => {
const { navigation } = this.props;
navigation.goBack();
};

View File

@ -3,9 +3,9 @@
"curation_reward": "কিউরেটর পুরস্কার",
"author_reward": "Whats up everyone",
"comment_benefactor_reward": "মন্তব্য উপকারিতার পুরস্কার",
"claim_reward_balance": "Claim Reward Balance",
"transfer": "Transfer",
"transfer_to_vesting": "Transfer To Vesting",
"claim_reward_balance": "এখানে iOS ডিভাইসগুলির জন্য eSteem Mobile 2 সাম্প্রতিক আপডেট। নতুন মোবাইল ক্লায়েন্ট পুনঃপ্রতিষ্ঠিত ইউজার ইন্টারফেসের সাথে Steem Blockchain এর জন্য।\n\n>এই সংস্করণটি eSteem 1.6.0 থেকে আলাদা, আপনি আগে এটি ব্যবহার করতে পারেন। এটি নিজে নিজে আপডেট হবে না। এখানে নতুন সংস্করণ ডাউনলোড করুন: [iOS](https:\/\/itunes.apple.com\/WebObjects\/MZStore.woa\/wa\/viewSoftware?id=1451896376&mt=8), [Android](https:\/\/play.google.com\/store\/apps\/details?id=app.esteem.mobile)\n\n**2.0.8 তে কি কি নতুন**\n\nআমাদের Android ব্যবহারকারীদের জন্য সমস্ত কার্যকারিতা আছে। আপনি কিছু সমস্যা খুঁজে পেলে আমাদের রিপোর্ট করুন, যাতে আমরা এটি আরো উন্নত করতে পারি। এখন থেকে আমাদের Android এবং iOS রিলিজগুলো সমস্ত ব্যবহারকারীদের সন্তুষ্ট করতে সক্ষম হবে।\n\nকিছু বৈশিষ্ট্য:\n\n* পোস্ট করতে, কমেন্ট করতে, ভোট দিতে পারবেন\n* বিজ্ঞপ্তি দেওয়া হবে, কার্যক্রম উন্নত\n* একাধিক অ্যাকাউন্ট সমর্থন করা হবে\n* Steemconnect এর মাধ্যমে এবং ঐতিহ্যবাহী লগইন করতে পারবেন\n* কন্টেন্ট খসড়া, বুকমার্ক, পছন্দের তালিকায় রাখতে পারবেন\n* সুন্দর এবং সহজ সম্পাদক, ছবি আপলোড, ক্যাপচার সমর্থন করা হবে\n* সার্ভার নির্বাচন \/ পরিবর্তন করা যাবে\n* রিওয়ার্ড নির্বাচন \/ পরিবর্তন করা যাবে\n* গাঢ় থিম রয়েছে\n* ইংরেজির পাশাপাশি আরও ৫টি ভাষায় কথা বলার ব্যবস্থা রয়েছে\n* পিন কোড দ্বারা নিরাপত্তার ব্যবস্থা রয়েছে\n* এবং আরো অনেক কিছু\n\nঅ্যাপল অ্যাপস্টোর থেকে ডাউনলোড করে এটি পরীক্ষা করে দেখুন এবং আপনার কেমন লাগে তা আমাদের জানান ...\n\nAndroid Version খুঁজছেন?\n\n** পরীক্ষকগণ **\nআমরা এখনও বিটা পরীক্ষক খুঁজছি, মন্তব্য বা GitHub এ এখানে বাগ রিপোর্ট করুন। টেস্ট পুরস্কৃত করা হবে।",
"transfer": "স্থানান্তর",
"transfer_to_vesting": "Vesting কাছে হস্তান্তর",
"transfer_from_savings": "Transfer From Savings",
"withdraw_vesting": "Power Down",
"fill_order": "Fill Order"

View File

@ -199,5 +199,11 @@
"deep_link": {
"no_existing_user": "No existing user",
"no_existing_post": "No existing post"
},
"comment_filter": {
"trending": "trending",
"reputation": "reputation",
"votes": "votes",
"age": "age"
}
}

View File

@ -46,8 +46,8 @@
"hours": "시간",
"voting_power": "보팅 파워",
"login_to_see": "로그인해서 확인하기",
"havent_commented": "댓글을 작성한 적이 아직 없습니다",
"havent_posted": "게시한 글이 아직 없습니다",
"havent_commented": "아직 댓글을 작성한 적이 없습니다",
"havent_posted": "아직 게시한 글이 없습니다",
"steem_power": "스팀 파워",
"next_power_text": "다음 번 파워 다운까지 남은 기간",
"days": "일",
@ -57,7 +57,7 @@
},
"settings": {
"settings": "설정",
"currency": "화폐 표기",
"currency": "화폐",
"language": "언어",
"server": "서버",
"dark_theme": "어두운 테마",
@ -120,7 +120,7 @@
},
"pincode": {
"enter_text": "잠금을 해제하려면 PIN 코드를 입력해주세요",
"set_new": "새로운 PIN 코드 설정",
"set_new": "새로운 PIN 코드 설정하기",
"write_again": "다시 한 번 입력해주세요",
"forgot_text": "PIN 코드가 기억나지 않습니다..."
},
@ -137,7 +137,7 @@
"warning": "경고",
"invalid_pincode": "올바르지 않은 PIN 코드입니다. 확인 후 다시 시도해주세요.",
"remove_alert": "정말 삭제하시겠습니까?",
"clear_alert": "정말 삭제하시겠습니까?",
"clear_alert": "정말 삭제하시겠습니까?",
"clear": "삭제",
"cancel": "취소",
"delete": "삭제",
@ -145,7 +145,7 @@
"no_internet": "인터넷 연결을 확인하세요!"
},
"post": {
"reblog_alert": "정말 리블로그 하시겠습니까?"
"reblog_alert": "정말 리블로그 하시겠습니까?"
},
"drafts": {
"title": "임시 보관함",

View File

@ -0,0 +1 @@
export default ['trending', 'reputation', 'votes', 'age'];

View File

@ -75,10 +75,12 @@ class ApplicationContainer extends Component {
this.state = {
isRenderRequire: true,
isReady: false,
isIos: Platform.OS !== 'android',
};
}
componentDidMount = async () => {
const { isIos } = this.state;
let isConnected;
await NetInfo.isConnected.fetch().then((_isConnected) => {
@ -86,7 +88,7 @@ class ApplicationContainer extends Component {
});
NetInfo.isConnected.addEventListener('connectionChange', this._handleConntectionChange);
BackHandler.addEventListener('hardwareBackPress', this._onBackPress);
if (!isIos) BackHandler.addEventListener('hardwareBackPress', this._onBackPress);
if (isConnected) {
this._fetchApp();
@ -116,7 +118,10 @@ class ApplicationContainer extends Component {
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.onBackPress);
const { isIos } = this.state;
if (!isIos) BackHandler.removeEventListener('hardwareBackPress', this._onBackPress);
NetInfo.isConnected.removeEventListener('connectionChange', this._handleConntectionChange);
clearInterval(this.globalInterval);
}

View File

@ -71,7 +71,7 @@ class NotificationContainer extends Component {
params: {
author: data.author,
permlink: data.permlink,
isNotification: true,
isHasParentPost: data.parent_author && data.parent_permlink,
},
key: data.permlink,
});

View File

@ -20,7 +20,7 @@ class PostContainer extends Component {
post: null,
error: null,
isNewPost: false,
isNotification: false,
isHasParentPost: false,
parentPost: null,
};
}
@ -29,7 +29,7 @@ class PostContainer extends Component {
componentDidMount() {
const { navigation } = this.props;
const {
content, permlink, author, isNewPost, isNotification,
content, permlink, author, isNewPost, isHasParentPost,
} = navigation.state && navigation.state.params;
if (isNewPost) this.setState({ isNewPost });
@ -39,7 +39,7 @@ class PostContainer extends Component {
} else if (author && permlink) {
this._loadPost(author, permlink);
if (isNotification) this.setState({ isNotification });
if (isHasParentPost) this.setState({ isHasParentPost });
}
}
@ -81,10 +81,10 @@ class PostContainer extends Component {
render() {
const { currentAccount, isLoggedIn } = this.props;
const {
error, isNewPost, parentPost, post, isNotification,
error, isNewPost, parentPost, post, isHasParentPost,
} = this.state;
if (isNotification && post) this._loadPost(post.parent_author, post.parent_permlink, true);
if (isHasParentPost && post) this._loadPost(post.parent_author, post.parent_permlink, true);
return (
<PostScreen

View File

@ -0,0 +1,24 @@
export default (input) => {
if (input === 0) {
return 25;
}
if (!input) {
return input;
}
let neg = false;
if (input < 0) neg = true;
let reputationLevel = Math.log10(Math.abs(input));
reputationLevel = Math.max(reputationLevel - 9, 0);
if (reputationLevel < 0) reputationLevel = 0;
if (neg) reputationLevel *= -1;
reputationLevel = reputationLevel * 9 + 25;
return Math.floor(reputationLevel);
};

View File

@ -56,6 +56,7 @@ export const parsePost = (post, currentUserName, isSummary = false) => {
const isVoted = (activeVotes, currentUserName) => activeVotes.some(v => v.voter === currentUserName && v.percent > 0);
const postImage = (metaData, body) => {
const imgTagRegex = /(<img[^>]*>)/g;
const markdownImageRegex = /!\[[^\]]*\]\((.*?)\s*("(?:.*[^"])")?\s*\)/g;
const urlRegex = /(http|ftp|https):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?/gm;
const imageRegex = /(http(s?):)([/|.|\w|\s|-])*\.(?:jpg|gif|png)/g;
@ -76,47 +77,56 @@ const postImage = (metaData, body) => {
imageLink = imageMatch[0];
}
if (!imageLink && imgTagRegex.test(body)) {
const _imgTag = body.match(imgTagRegex);
const match = _imgTag[0].match(urlRegex);
if (match && match[0]) {
imageLink = match[0];
}
}
if (imageLink) {
return `https://steemitimages.com/600x0/${imageLink}`;
}
return '';
};
export const protocolUrl2Obj = (url) => {
let urlPart = url.split('://')[1];
// export const protocolUrl2Obj = (url) => {
// let urlPart = url.split('://')[1];
// remove last char if /
if (urlPart.endsWith('/')) {
urlPart = urlPart.substring(0, urlPart.length - 1);
}
// // remove last char if /
// if (urlPart.endsWith('/')) {
// urlPart = urlPart.substring(0, urlPart.length - 1);
// }
const parts = urlPart.split('/');
// const parts = urlPart.split('/');
// filter
if (parts.length === 1) {
return { type: 'filter' };
}
// // filter
// if (parts.length === 1) {
// return { type: 'filter' };
// }
// filter with tag
if (parts.length === 2) {
return { type: 'filter-tag', filter: parts[0], tag: parts[1] };
}
// // filter with tag
// if (parts.length === 2) {
// return { type: 'filter-tag', filter: parts[0], tag: parts[1] };
// }
// account
if (parts.length === 1 && parts[0].startsWith('@')) {
return { type: 'account', account: parts[0].replace('@', '') };
}
// // account
// if (parts.length === 1 && parts[0].startsWith('@')) {
// return { type: 'account', account: parts[0].replace('@', '') };
// }
// post
if (parts.length === 3 && parts[1].startsWith('@')) {
return {
type: 'post',
cat: parts[0],
author: parts[1].replace('@', ''),
permlink: parts[2],
};
}
};
// // post
// if (parts.length === 3 && parts[1].startsWith('@')) {
// return {
// type: 'post',
// cat: parts[0],
// author: parts[1].replace('@', ''),
// permlink: parts[2],
// };
// }
// };
export const parseComments = (comments) => {
comments.map((comment) => {