Merge pull request #2882 from ecency/nt/recover-lint

removed conflicting eslint parser
This commit is contained in:
Feruz M 2024-05-31 13:28:38 +03:00 committed by GitHub
commit 64d9a86de6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
72 changed files with 1446 additions and 1649 deletions

View File

@ -97,6 +97,10 @@
"ts": "never",
"tsx": "never"
}
]
],
"class-methods-use-this": "off",
"react/jsx-wrap-multilines": "off",
"jsx-one-expression-per-line": "off",
"react/jsx-one-expression-per-line": "off"
}
}

View File

@ -196,7 +196,6 @@
"@babel/preset-env": "^7.20.0",
"@babel/runtime": "^7.20.0",
"@bugsnag/source-maps": "^2.3.1",
"@react-native-community/eslint-config": "^2.0.0",
"@react-native/babel-preset": "0.73.19",
"@react-native/eslint-config": "0.73.2",
"@react-native/metro-config": "0.73.3",

View File

@ -25,7 +25,6 @@ export const AutoHeightImage = ({
const _initialHeight = useMemo(() => {
let _height = contentWidth / (16 / 9);
if (metadata && metadata.image && metadata.image_ratios) {
metadata.image_ratios.forEach((_ratio, index) => {
const url = metadata.image[index];
@ -75,12 +74,7 @@ export const AutoHeightImage = ({
return (
<TouchableOpacity onPress={onPress} disabled={isAnchored} activeOpacity={activeOpacity || 1}>
<ExpoImage
style={imgStyle}
source={{ uri: imgUrl }}
contentFit={"contain"}
onLoad={_onLoad}
/>
<ExpoImage style={imgStyle} source={{ uri: imgUrl }} contentFit="contain" onLoad={_onLoad} />
</TouchableOpacity>
);
};

View File

@ -6,7 +6,6 @@ import { useSelector } from 'react-redux';
import styles from './listItemPlaceHolderStyles';
const CommentPlaceHolderView = () => {
const _width = 300;
const animationStyle = {

View File

@ -10,7 +10,7 @@ const PostCardPlaceHolder = () => {
const animationStyle = {
width: _width,
height: _width * 2
height: _width * 2,
};
const isDarkTheme = useSelector((state) => state.application.isDarkTheme);

View File

@ -9,9 +9,8 @@ const PostPlaceHolder = () => {
const isDarkTheme = useSelector((state) => state.application.isDarkTheme);
const color = isDarkTheme ? '#2e3d51' : '#f5f5f5';
const width = getWindowDimensions().nativeWidth - 36;
const height = width * 1.65 ;
const height = width * 1.65;
return (
<View>

View File

@ -9,7 +9,7 @@ interface CheckBoxProps {
locked?: boolean;
isRound?: boolean;
style?: ViewStyle;
clicked: (val: string, isChecked: boolean) => void,
clicked: (val: string, isChecked: boolean) => void;
}
const CheckBoxView = ({ clicked, value, isChecked, style, locked, isRound }: CheckBoxProps) => {
@ -27,12 +27,12 @@ const CheckBoxView = ({ clicked, value, isChecked, style, locked, isRound }: Che
}
};
const containerStyle = [styles.bigSquare, style, isRound && { borderRadius: 10 }]
const containerStyle = [styles.bigSquare, style, isRound && { borderRadius: 10 }];
const innerStyle = [
styles.smallSquare,
isCheck && styles.checked,
isRound && { borderRadius: 5 }
]
isRound && { borderRadius: 5 },
];
return (
<TouchableOpacity disabled={locked} onPress={_checkClicked}>

View File

@ -185,11 +185,7 @@ const DraftListItemView = ({
<View style={styles.body}>
<TouchableOpacity onPress={_onItemPress} onLongPress={_onItemLongPress}>
{image !== null && (
<ExpoImage
source={image}
style={styles.thumbnail}
contentFit="cover"
/>
<ExpoImage source={image} style={styles.thumbnail} contentFit="cover" />
)}
<View style={styles.postDescripton}>
{title !== '' && <Text style={styles.title}>{title}</Text>}

View File

@ -19,7 +19,7 @@ const EditAvatar = ({ username, avatarUrl, showImageUploadActions, isUploading }
/>
<IconButton
isLoading={isUploading}
color={'white'}
color="white"
iconStyle={styles.addIcon}
style={styles.addButton}
iconType="MaterialIcons"

View File

@ -7,17 +7,17 @@ export default EStyleSheet.create({
},
leftContainer: {
position: 'absolute',
flexDirection:'row',
alignItems:'center',
flexDirection: 'row',
alignItems: 'center',
left: 8,
},
rightContainer: {
position: 'absolute',
flexDirection:'row',
flexDirection: 'row',
right: 8,
},
imageGalleryHeaderText: {
color: '$iconColor',
fontSize: 16
fontSize: 16,
},
});

View File

@ -1,165 +1,148 @@
import React, { forwardRef, useImperativeHandle, useState } from 'react';
import { View, Text, Platform, SafeAreaView, Share, Alert } from 'react-native';
import styles from './imageViewer.styles';
import EStyleSheet from 'react-native-extended-stylesheet';
import ImageViewing from 'react-native-image-viewing';
import { useIntl } from 'react-intl';
import IconButton from '../iconButton';
import { useDispatch } from 'react-redux';
import { PermissionsAndroid } from 'react-native';
import { CameraRoll } from '@react-native-camera-roll/camera-roll';
import { Image as ExpoImage } from 'expo-image';
import RNFetchBlob from 'rn-fetch-blob';
import IconButton from '../iconButton';
import styles from './imageViewer.styles';
// eslint-disable-next-line no-empty-pattern
export const ImageViewer = forwardRef(({}, ref) => {
const intl = useIntl();
// intl.formatMessage({ id: 'post.copy_link' }),
// intl.formatMessage({ id: 'post.gallery_mode' }),
// intl.formatMessage({ id: 'post.save_to_local' }),
export const ImageViewer = forwardRef(({ }, ref) => {
const intl = useIntl();
const dispatch = useDispatch();
// intl.formatMessage({ id: 'post.copy_link' }),
// intl.formatMessage({ id: 'post.gallery_mode' }),
// intl.formatMessage({ id: 'post.save_to_local' }),
const [visible, setVisible] = useState(false);
const [imageUrls, setImageUrls] = useState<string[]>([]);
const [selectedIndex, setSelectedIndex] = useState(0);
const [visible, setVisible] = useState(false);
const [imageUrls, setImageUrls] = useState<string[]>([]);
const [selectedIndex, setSelectedIndex] = useState(0);
useImperativeHandle(ref, () => ({
show(selectedUrl: string, _imageUrls: string[]) {
setImageUrls(_imageUrls);
setSelectedIndex(_imageUrls.indexOf(selectedUrl));
setVisible(true);
if (Platform.OS === 'ios') {
ExpoImage.prefetch(_imageUrls, 'memory');
}
},
}));
useImperativeHandle(ref, () => ({
show(selectedUrl: string, _imageUrls: string[]) {
setImageUrls(_imageUrls)
setSelectedIndex(_imageUrls.indexOf(selectedUrl))
setVisible(true);
if (Platform.OS === 'ios') {
ExpoImage.prefetch(_imageUrls, "memory");
}
},
}));
const checkAndroidPermission = async () => {
try {
const permission = PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE;
await PermissionsAndroid.request(permission);
Promise.resolve();
} catch (error) {
Promise.reject(error);
}
};
const _downloadImage = async (uri: string) => {
return RNFetchBlob.config({
fileCache: true,
appendExt: 'jpg',
})
.fetch('GET', uri)
.then((res) => {
const { status } = res.info();
if (status == 200) {
return res.path();
} else {
Promise.reject();
}
})
.catch((errorMessage) => {
Promise.reject(errorMessage);
});
};
const _onSavePress = async (index: number) => {
try {
if (Platform.OS === 'android') {
await checkAndroidPermission();
}
const url = imageUrls[index];
const imagePath = Platform.select({
ios: await ExpoImage.getCachePathAsync(url),
android: await _downloadImage(url)
})
if (!imagePath) {
return;
}
const uri = `file://${imagePath}`
await CameraRoll.saveAsset(uri, { album: 'Ecency' })
Alert.alert(intl.formatMessage({ id: "post.image_saved" }));
} catch (err) {
console.warn("fail to save image", err.message)
}
};
const _onCopyPress = (index: number) => {
const url = imageUrls[index]
Share.share(Platform.OS === 'ios' ?
{ url } : { message: url })
const checkAndroidPermission = async () => {
try {
const permission = PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE;
await PermissionsAndroid.request(permission);
Promise.resolve();
} catch (error) {
Promise.reject(error);
}
};
const _downloadImage = async (uri: string) => {
return RNFetchBlob.config({
fileCache: true,
appendExt: 'jpg',
})
.fetch('GET', uri)
.then((res) => {
const { status } = res.info();
const _renderIconButton = (iconName: string, onPress: () => void) => (
<IconButton
name={iconName}
iconType="MaterialCommunityIcons"
color={EStyleSheet.value('$iconColor')}
style={{ marginHorizontal: 4 }}
size={24}
onPress={onPress}
/>
)
if (status == 200) {
return res.path();
} else {
Promise.reject();
}
})
.catch((errorMessage) => {
Promise.reject(errorMessage);
});
};
const _onSavePress = async (index: number) => {
try {
if (Platform.OS === 'android') {
await checkAndroidPermission();
}
const _renderImageViewerHeader = (imageIndex: number) => {
return (
<SafeAreaView
style={{
marginTop: Platform.select({ ios: 0, android: 25 }),
}}>
<View style={styles.imageViewerHeaderContainer}>
<View style={styles.leftContainer}>
{_renderIconButton('close', _onCloseImageViewer)}
<Text style={styles.imageGalleryHeaderText}>{
`Preview (${imageIndex + 1}/${imageUrls.length})`}</Text>
</View>
const url = imageUrls[index];
<View style={styles.rightContainer}>
{_renderIconButton('content-copy', () => _onCopyPress(imageIndex))}
{_renderIconButton('download', () => _onSavePress(imageIndex))}
</View>
const imagePath = Platform.select({
ios: await ExpoImage.getCachePathAsync(url),
android: await _downloadImage(url),
});
</View>
</SafeAreaView>
);
};
if (!imagePath) {
return;
}
const uri = `file://${imagePath}`;
await CameraRoll.saveAsset(uri, { album: 'Ecency' });
const _onCloseImageViewer = () => {
setVisible(false);
setImageUrls([])
Alert.alert(intl.formatMessage({ id: 'post.image_saved' }));
} catch (err) {
console.warn('fail to save image', err.message);
}
};
const _onCopyPress = (index: number) => {
const url = imageUrls[index];
Share.share(Platform.OS === 'ios' ? { url } : { message: url });
};
const _renderIconButton = (iconName: string, onPress: () => void) => (
<IconButton
name={iconName}
iconType="MaterialCommunityIcons"
color={EStyleSheet.value('$iconColor')}
style={{ marginHorizontal: 4 }}
size={24}
onPress={onPress}
/>
);
const _renderImageViewerHeader = (imageIndex: number) => {
return (
<ImageViewing
images={imageUrls.map((url) => ({ uri: url }))}
imageIndex={selectedIndex}
visible={visible}
animationType="slide"
swipeToCloseEnabled
onRequestClose={_onCloseImageViewer}
HeaderComponent={(data) => _renderImageViewerHeader(data.imageIndex)}
/>
<SafeAreaView
style={{
marginTop: Platform.select({ ios: 0, android: 25 }),
}}
>
<View style={styles.imageViewerHeaderContainer}>
<View style={styles.leftContainer}>
{_renderIconButton('close', _onCloseImageViewer)}
<Text style={styles.imageGalleryHeaderText}>
{`Preview (${imageIndex + 1}/${imageUrls.length})`}
</Text>
</View>
<View style={styles.rightContainer}>
{_renderIconButton('content-copy', () => _onCopyPress(imageIndex))}
{_renderIconButton('download', () => _onSavePress(imageIndex))}
</View>
</View>
</SafeAreaView>
);
};
const _onCloseImageViewer = () => {
setVisible(false);
setImageUrls([]);
};
return (
<ImageViewing
images={imageUrls.map((url) => ({ uri: url }))}
imageIndex={selectedIndex}
visible={visible}
animationType="slide"
swipeToCloseEnabled
onRequestClose={_onCloseImageViewer}
HeaderComponent={(data) => _renderImageViewerHeader(data.imageIndex)}
/>
);
});

View File

@ -1 +1 @@
export * from './imageViewer';
export * from './imageViewer';

View File

@ -260,5 +260,5 @@ export {
WebViewModal,
OrDivider,
PostTranslationModal,
ImageViewer
ImageViewer,
};

View File

@ -1,5 +1,5 @@
import React, { useEffect, useRef, useState } from 'react';
import { Keyboard, Text, View, ViewStyle } from 'react-native';
import { Keyboard, View, ViewStyle } from 'react-native';
import {
FlatList,
Gesture,
@ -279,4 +279,4 @@ export const EditorToolbar = ({
)}
</View>
);
};
};

View File

@ -44,7 +44,6 @@ export const PostCardActionsPanel = ({ content, handleCardInteraction }: Props)
permlink,
},
});
};
return (

View File

@ -1,13 +1,7 @@
import React, { useMemo, useState } from 'react';
import { TouchableOpacity, Text, View } from 'react-native';
// Utils
import { Image as ExpoImage } from 'expo-image';
import { get } from 'lodash';
import { useIntl } from 'react-intl';
// Components
@ -47,16 +41,17 @@ export const PostCardContent = ({
const [calcImgHeight, setCalcImgHeight] = useState(imageRatio ? imgWidth / imageRatio : 300);
const resizeMode = useMemo(() => {
return calcImgHeight < dim.height ? "contain" : "cover";
return calcImgHeight < dim.height ? 'contain' : 'cover';
}, [dim.height]);
//featured text can be used to add more labels in future by just inserting text as array item
// featured text can be used to add more labels in future by just inserting text as array item
const _featuredText = [
content?.is_promoted && intl.formatMessage({ id: 'post.promoted' }),
content?.json_metadata?.content_type === ContentType.POLL && intl.formatMessage({ id: 'post.poll' })
].filter(i => !!i).join(' | ')
content?.is_promoted && intl.formatMessage({ id: 'post.promoted' }),
content?.json_metadata?.content_type === ContentType.POLL &&
intl.formatMessage({ id: 'post.poll' }),
]
.filter((i) => !!i)
.join(' | ');
const _onPress = () => {
handleCardInteraction(PostCardActionIds.NAVIGATE, {
@ -107,9 +102,7 @@ export const PostCardContent = ({
)}
<View style={[styles.postDescripton]}>
{!!_featuredText && (
<Text style={styles.promotedText}>{_featuredText}</Text>
)}
{!!_featuredText && <Text style={styles.promotedText}>{_featuredText}</Text>}
<Text style={styles.title}>{content.title}</Text>
<Text style={styles.summary}>{content.summary}</Text>
</View>

View File

@ -64,7 +64,12 @@ export const PostCardHeader = ({ intl, content, isHideImage, handleCardInteracti
<Icon style={styles.pollPostIcon} size={16} name="chart" iconType="SimpleLineIcons" />
)}
{(content?.stats?.is_pinned || content?.stats?.is_pinned_blog) && (
<Icon style={styles.pushPinIcon} size={20} name="pin" iconType="MaterialCommunityIcons" />
<Icon
style={styles.pushPinIcon}
size={20}
name="pin"
iconType="MaterialCommunityIcons"
/>
)}
</View>

View File

@ -80,7 +80,7 @@ export default EStyleSheet.create({
flexDirection: 'row',
alignItems: 'center',
marginRight: -8,
paddingBottom:5
paddingBottom: 5,
},
pushPinIcon: {
color: '$primaryRed',

View File

@ -1,6 +1,6 @@
import React, { Fragment, useState, useEffect, useRef } from 'react';
import { PermissionsAndroid, Platform, View } from 'react-native';
import {CameraRoll} from '@react-native-camera-roll/camera-roll';
import { CameraRoll } from '@react-native-camera-roll/camera-roll';
import { useIntl } from 'react-intl';
import EStyleSheet from 'react-native-extended-stylesheet';
import RNFetchBlob from 'rn-fetch-blob';
@ -64,7 +64,6 @@ const PostBody = ({ body, metadata, onLoadEnd, width }) => {
}
};
const handleLinkPress = (ind) => {
if (ind === 1) {
// open link
@ -231,18 +230,14 @@ const PostBody = ({ body, metadata, onLoadEnd, width }) => {
};
const _handleSetSelectedImage = (imageLink, postImgUrls) => {
if(imageViewerRef.current){
if (imageViewerRef.current) {
imageViewerRef.current.show(imageLink, postImgUrls);
}
};
return (
<Fragment>
<ImageViewer
ref={imageViewerRef}
/>
<ImageViewer ref={imageViewerRef} />
<ActionSheetView
ref={youtubePlayerRef}

View File

@ -23,7 +23,7 @@ interface PostHtmlInteractionHandlerProps {
export const PostHtmlInteractionHandler = forwardRef(
({ postType }: PostHtmlInteractionHandlerProps, ref) => {
console.log('Post Type', postType)
console.log('Post Type', postType);
const navigation = useNavigation();
const dispatch = useDispatch();
@ -40,7 +40,7 @@ export const PostHtmlInteractionHandler = forwardRef(
useImperativeHandle(ref, () => ({
handleImagePress: (url: string, postImgUrls: string[]) => {
if(imageViewerRef.current){
if (imageViewerRef.current) {
imageViewerRef.current.show(url, postImgUrls);
}
},
@ -69,9 +69,6 @@ export const PostHtmlInteractionHandler = forwardRef(
},
}));
const _handleLinkOptionPress = (ind) => {
if (ind === 1) {
// open link
@ -103,10 +100,7 @@ export const PostHtmlInteractionHandler = forwardRef(
return (
<Fragment>
<ImageViewer
ref={imageViewerRef}
/>
<ImageViewer ref={imageViewerRef} />
<OptionsModal
ref={actionLink}

View File

@ -1,2 +1,2 @@
export * from './pollHeader';
export * from './pollChoices';
export * from './pollChoices';

View File

@ -1,17 +1,17 @@
import React, { useEffect, useMemo, useState } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { Bar as ProgressBar } from 'react-native-progress';
import EStyleSheet from 'react-native-extended-stylesheet';
import { useIntl } from 'react-intl';
import { get } from 'lodash';
import styles from '../styles/pollChoices.styles';
import getWindowDimensions from '../../../utils/getWindowDimensions';
import EStyleSheet from 'react-native-extended-stylesheet';
import { PollChoice, PollVoter } from '../../../providers/polls/polls.types';
import { mapMetaChoicesToPollChoices } from '../../../providers/polls/converters';
import { CheckBox } from '../../checkbox';
import { PostMetadata, PollPreferredInterpretation } from '../../../providers/hive/hive.types';
import { PostMetadata } from '../../../providers/hive/hive.types';
import { PollModes } from '../container/postPoll';
import { TextButton } from '../../buttons';
import { useIntl } from 'react-intl';
import { get } from 'lodash';
interface PollChoicesProps {
loading: boolean;
@ -22,13 +22,11 @@ interface PollChoicesProps {
selection: number[];
voteDisabled: boolean;
hideVoters: boolean;
interpretationToken?: boolean
interpretationToken?: boolean;
handleChoiceSelect: (optionNum: number) => void;
handleVotersPress: (optionNum: number) => void;
}
export const PollChoices = ({
choices,
metadata,
@ -42,101 +40,110 @@ export const PollChoices = ({
handleChoiceSelect,
handleVotersPress,
}: PollChoicesProps) => {
const intl = useIntl()
const intl = useIntl();
const [_choices, setChoices] = useState(
choices || mapMetaChoicesToPollChoices(metadata.choices));
const [_choices, setChoices] = useState(choices || mapMetaChoicesToPollChoices(metadata.choices));
const totalVotes = useMemo(
() => _choices.reduce(
(prevVal, option) => prevVal + get(option.votes, interpretationToken ? 'hive_hp_incl_proxied':'total_votes', 0), 0)
, [_choices, interpretationToken]);
() =>
_choices.reduce(
(prevVal, option) =>
prevVal +
get(option.votes, interpretationToken ? 'hive_hp_incl_proxied' : 'total_votes', 0),
0,
),
[_choices, interpretationToken],
);
useEffect(() => {
if (!loading && !!choices) {
setChoices(choices)
setChoices(choices);
}
}, [loading, choices])
const _isModeSelect = mode === PollModes.SELECT
}, [loading, choices]);
const _isModeSelect = mode === PollModes.SELECT;
const _handleChoiceSelect = (choiceNum: number) => {
handleChoiceSelect(choiceNum);
}
};
const _renderProgressBar = (option: PollChoice) => {
const _isVoted =
!_isModeSelect && userVote?.choices && userVote.choices.includes(option.choice_num);
const _isSelected = selection.includes(option.choice_num);
const _isVoted = !_isModeSelect && userVote?.choices && userVote.choices.includes(option.choice_num);
const _isSelected = selection.includes(option.choice_num)
const votes =
Math.round(
get(option.votes, interpretationToken ? 'hive_hp_incl_proxied' : 'total_votes', 0) * 1000,
) / 1000;
const votes = Math.round(get(option.votes, interpretationToken ? 'hive_hp_incl_proxied':'total_votes', 0) * 1000 ) / 1000;
const percentage = (!_isModeSelect && !!totalVotes) ? (votes / totalVotes) * 100 : 0; //TODO: adjust logic here
const percentage = !_isModeSelect && !!totalVotes ? (votes / totalVotes) * 100 : 0; // TODO: adjust logic here
const _barWidth = getWindowDimensions().width - 64;
const _barStyle = {
...styles.progressBar,
borderColor: EStyleSheet.value(_isSelected ? '$primaryBlue' : 'transparent')
}
borderColor: EStyleSheet.value(_isSelected ? '$primaryBlue' : 'transparent'),
};
const _onVotersPress = () => {
handleVotersPress(option.choice_num)
}
handleVotersPress(option.choice_num);
};
return (
<View style={styles.choiceWrapper}>
<View>
<ProgressBar
progress={_isModeSelect ? 0 : percentage / 100} width={_barWidth} height={40}
progress={_isModeSelect ? 0 : percentage / 100}
width={_barWidth}
height={40}
style={_barStyle}
unfilledColor={EStyleSheet.value("$primaryLightBackground")}
color={EStyleSheet.value(_isVoted ? "$primaryLightBlue2" : "$darkIconColor")}
unfilledColor={EStyleSheet.value('$primaryLightBackground')}
color={EStyleSheet.value(_isVoted ? '$primaryLightBlue2' : '$darkIconColor')}
indeterminate={mode === PollModes.LOADING}
useNativeDriver={true}
/>
<View style={styles.progressContentWrapper}>
<View style={styles.choiceLabelWrapper}>
<CheckBox locked isChecked={_isVoted} isRound={true} style={styles.checkContainerStyle} />
<Text numberOfLines={2} style={styles.label}>{option.choice_text}</Text>
<CheckBox
locked
isChecked={_isVoted}
isRound={true}
style={styles.checkContainerStyle}
/>
<Text numberOfLines={2} style={styles.label}>
{option.choice_text}
</Text>
</View>
{!_isModeSelect &&
{!_isModeSelect && (
<TextButton
disabled={hideVoters}
textStyle={styles.count}
text={`${votes} ${intl.formatMessage({ id: interpretationToken ? 'post_poll.hp':'post_poll.voted'})}`} onPress={_onVotersPress} />
}
text={`${votes} ${intl.formatMessage({
id: interpretationToken ? 'post_poll.hp' : 'post_poll.voted',
})}`}
onPress={_onVotersPress}
/>
)}
</View>
</View>
</View>
);
};
const renderOptions = () => {
return _choices.map((option, index) => {
return (
<TouchableOpacity key={index} disabled={voteDisabled} onPress={() => _handleChoiceSelect(option.choice_num)}>
<View style={{ marginVertical: 5 }}>
{_renderProgressBar(option)}
</View>
<TouchableOpacity
// eslint-disable-next-line react/no-array-index-key
key={`key_${index}`}
disabled={voteDisabled}
onPress={() => _handleChoiceSelect(option.choice_num)}
>
<View style={{ marginVertical: 5 }}>{_renderProgressBar(option)}</View>
</TouchableOpacity>
);
});
};
return (
<View>
{renderOptions()}
</View>
);
return <View>{renderOptions()}</View>;
};

View File

@ -1,60 +1,59 @@
import React from 'react';
import { View, Text } from 'react-native';
import styles from '../styles/pollHeader.styles';
import { Icon, PopoverWrapper } from '../../';
import { getTimeFromNow } from '../../../utils/time';
import EStyleSheet from 'react-native-extended-stylesheet';
import { useIntl } from 'react-intl';
import styles from '../styles/pollHeader.styles';
import { Icon, PopoverWrapper } from '../..';
import { getTimeFromNow } from '../../../utils/time';
import { PollPreferredInterpretation, PostMetadata } from '../../../providers/hive/hive.types';
interface PollHeaderProps {
metadata: PostMetadata
expired: boolean;
metadata: PostMetadata;
expired: boolean;
}
export const PollHeader = ({ metadata, expired }: PollHeaderProps) => {
const intl = useIntl();
const intl = useIntl();
const _endDate = new Date(metadata.end_time * 1000);
const formattedEndTime = expired
? intl.formatMessage({ id: "post_poll.ended" })
: intl.formatMessage({ id: "post_poll.ends" }, { inTime: getTimeFromNow(_endDate) })
const _endDate = new Date(metadata.end_time * 1000);
const formattedEndTime = expired
? intl.formatMessage({ id: 'post_poll.ended' })
: intl.formatMessage({ id: 'post_poll.ends' }, { inTime: getTimeFromNow(_endDate) });
const _ageLimit = metadata?.filters?.account_age || 0;
const _interpretationToken =
metadata?.preferred_interpretation === PollPreferredInterpretation.TOKENS || false;
const _maxChoicesVotable = metadata?.max_choices_voted || 1;
const _ageLimit = metadata?.filters?.account_age || 0;
const _interpretationToken = metadata?.preferred_interpretation === PollPreferredInterpretation.TOKENS || false;
const _maxChoicesVotable = metadata?.max_choices_voted || 1;
const _renderSubText = (text) => (
<Text style={styles.subText}>
{text}
</Text>
)
const _renderSubText = (text) => <Text style={styles.subText}>{text}</Text>;
return (
<View>
<View style={styles.headerWrapper}>
<Text style={styles.question}>{metadata.question} </Text>
<PopoverWrapper text={_endDate.toString()} >
<View style={styles.timeContainer}>
<Text style={styles.timeText} >{formattedEndTime}</Text>
<Icon
iconType="MaterialCommunityIcons"
style={styles.clockIcon}
name="clock-outline"
color={EStyleSheet.value('$primaryDarkText')}
size={20}
/>
</View>
</PopoverWrapper>
</View>
{!!_ageLimit && _renderSubText(intl.formatMessage({ id: "post_poll.age_limit" }, { days: _ageLimit }))}
{_interpretationToken && _renderSubText(intl.formatMessage({ id: "post_poll.interpretation_token" }))}
{_maxChoicesVotable > 1 && _renderSubText(intl.formatMessage({ id: "post_poll.max_choices" }, { choices: _maxChoicesVotable }))}
</View>
);
return (
<View>
<View style={styles.headerWrapper}>
<Text style={styles.question}>{metadata.question}</Text>
<PopoverWrapper text={_endDate.toString()}>
<View style={styles.timeContainer}>
<Text style={styles.timeText}>{formattedEndTime}</Text>
<Icon
iconType="MaterialCommunityIcons"
style={styles.clockIcon}
name="clock-outline"
color={EStyleSheet.value('$primaryDarkText')}
size={20}
/>
</View>
</PopoverWrapper>
</View>
{!!_ageLimit &&
_renderSubText(intl.formatMessage({ id: 'post_poll.age_limit' }, { days: _ageLimit }))}
{_interpretationToken &&
_renderSubText(intl.formatMessage({ id: 'post_poll.interpretation_token' }))}
{_maxChoicesVotable > 1 &&
_renderSubText(
intl.formatMessage({ id: 'post_poll.max_choices' }, { choices: _maxChoicesVotable }),
)}
</View>
);
};

View File

@ -1,212 +1,207 @@
import React, { useEffect, useMemo, useState } from 'react'
import { View } from 'react-native'
import { ContentType, PollPreferredInterpretation, PostMetadata } from '../../../providers/hive/hive.types';
import React, { useEffect, useMemo, useState } from 'react';
import { View } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { useIntl } from 'react-intl';
import {
ContentType,
PollPreferredInterpretation,
PostMetadata,
} from '../../../providers/hive/hive.types';
import { PollChoices, PollHeader } from '../children';
import styles from '../styles/postPoll.styles';
import { pollQueries } from '../../../providers/queries';
import { useAppSelector } from '../../../hooks';
import { MainButton, TextButton } from '../..';
import { useNavigation } from '@react-navigation/native';
import ROUTES from '../../../constants/routeNames';
import { useIntl } from 'react-intl';
import { getDaysPassedSince } from '../../../utils/time';
export enum PollModes {
LOADING = 0,
SELECT = 1,
RESULT = 2,
LOADING = 0,
SELECT = 1,
RESULT = 2,
}
interface PostPoll {
author: string;
permlink: string;
metadata: PostMetadata;
author: string;
permlink: string;
metadata: PostMetadata;
}
export const PostPoll = ({
author,
permlink,
metadata
}: PostPoll) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const PostPoll = ({ author, permlink, metadata }: PostPoll) => {
if (metadata.content_type !== ContentType.POLL) {
return null;
}
const intl = useIntl();
const navigation = useNavigation();
if (metadata.content_type !== ContentType.POLL) {
return null;
const currentAccount = useAppSelector((state) => state.account.currentAccount);
const isLoggedIn = useAppSelector((state) => state.application.isLoggedIn);
const [selection, setSelection] = useState<number[]>([]);
const [mode, setMode] = useState(PollModes.LOADING);
const [interpretation, setInterpretation] = useState(
metadata.preferred_interpretation || PollPreferredInterpretation.NUMBER_OF_VOTES,
);
const _isModeSelect = mode === PollModes.SELECT;
const _isInterpretationToken = interpretation === PollPreferredInterpretation.TOKENS;
const _isPollAuthor = author === currentAccount?.username;
const pollsQuery = pollQueries.useGetPollQuery(author, permlink, metadata);
const votePollMutation = pollQueries.useVotePollMutation(pollsQuery.data);
const _accAgeLimit =
pollsQuery.data?.filter_account_age_days || metadata.filters?.account_age || 0;
const userVote = useMemo(() => {
if (pollsQuery.data) {
return pollsQuery.data.poll_voters.find((voter) => voter.name === currentAccount.username);
}
}, [pollsQuery.data?.poll_voters, currentAccount.username]);
const _expired = useMemo(
() => new Date(metadata.end_time * 1000).getTime() < new Date().getTime(),
[metadata],
);
const _hideVoters = useMemo(
() => metadata.hide_votes && !_isPollAuthor,
[metadata, _isPollAuthor],
);
const _voteDisabled = useMemo(() => {
const _ageLimitApllies =
currentAccount && _accAgeLimit
? getDaysPassedSince(currentAccount.created) < _accAgeLimit
: false;
const _noVoteChange =
metadata.vote_change !== undefined ? !metadata.vote_change && !!userVote : false;
return _expired || !isLoggedIn || _noVoteChange || _ageLimitApllies;
}, [metadata, userVote]);
useEffect(() => {
if (pollsQuery.isSuccess) {
setMode(!!userVote || _expired ? PollModes.RESULT : PollModes.SELECT);
setInterpretation(
pollsQuery.data?.preferred_interpretation || PollPreferredInterpretation.NUMBER_OF_VOTES,
);
}
}, [pollsQuery.isLoading, userVote]);
const _handleCastVote = () => {
votePollMutation.mutate({ choices: selection });
setSelection([]);
};
const _handleChoiceSelect = (choiceNum: number) => {
const _maxSelectable = pollsQuery.data?.max_choices_voted || 1;
if (_maxSelectable > 1) {
// handle multiple choice selection
const _maxSelected = pollsQuery.data
? selection.length >= pollsQuery.data?.max_choices_voted
: 1;
const index = selection.indexOf(choiceNum);
if (index >= 0) {
selection.splice(index, 1);
} else if (!_maxSelected) {
selection.push(choiceNum);
}
setSelection([...selection]);
} else {
// if only one choice allowed, overwrite selection
setSelection([choiceNum]);
}
};
const _handleModeToggle = () => {
setMode(_isModeSelect ? PollModes.RESULT : PollModes.SELECT);
};
const _handleVotersPress = (choiceNum: number) => {
const _voters = pollsQuery.data?.poll_voters;
if (!_voters) {
return;
}
const intl = useIntl();
const navigation = useNavigation();
const _filteredVoters = _voters
.filter((item) => item.choices.includes(choiceNum))
.map((voter) => ({ account: voter.name }));
navigation.navigate(ROUTES.MODALS.ACCOUNT_LIST, {
title: intl.formatMessage({ id: 'post_poll.voters' }),
users: _filteredVoters,
});
};
const currentAccount = useAppSelector(state => state.account.currentAccount)
const isLoggedIn = useAppSelector(state => state.application.isLoggedIn);
const _switchInterpretation = () => {
setInterpretation(
_isInterpretationToken
? PollPreferredInterpretation.NUMBER_OF_VOTES
: PollPreferredInterpretation.TOKENS,
);
};
const [selection, setSelection] = useState<number[]>([]);
const [mode, setMode] = useState(PollModes.LOADING)
const [interpretation, setInterpretation] = useState(metadata.preferred_interpretation || PollPreferredInterpretation.NUMBER_OF_VOTES);
const _authorPanel = _isPollAuthor && (
<View style={styles.authorPanel}>
<TextButton
text={intl.formatMessage({
id: _isModeSelect ? 'post_poll.view_stats' : 'post_poll.hide_stats',
})}
onPress={_handleModeToggle}
textStyle={styles.viewVotesBtn}
/>
const _isModeSelect = mode === PollModes.SELECT;
const _isInterpretationToken = interpretation === PollPreferredInterpretation.TOKENS;
const _isPollAuthor = author === currentAccount?.username;
{!_isModeSelect && (
<TextButton
text={intl.formatMessage({
id: _isInterpretationToken ? 'post_poll.interpret_vote' : 'post_poll.interpret_token',
})}
textStyle={styles.viewVotesBtn}
onPress={_switchInterpretation}
/>
)}
</View>
);
const pollsQuery = pollQueries.useGetPollQuery(author, permlink, metadata)
const votePollMutation = pollQueries.useVotePollMutation(pollsQuery.data);
const _accAgeLimit = pollsQuery.data?.filter_account_age_days || metadata.filters?.account_age || 0;
const _actionPanel = !_voteDisabled && (
<View style={styles.actionPanel}>
<MainButton
style={styles.voteButton}
iconName="chart"
iconType="SimpleLineIcons"
iconColor="white"
iconStyle={{ fontSize: 16 }}
onPress={_handleCastVote}
text="Vote"
isDisable={!selection.length}
/>
</View>
);
return (
<View style={styles.container}>
<PollHeader metadata={metadata} expired={_expired} />
const userVote = useMemo(() => {
if (pollsQuery.data) {
return pollsQuery.data.poll_voters.find(voter => voter.name === currentAccount.username)
}
}, [pollsQuery.data?.poll_voters, currentAccount.username])
<PollChoices
metadata={metadata}
choices={pollsQuery.data?.poll_choices}
userVote={userVote}
voteDisabled={_voteDisabled}
loading={pollsQuery.isLoading}
mode={mode}
selection={selection}
hideVoters={_hideVoters}
interpretationToken={interpretation === PollPreferredInterpretation.TOKENS}
handleChoiceSelect={_handleChoiceSelect}
handleVotersPress={_handleVotersPress}
/>
const _expired = useMemo(
() => new Date(metadata.end_time * 1000).getTime() < new Date().getTime(),
[metadata]);
const _hideVoters = useMemo(() => metadata.hide_votes && !_isPollAuthor, [metadata, _isPollAuthor]);
const _voteDisabled = useMemo(() => {
const _ageLimitApllies = currentAccount && _accAgeLimit
? getDaysPassedSince(currentAccount.created) < _accAgeLimit : false;
const _noVoteChange = metadata.vote_change !== undefined
? !metadata.vote_change && !!userVote
: false;
return _expired || !isLoggedIn || _noVoteChange || _ageLimitApllies
}, [metadata, userVote]);
useEffect(() => {
if (pollsQuery.isSuccess) {
setMode(!!userVote || _expired ? PollModes.RESULT : PollModes.SELECT);
setInterpretation(pollsQuery.data?.preferred_interpretation || PollPreferredInterpretation.NUMBER_OF_VOTES)
}
}, [pollsQuery.isLoading, userVote])
const _handleCastVote = () => {
votePollMutation.mutate({ choices: selection })
setSelection([]);
}
const _handleChoiceSelect = (choiceNum: number) => {
const _maxSelectable = pollsQuery.data?.max_choices_voted || 1
if (_maxSelectable > 1) {
//handle multiple choice selection
const _maxSelected = pollsQuery.data
? selection.length >= pollsQuery.data?.max_choices_voted : 1
const index = selection.indexOf(choiceNum)
if (index >= 0) {
selection.splice(index, 1);
} else if (!_maxSelected) {
selection.push(choiceNum)
}
setSelection([...selection]);
} else {
//if only one choice allowed, overwrite selection
setSelection([choiceNum]);
}
}
const _handleModeToggle = () => {
setMode(_isModeSelect ? PollModes.RESULT : PollModes.SELECT);
}
const _handleVotersPress = (choiceNum: number) => {
const _voters = pollsQuery.data?.poll_voters;
if (!_voters) {
return;
}
const _filteredVoters = _voters
.filter(item => item.choices.includes(choiceNum))
.map(voter => ({ account: voter.name }))
navigation.navigate(ROUTES.MODALS.ACCOUNT_LIST, {
title: intl.formatMessage({ id: 'post_poll.voters' }),
users: _filteredVoters,
});
}
const _switchInterpretation = () => {
setInterpretation(_isInterpretationToken
? PollPreferredInterpretation.NUMBER_OF_VOTES
: PollPreferredInterpretation.TOKENS
)
}
const _authorPanel = _isPollAuthor && (
<View style={styles.authorPanel}>
<TextButton
text={intl.formatMessage({
id: _isModeSelect ? "post_poll.view_stats" : "post_poll.hide_stats"
})}
onPress={_handleModeToggle}
textStyle={styles.viewVotesBtn} />
{!_isModeSelect && (
<TextButton
text={intl.formatMessage({
id: _isInterpretationToken ? "post_poll.interpret_vote" : "post_poll.interpret_token"
})}
textStyle={styles.viewVotesBtn}
onPress={_switchInterpretation} />
)}
</View>
)
const _actionPanel = !_voteDisabled && (
<View style={styles.actionPanel}>
<MainButton
style={styles.voteButton}
iconName="chart"
iconType="SimpleLineIcons"
iconColor="white"
iconStyle={{ fontSize: 16 }}
onPress={_handleCastVote}
text={"Vote"}
isDisable={!selection.length}
/>
</View>
)
return (
<View style={styles.container}>
<PollHeader
metadata={metadata}
expired={_expired} />
<PollChoices
metadata={metadata}
choices={pollsQuery.data?.poll_choices}
userVote={userVote}
voteDisabled={_voteDisabled}
loading={pollsQuery.isLoading}
mode={mode}
selection={selection}
hideVoters={_hideVoters}
interpretationToken={interpretation === PollPreferredInterpretation.TOKENS}
handleChoiceSelect={_handleChoiceSelect}
handleVotersPress={_handleVotersPress} />
{_authorPanel}
{_actionPanel}
</View>
)
}
{_authorPanel}
{_actionPanel}
</View>
);
};

View File

@ -1 +1 @@
export * from './container/postPoll';
export * from './container/postPoll';

View File

@ -2,41 +2,42 @@ import { TextStyle, ViewStyle } from 'react-native';
import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
choiceWrapper: {
marginBottom: 8
},
progressBar: {
borderRadius: 12,
borderWidth: 2,
alignSelf: 'stretch',
marginHorizontal: 8,
} as ViewStyle,
progressContentWrapper: {
position: 'absolute',
left: 24, right: 24,
top: 0, bottom: 0,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between'
} as ViewStyle,
choiceLabelWrapper: {
flexDirection: 'row',
alignItems: 'center',
flexShrink:1
} as ViewStyle,
label: {
marginLeft: 8,
fontSize: 12,
color: '$primaryBlack',
flexShrink:1,
} as TextStyle,
count: {
fontSize: 12,
marginLeft: 8,
color: '$primaryDarkGray',
} as TextStyle,
checkContainerStyle: {
backgroundColor: '$white',
} as ViewStyle
});
choiceWrapper: {
marginBottom: 8,
},
progressBar: {
borderRadius: 12,
borderWidth: 2,
alignSelf: 'stretch',
marginHorizontal: 8,
} as ViewStyle,
progressContentWrapper: {
position: 'absolute',
left: 24,
right: 24,
top: 0,
bottom: 0,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
} as ViewStyle,
choiceLabelWrapper: {
flexDirection: 'row',
alignItems: 'center',
flexShrink: 1,
} as ViewStyle,
label: {
marginLeft: 8,
fontSize: 12,
color: '$primaryBlack',
flexShrink: 1,
} as TextStyle,
count: {
fontSize: 12,
marginLeft: 8,
color: '$primaryDarkGray',
} as TextStyle,
checkContainerStyle: {
backgroundColor: '$white',
} as ViewStyle,
});

View File

@ -2,33 +2,31 @@ import { TextStyle, ViewStyle } from 'react-native';
import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
headerWrapper: {
flexWrap: 'wrap',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginHorizontal: 8,
marginBottom: 12
} as ViewStyle,
question: {
fontSize: 16,
fontWeight: '600',
color: "$primaryBlack",
} as TextStyle,
timeContainer:{
flexDirection:'row',
alignItems:'center'
} as ViewStyle,
clockIcon:{
marginLeft:4
},
timeText:{
color:'$primaryDarkText',
} as TextStyle,
subText:{
color:'$primaryDarkText',
marginBottom:8,
} as TextStyle
});
headerWrapper: {
flexWrap: 'wrap',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginHorizontal: 8,
marginBottom: 12,
} as ViewStyle,
question: {
fontSize: 16,
fontWeight: '600',
color: '$primaryBlack',
} as TextStyle,
timeContainer: {
flexDirection: 'row',
alignItems: 'center',
} as ViewStyle,
clockIcon: {
marginLeft: 4,
},
timeText: {
color: '$primaryDarkText',
} as TextStyle,
subText: {
color: '$primaryDarkText',
marginBottom: 8,
} as TextStyle,
});

View File

@ -2,58 +2,60 @@ import { TextStyle, ViewStyle } from 'react-native';
import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
container: {
marginTop:16,
borderRadius: 12,
borderWidth: EStyleSheet.hairlineWidth,
borderColor: '$iconColor',
padding:8
} as ViewStyle,
headerWrapper:{
flexWrap:'wrap',
flexDirection:'row',
alignItems:'center',
justifyContent:'space-between',
marginHorizontal:8,
marginBottom:12
} as ViewStyle,
question:{
fontSize:16,
fontWeight:'600',
color: "$primaryBlack",
} as TextStyle,
countdonw:{
fontSize:12,
color: "$primaryBlack",
} as TextStyle,
optionsTextWrapper: {
position: 'absolute',
left: 24, right: 24,
top: 0, bottom: 0,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between'
} as ViewStyle,
authorPanel:{
flexDirection:'row',
alignItems:'center',
justifyContent:'space-between',
marginBottom:8
} as ViewStyle,
actionPanel:{
flexDirection:'row-reverse',
alignItems:'center',
} as ViewStyle,
voteButton:{
width: 140,
height: 44,
alignItems:'center',
justifyContent:'center'
} as ViewStyle,
viewVotesBtn:{
color: '$primaryDarkGray',
fontSize: 14,
marginHorizontal:8,
fontWeight: '500',
} as ViewStyle
});
container: {
marginTop: 16,
borderRadius: 12,
borderWidth: EStyleSheet.hairlineWidth,
borderColor: '$iconColor',
padding: 8,
} as ViewStyle,
headerWrapper: {
flexWrap: 'wrap',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginHorizontal: 8,
marginBottom: 12,
} as ViewStyle,
question: {
fontSize: 16,
fontWeight: '600',
color: '$primaryBlack',
} as TextStyle,
countdonw: {
fontSize: 12,
color: '$primaryBlack',
} as TextStyle,
optionsTextWrapper: {
position: 'absolute',
left: 24,
right: 24,
top: 0,
bottom: 0,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
} as ViewStyle,
authorPanel: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: 8,
} as ViewStyle,
actionPanel: {
flexDirection: 'row-reverse',
alignItems: 'center',
} as ViewStyle,
voteButton: {
width: 140,
height: 44,
alignItems: 'center',
justifyContent: 'center',
} as ViewStyle,
viewVotesBtn: {
color: '$primaryDarkGray',
fontSize: 14,
marginHorizontal: 8,
fontWeight: '500',
} as ViewStyle,
});

View File

@ -67,14 +67,14 @@ const PostDisplayContainer = ({
};
const _handleOnReblogsPress = () => {
navigation.navigate({
name: ROUTES.SCREENS.REBLOGS,
params: {
author,
permlink,
},
key: post.permlink + post.reblogs.length,
} as never);
navigation.navigate({
name: ROUTES.SCREENS.REBLOGS,
params: {
author,
permlink,
},
key: post.permlink + post.reblogs.length,
} as never);
};
const _handleOnReplyPress = () => {

View File

@ -7,6 +7,7 @@ import get from 'lodash/get';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
// Utils
import { useQueryClient } from '@tanstack/react-query';
import { getTimeFromNow } from '../../../utils/time';
// Components
@ -28,7 +29,6 @@ import { PostComments } from '../../postComments';
import { UpvoteButton } from '../../postCard/children/upvoteButton';
import UpvotePopover from '../../upvotePopover';
import { PostPoll } from '../../postPoll';
import { useQueryClient } from '@tanstack/react-query';
import QUERIES from '../../../providers/queries/queryKeys';
const WIDTH = getWindowDimensions().width;
@ -284,11 +284,8 @@ const PostDisplayView = ({
metadata={post.json_metadata}
onLoadEnd={_handleOnPostBodyLoad}
/>
<PostPoll
author={author}
permlink={permlink}
metadata={post.json_metadata} />
<PostPoll author={author} permlink={permlink} metadata={post.json_metadata} />
{!postBodyLoading && (
<View style={styles.footer}>
@ -299,7 +296,7 @@ const PostDisplayView = ({
{
username: author || post.author,
appname: post?.json_metadata?.app
? capitalize((post?.json_metadata?.app).split('/')[0])
? capitalize(post?.json_metadata?.app?.split('/')[0])
: 'Ecency',
},
)}

View File

@ -1,12 +1,11 @@
import React from 'react';
import { View, TouchableOpacity, Text, Platform, ActivityIndicator } from 'react-native';
import { View, TouchableOpacity, Text, Platform } from 'react-native';
import Animated, { BounceInRight } from 'react-native-reanimated';
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import { injectIntl, useIntl } from 'react-intl';
// Images
import { Image as ExpoImage } from 'expo-image';
import EStyleSheet from 'react-native-extended-stylesheet';
import LIGHT_COVER_IMAGE from '../../assets/default_cover_image.png';
import DARK_COVER_IMAGE from '../../assets/dark_cover_image.png';
@ -77,7 +76,7 @@ const ProfileEditFormView = ({
}
/>
<IconButton
color={'white'}
color="white"
isLoading={isUploading}
iconStyle={styles.addIcon}
style={styles.addButton}

View File

@ -13,6 +13,7 @@ import get from 'lodash/get';
// Constants
import { Image as ExpoImage } from 'expo-image';
import EStyleSheet from 'react-native-extended-stylesheet';
import { proxifyImageSrc } from '@ecency/render-helper';
import LIGHT_COVER_IMAGE from '../../../assets/default_cover_image.png';
import DARK_COVER_IMAGE from '../../../assets/dark_cover_image.png';
@ -23,7 +24,6 @@ import { DropdownButton } from '../../dropdownButton';
// Utils
import { makeCountFriendly } from '../../../utils/formatter';
import { proxifyImageSrc } from '@ecency/render-helper';
// Styles
import styles from './profileSummaryStyles';

View File

@ -6,6 +6,7 @@ import { useIntl } from 'react-intl';
import { check, request, PERMISSIONS, RESULTS, openSettings } from 'react-native-permissions';
import { get } from 'lodash';
import * as hiveuri from 'hive-uri';
import { useCameraDevice, Camera, useCodeScanner } from 'react-native-vision-camera';
import styles from './qrModalStyles';
import { useAppDispatch, useAppSelector } from '../../hooks';
import {
@ -25,7 +26,6 @@ import showLoginAlert from '../../utils/showLoginAlert';
import authType from '../../constants/authType';
import { delay } from '../../utils/editor';
import ROUTES from '../../constants/routeNames';
import {useCameraDevice, Camera, useCodeScanner} from 'react-native-vision-camera';
const screenHeight = getWindowDimensions().height;
@ -46,10 +46,10 @@ export const QRModal = () => {
const codeScanner = useCodeScanner({
codeTypes: ['qr'],
onCodeScanned: (codes) => {
console.log(`Scanned ${codes.length} codes!`, codes)
handleLink({data:codes[0].value});
}
})
console.log(`Scanned ${codes.length} codes!`, codes);
handleLink({ data: codes[0].value });
},
});
// TODO: make sure to properly clean uri processing code to process uri from deep links and notifications
const deepLinkToHandle = useAppSelector((state) => state.ui.deepLinkToHandle);
@ -291,7 +291,6 @@ export const QRModal = () => {
);
};
return (
<ActionSheet
ref={sheetModalRef}
@ -301,7 +300,7 @@ export const QRModal = () => {
indicatorStyle={styles.indicator}
>
<View style={styles.mainContainer}>
<Camera
<Camera
style={EStyleSheet.absoluteFill}
device={device}
isActive={isScannerActive}

View File

@ -145,8 +145,7 @@ export const QuickReplyModalContent = forwardRef(
// handle submit reply
const _submitPost = async () => {
if(isSubmitting){
if (isSubmitting) {
return;
}

View File

@ -28,4 +28,4 @@ export default EStyleSheet.create({
},
elevation: 3,
},
});
});

View File

@ -71,4 +71,4 @@ const ToggleSwitchView = ({ onColor, offColor, latchBack, onToggle, ...props }:
);
};
export default ToggleSwitchView;
export default ToggleSwitchView;

View File

@ -65,7 +65,7 @@ const UploadsGalleryContent = ({
animatedHeight.value = withTiming(COMPACT_HEIGHT, {
easing: Easing.inOut(Easing.cubic),
});
}, [])
}, []);
const isDeleting =
mode === Modes.MODE_IMAGE
@ -247,8 +247,8 @@ const UploadsGalleryContent = ({
{mode === Modes.MODE_IMAGE
? _renderSelectButtons
: isAddingToUploads
? _renderSelectButton('progress-upload', 'Uploading', handleOpenSpeakUploader)
: _renderSelectButtons}
? _renderSelectButton('progress-upload', 'Uploading', handleOpenSpeakUploader)
: _renderSelectButtons}
</View>
<View style={styles.pillBtnContainer}>
<IconButton
@ -382,4 +382,4 @@ const UploadsGalleryContent = ({
);
};
export default UploadsGalleryContent;
export default UploadsGalleryContent;

View File

@ -1,11 +1,11 @@
//This document lets us mark which wallet operations can be repeatable
//directly from transaction/activities list
// This document lets us mark which wallet operations can be repeatable
// directly from transaction/activities list
export const RepeatableTransfers = {
//Hive transfers
"transfer": true,
//Ecency transfer
"outgoing_transfer_title": true,
//Engine transfers
"tokens_transfer": true,
}
// Hive transfers
transfer: true,
// Ecency transfer
outgoing_transfer_title: true,
// Engine transfers
tokens_transfer: true,
};

View File

@ -78,10 +78,11 @@ const MainStackNavigator = () => {
component={AssetsSelect}
options={{ presentation: 'modal' }}
/>
<MainStack.Screen
name={ROUTES.MODALS.ACCOUNT_LIST}
component={AccountList}
options={{ presentation: 'modal' }}/>
<MainStack.Screen
name={ROUTES.MODALS.ACCOUNT_LIST}
component={AccountList}
options={{ presentation: 'modal' }}
/>
</MainStack.Group>
</MainStack.Navigator>
);

View File

@ -966,8 +966,7 @@ export const getCommentHistory = async (
export const getAnnouncements = async (accessToken: string) => {
try {
const params = accessToken ? { code:accessToken } : null
const params = accessToken ? { code: accessToken } : null;
const res = await ecencyApi.get('/private-api/announcements', { params });
console.log('announcements fetcehd', res.data);

View File

@ -333,22 +333,22 @@ export const getUpdatedUserData = (userData, data) => {
: get(userData, 'masterKey', ''),
postingKey:
get(userData, 'authType', '') === AUTH_TYPE.MASTER_KEY ||
get(userData, 'authType', '') === AUTH_TYPE.POSTING_KEY
get(userData, 'authType', '') === AUTH_TYPE.POSTING_KEY
? encryptKey(get(privateKeys, 'postingKey', '').toString(), get(data, 'pinCode'))
: get(userData, 'postingKey', ''),
activeKey:
get(userData, 'authType', '') === AUTH_TYPE.MASTER_KEY ||
get(userData, 'authType', '') === AUTH_TYPE.ACTIVE_KEY
get(userData, 'authType', '') === AUTH_TYPE.ACTIVE_KEY
? encryptKey(get(privateKeys, 'activeKey', '').toString(), get(data, 'pinCode'))
: get(userData, 'activeKey', ''),
memoKey:
get(userData, 'authType', '') === AUTH_TYPE.MASTER_KEY ||
get(userData, 'authType', '') === AUTH_TYPE.MEMO_KEY
get(userData, 'authType', '') === AUTH_TYPE.MEMO_KEY
? encryptKey(get(privateKeys, 'memoKey', '').toString(), get(data, 'pinCode'))
: get(userData, 'memoKey', ''),
ownerKey:
get(userData, 'authType', '') === AUTH_TYPE.MASTER_KEY ||
get(userData, 'authType', '') === AUTH_TYPE.OWNER_KEY
get(userData, 'authType', '') === AUTH_TYPE.OWNER_KEY
? encryptKey(get(privateKeys, 'ownerKey', '').toString(), get(data, 'pinCode'))
: get(userData, 'ownerKey', ''),
};
@ -382,25 +382,23 @@ export const getUpdatedUserKeys = async (currentAccountData, data) => {
}
});
if (loginFlag) {
const _prevAuthType = currentAccountData.authType
const _prevAuthType = currentAccountData.authType;
const _localData = {
...currentAccountData.local,
authType
}
const _userData = getUpdatedUserData(_localData, data)
authType,
};
const _userData = getUpdatedUserData(_localData, data);
//sustain appropriate authType;
if(_prevAuthType === AUTH_TYPE.STEEM_CONNECT){
// sustain appropriate authType;
if (_prevAuthType === AUTH_TYPE.STEEM_CONNECT) {
_userData.authType = _prevAuthType;
}
await setUserData(_userData);
currentAccountData.local = _userData
currentAccountData.local = _userData;
return currentAccountData;
}
return Promise.reject(new Error('auth.invalid_credentials'));

View File

@ -371,7 +371,7 @@ export const getUser = async (user, loggedIn = true) => {
getCache('rcPower');
await setCache('rcPower', rcPower);
_account.reputation = await getUserReputation(user)
_account.reputation = await getUserReputation(user);
_account.username = _account.name;
_account.unread_activity_count = unreadActivityCount;
_account.vp_manabar = client.rc.calculateVPMana(_account);
@ -422,13 +422,11 @@ export const getUserReputation = async (author) => {
};
return parseReputation(_account.reputation);
} catch (error) {
bugsnagInstance.notify(error)
bugsnagInstance.notify(error);
return 0;
}
}
};
const cache = {};
const patt = /hive-\d\w+/g;
@ -746,11 +744,11 @@ export const getPost = async (author, permlink, currentUserName = null, isPromot
try {
console.log('Getting post: ', author, permlink);
const post = await client.call('bridge', 'get_post', { author, permlink });
console.log("post fetched", post?.post_id)
console.log('post fetched', post?.post_id);
return post ? parsePost(post, currentUserName, isPromoted) : null;
} catch (error) {
console.warn(error)
bugsnagInstance.notify(error)
console.warn(error);
bugsnagInstance.notify(error);
return error;
}
};
@ -1759,25 +1757,20 @@ export const reblog = (account, pinCode, author, permlink, undo = false) =>
return resp;
});
const _reblog = async (account, pinCode, author, permlink, undo = false) => {
const json = [
'reblog',
{
account: account.name,
author,
permlink,
delete: undo ? 'delete' : undefined
delete: undo ? 'delete' : undefined,
},
];
return broadcastPostingJSON('follow', json, account, pinCode)
return broadcastPostingJSON('follow', json, account, pinCode);
};
export const claimRewardBalance = (account, pinCode, rewardHive, rewardHbd, rewardVests) => {
const pin = getDigitPinCode(pinCode);
const key = getAnyPrivateKey(get(account, 'local'), pin);

View File

@ -5,11 +5,11 @@ export enum ContentType {
export enum PollPreferredInterpretation {
NUMBER_OF_VOTES = 'number_of_votes',
TOKENS = 'tokens'
TOKENS = 'tokens',
}
export interface PostMetadata {
//GENERAL
// GENERAL
tags: string[];
token: string;
content_type: ContentType;
@ -18,23 +18,22 @@ export interface PostMetadata {
version: number;
app: string;
//IMAGE
// IMAGE
image: string[];
image_ratios: number[];
//POLL
// POLL
question: string;
preferred_interpretation: PollPreferredInterpretation;
max_choices_voted: number;
choices: string[];
filters: {
account_age: number
}
account_age: number;
};
end_time: number;
//ECENCY SPECIFIC POLL OPTIONS
// ECENCY SPECIFIC POLL OPTIONS
vote_change: boolean;
hide_votes: boolean;
}
export interface Vote {
@ -155,4 +154,3 @@ export interface TransferDataType {
amount: string;
memo?: string;
}

View File

@ -1,114 +1,115 @@
import { Poll, PollChoice, PollStats, PollVoter } from "./polls.types";
import { Poll, PollChoice, PollStats, PollVoter } from './polls.types';
//conveter generated by ChatGPT
// conveter generated by ChatGPT
export const convertPoll = (rawData: any): Poll | null => {
if (!rawData) return null;
if (!rawData) return null;
const {
post_title,
post_body,
author,
created,
permlink,
parent_permlink,
tags,
image,
protocol_version,
question,
preferred_interpretation,
token,
end_time,
status,
max_choices_voted,
filter_account_age_days,
ui_hide_res_until_voted,
platform,
poll_trx_id,
poll_choices,
poll_voters,
poll_stats
} = rawData;
const {
post_title,
post_body,
author,
created,
permlink,
parent_permlink,
tags,
image,
protocol_version,
question,
preferred_interpretation,
token,
end_time,
status,
max_choices_voted,
filter_account_age_days,
ui_hide_res_until_voted,
platform,
poll_trx_id,
poll_choices,
poll_voters,
poll_stats,
} = rawData;
// Ensure required properties are present
if (!question || !end_time || !poll_choices || !poll_stats) {
return null;
}
// Ensure required properties are present
if (!question || !end_time || !poll_choices || !poll_stats) {
return null;
}
// Parsing poll choices
const parsedPollChoices: PollChoice[] = poll_choices.map((choice: any) => ({
choice_num: choice.choice_num,
choice_text: choice.choice_text,
votes: {
total_votes: choice.votes?.total_votes || 0,
hive_hp: choice.votes?.hive_hp || 0,
hive_proxied_hp: choice.votes?.hive_proxied_hp || 0,
hive_hp_incl_proxied: choice.votes?.hive_hp_incl_proxied || 0
}
}));
// Parsing poll choices
const parsedPollChoices: PollChoice[] = poll_choices.map((choice: any) => ({
choice_num: choice.choice_num,
choice_text: choice.choice_text,
votes: {
total_votes: choice.votes?.total_votes || 0,
hive_hp: choice.votes?.hive_hp || 0,
hive_proxied_hp: choice.votes?.hive_proxied_hp || 0,
hive_hp_incl_proxied: choice.votes?.hive_hp_incl_proxied || 0,
},
}));
// Parsing poll voters
const parsedPollVoters: PollVoter[] = poll_voters ? poll_voters.map((voter: any) => ({
// Parsing poll voters
const parsedPollVoters: PollVoter[] = poll_voters
? poll_voters.map((voter: any) => ({
name: voter.name,
choices: voter.choices,
hive_hp: voter.hive_hp || 0,
hive_proxied_hp: voter.hive_proxied_hp || 0,
hive_hp_incl_proxied: voter.hive_hp_incl_proxied || 0
})) : [];
hive_hp_incl_proxied: voter.hive_hp_incl_proxied || 0,
}))
: [];
// Parsing poll stats
const parsedPollStats: PollStats = {
total_voting_accounts_num: poll_stats.total_voting_accounts_num || 0,
total_hive_hp: poll_stats.total_hive_hp || 0,
total_hive_proxied_hp: poll_stats.total_hive_proxied_hp || 0,
total_hive_hp_incl_proxied: poll_stats.total_hive_hp_incl_proxied || 0,
};
// Parsing poll stats
const parsedPollStats: PollStats = {
total_voting_accounts_num: poll_stats.total_voting_accounts_num || 0,
total_hive_hp: poll_stats.total_hive_hp || 0,
total_hive_proxied_hp: poll_stats.total_hive_proxied_hp || 0,
total_hive_hp_incl_proxied: poll_stats.total_hive_hp_incl_proxied || 0
};
// Constructing the parsed poll response
const parsedResponse: Poll = {
post_title: post_title || "",
post_body: post_body || "",
author,
created,
permlink,
parent_permlink,
tags: tags || [],
image: image || [],
protocol_version: protocol_version || 0,
question,
preferred_interpretation,
token,
end_time,
status,
max_choices_voted: max_choices_voted || 1,
filter_account_age_days: filter_account_age_days || 0,
ui_hide_res_until_voted, // Assuming this field can be null, otherwise adjust accordingly
platform, // Assuming this field can be null, otherwise adjust accordingly
poll_trx_id,
poll_choices: parsedPollChoices,
poll_voters: parsedPollVoters,
poll_stats: parsedPollStats
};
return parsedResponse;
}
// Constructing the parsed poll response
const parsedResponse: Poll = {
post_title: post_title || '',
post_body: post_body || '',
author,
created,
permlink,
parent_permlink,
tags: tags || [],
image: image || [],
protocol_version: protocol_version || 0,
question,
preferred_interpretation,
token,
end_time,
status,
max_choices_voted: max_choices_voted || 1,
filter_account_age_days: filter_account_age_days || 0,
ui_hide_res_until_voted, // Assuming this field can be null, otherwise adjust accordingly
platform, // Assuming this field can be null, otherwise adjust accordingly
poll_trx_id,
poll_choices: parsedPollChoices,
poll_voters: parsedPollVoters,
poll_stats: parsedPollStats,
};
return parsedResponse;
};
export const mapMetaChoicesToPollChoices = (metaChoices: string[]) => {
if (!metaChoices) {
return [] as PollChoice[]
}
if (!metaChoices) {
return [] as PollChoice[];
}
return metaChoices.map(((choice, index) => ({
return metaChoices.map(
(choice, index) =>
({
choice_num: index + 1,
choice_text: choice,
votes: {
total_votes: 0,
hive_hp: 0,
hive_proxied_hp: 0,
hive_hp_incl_proxied: 0,
}
} as PollChoice)))
}
total_votes: 0,
hive_hp: 0,
hive_proxied_hp: 0,
hive_hp_incl_proxied: 0,
},
} as PollChoice),
);
};

View File

@ -1,109 +1,113 @@
import axios from "axios";
import axios from 'axios';
import { Operation, PrivateKey } from '@esteemapp/dhive';
import bugsnagInstance from '../../config/bugsnag';
import { Poll } from "./polls.types";
import { convertPoll } from "./converters";
import { getActiveKey, getDigitPinCode, sendHiveOperations } from "../hive/dhive";
import { Operation, PrivateKey } from "@esteemapp/dhive";
import { Poll } from './polls.types';
import { convertPoll } from './converters';
import { getActiveKey, getDigitPinCode, sendHiveOperations } from '../hive/dhive';
/**
*
*
* swapper importable api url
* https://polls-beta.hivehub.dev/
*
*
* hive polls docs reference:
* https://gitlab.com/peakd/hive-open-polls
*
*
*/
const POLLS_BASE_URL = 'https://polls.hivehub.dev/';
const PATH_RPC = 'rpc'
const PATH_POLL = 'poll'
const PATH_RPC = 'rpc';
const PATH_POLL = 'poll';
const pollsApi = axios.create({
baseURL: POLLS_BASE_URL,
baseURL: POLLS_BASE_URL,
});
const executePollAction = (id: string, json: any, currentAccount: any, pinHash: string) => {
const pin = getDigitPinCode(pinHash);
const key = getActiveKey(currentAccount.local, pin);
const username = currentAccount.name;
const pin = getDigitPinCode(pinHash);
const key = getActiveKey(currentAccount.local, pin);
const username = currentAccount.name;
if (key) {
const privateKey = PrivateKey.fromString(key);
if (key) {
const privateKey = PrivateKey.fromString(key);
const op = {
id,
json: JSON.stringify(json),
required_auths: [username],
required_posting_auths: [],
};
const opArray: Operation[] = [['custom_json', op]];
return sendHiveOperations(opArray, privateKey);
}
const op = {
id,
json: JSON.stringify(json),
required_auths: [username],
required_posting_auths: [],
};
const opArray: Operation[] = [['custom_json', op]];
return sendHiveOperations(opArray, privateKey);
}
return Promise.reject(
new Error('Check private key permission! Required private active key or above.'),
);
return Promise.reject(
new Error('Check private key permission! Required private active key or above.'),
);
};
export const getPollData = async (author: string, permlink: string): Promise<Poll> => {
try {
if (!author || !permlink) {
throw new Error("author and permlink are requied for fetching polls data");
}
//prefix (eq.) is a requirement from api implementation
const params = {
author: `eq.${author}`,
permlink: `eq.${permlink}`
}
const res = await pollsApi.get(`/${PATH_RPC}/${PATH_POLL}`, { params });
console.log('poll data fetcehd', res.data);
if (!res.data || !res.data[0]) {
throw new Error('No poll data found!');
}
const data = convertPoll(res.data[0]);
if (!data) {
throw new Error('Failed to parse poll resposne data');
}
return data
} catch (error) {
bugsnagInstance.notify(error);
throw error;
try {
if (!author || !permlink) {
throw new Error('author and permlink are requied for fetching polls data');
}
// prefix (eq.) is a requirement from api implementation
const params = {
author: `eq.${author}`,
permlink: `eq.${permlink}`,
};
const res = await pollsApi.get(`/${PATH_RPC}/${PATH_POLL}`, { params });
console.log('poll data fetcehd', res.data);
if (!res.data || !res.data[0]) {
throw new Error('No poll data found!');
}
const data = convertPoll(res.data[0]);
if (!data) {
throw new Error('Failed to parse poll resposne data');
}
return data;
} catch (error) {
bugsnagInstance.notify(error);
throw error;
}
};
export const castPollVote = async (postId: string, choices: number[], currentAccount: any, pinHash: string) => {
try {
if (!postId || !currentAccount) {
throw new Error("Failed to register vote")
}
if (!choices || !choices.length) {
throw new Error("Invalid vote")
}
await executePollAction("polls", {
poll: postId,
action: "vote",
choices: choices
}, currentAccount, pinHash);
return true;
} catch (error) {
bugsnagInstance.notify(error);
throw error;
export const castPollVote = async (
postId: string,
choices: number[],
currentAccount: any,
pinHash: string,
) => {
try {
if (!postId || !currentAccount) {
throw new Error('Failed to register vote');
}
}
if (!choices || !choices.length) {
throw new Error('Invalid vote');
}
await executePollAction(
'polls',
{
poll: postId,
action: 'vote',
choices,
},
currentAccount,
pinHash,
);
return true;
} catch (error) {
bugsnagInstance.notify(error);
throw error;
}
};

View File

@ -1,54 +1,53 @@
import { PollPreferredInterpretation } from "../hive/hive.types";
import { PollPreferredInterpretation } from '../hive/hive.types';
//types generated by ChatGPT
// types generated by ChatGPT
export interface PollChoice {
choice_num: number;
choice_text: string;
votes: {
total_votes: number;
hive_hp: number;
hive_proxied_hp: number;
hive_hp_incl_proxied: number;
};
}
export interface PollVoter {
name: string;
choices: number[];
choice_num: number;
choice_text: string;
votes: {
total_votes: number;
hive_hp: number;
hive_proxied_hp: number;
hive_hp_incl_proxied: number;
};
}
export interface PollVoter {
name: string;
choices: number[];
hive_hp: number;
hive_proxied_hp: number;
hive_hp_incl_proxied: number;
}
export interface PollStats {
total_voting_accounts_num: number;
total_hive_hp: number;
total_hive_proxied_hp: number;
total_hive_hp_incl_proxied: number;
total_voting_accounts_num: number;
total_hive_hp: number;
total_hive_proxied_hp: number;
total_hive_hp_incl_proxied: number;
}
export interface Poll {
post_title: string;
post_body: string;
author: string;
created: string;
permlink: string;
parent_permlink: string;
tags: string[];
image: any[]; // Adjust this type according to your data structure
protocol_version: number;
question: string;
preferred_interpretation: PollPreferredInterpretation;
token: string;
end_time: string;
max_choices_voted: number;
status: string;
filter_account_age_days: number;
ui_hide_res_until_voted: any; // Adjust this type according to your data structure
platform: any; // Adjust this type according to your data structure
poll_trx_id: string;
poll_choices: PollChoice[];
poll_voters: PollVoter[];
poll_stats: PollStats;
}
post_title: string;
post_body: string;
author: string;
created: string;
permlink: string;
parent_permlink: string;
tags: string[];
image: any[]; // Adjust this type according to your data structure
protocol_version: number;
question: string;
preferred_interpretation: PollPreferredInterpretation;
token: string;
end_time: string;
max_choices_voted: number;
status: string;
filter_account_age_days: number;
ui_hide_res_until_voted: any; // Adjust this type according to your data structure
platform: any; // Adjust this type according to your data structure
poll_trx_id: string;
poll_choices: PollChoice[];
poll_voters: PollVoter[];
poll_stats: PollStats;
}

View File

@ -2,8 +2,8 @@ import { useQuery } from '@tanstack/react-query';
import { useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { useEffect, useMemo } from 'react';
import { getAnnouncements } from '../ecency/ecency';
import VersionNumber from 'react-native-version-number';
import { getAnnouncements } from '../ecency/ecency';
import QUERIES from './queryKeys';
import { useAppSelector } from '../../hooks';
import { updateAnnoucementsMeta } from '../../redux/actions/cacheActions';
@ -24,48 +24,42 @@ export const useAnnouncementsQuery = () => {
const pinHash = useAppSelector((state) => state.application.pin);
const lastAppVersion = useAppSelector((state) => state.application.lastAppVersion);
const appVersion = useMemo(() => VersionNumber.appVersion, [])
const appVersion = useMemo(() => VersionNumber.appVersion, []);
const currentAccount = useAppSelector((state) => state.account.currentAccount);
const announcementsMeta = useAppSelector((state) => state.cache.announcementsMeta);
const announcmentsQuery = useQuery([QUERIES.ANNOUNCEMENTS.GET], () => {
const encToken = currentAccount?.local?.accessToken;
const token = !!encToken && decryptKey(encToken, getDigitPinCode(pinHash));
return getAnnouncements(token)
return getAnnouncements(token);
});
useEffect(() => {
//bypass if it's first launch after new version install/update
// bypass if it's first launch after new version install/update
const _isNewVersionLaunch =
!lastAppVersion || parseVersionNumber(lastAppVersion) < parseVersionNumber(appVersion)
!lastAppVersion || parseVersionNumber(lastAppVersion) < parseVersionNumber(appVersion);
if (_isNewVersionLaunch) {
return;
}
//bypass if logged in user is required for announcement, skip otherwise
// bypass if logged in user is required for announcement, skip otherwise
const firstAnnounce = announcementsMeta.data && announcmentsQuery.data[0];
if (!firstAnnounce || (firstAnnounce?.auth && !currentAccount?.username)) {
return;
}
//prepare annoucmnet data
// prepare annoucmnet data
const _metaId = `${firstAnnounce.id}_${currentAccount?.username || 'guest'}`;
const _meta = announcementsMeta && announcementsMeta[_metaId];
const curTime = new Date().getTime();
//bypass if already processed or last prompt limit now expired
if (_meta?.processed ||
_meta?.lastSeen + PROMPT_AGAIN_INTERVAL > curTime) {
// bypass if already processed or last prompt limit now expired
if (_meta?.processed || _meta?.lastSeen + PROMPT_AGAIN_INTERVAL > curTime) {
return;
}
_showAnnouncement(firstAnnounce, _metaId);
}, [announcmentsQuery.data, currentAccount.username, lastAppVersion]);
const _showAnnouncement = async (data, metaId) => {
@ -111,4 +105,3 @@ export const useAnnouncementsQuery = () => {
);
};
};

View File

@ -55,7 +55,6 @@ export const useNotificationsQuery = (filter: NotificationFilters) => {
};
const _fetchNextPage = () => {
if (!_lastPage || _lastPage.isFetching) {
return;
}
@ -69,7 +68,6 @@ export const useNotificationsQuery = (filter: NotificationFilters) => {
const _dataArrs = notificationQueries.map((query) => query.data);
return {
data: unionBy(..._dataArrs, 'id'),
isRefreshing,

View File

@ -1,255 +1,249 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import QUERIES from "../queryKeys";
import { castPollVote, getPollData } from "../../polls/polls";
import { PostMetadata } from "../../hive/hive.types";
import { useEffect, useMemo, useState } from "react";
import { Poll, PollChoice } from "../../polls/polls.types";
import { useAppSelector } from "../../../hooks";
import parseToken from "../../../utils/parseToken";
import { vestsToHp } from "../../../utils/conversions";
import { updatePollVoteCache } from "../../../redux/actions/cacheActions";
import { useDispatch } from "react-redux";
import { CacheStatus, PollVoteCache } from "../../../redux/reducers/cacheReducer";
import { toastNotification } from "../../../redux/actions/uiAction";
import { useIntl } from "react-intl";
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useEffect, useMemo, useState } from 'react';
import { useDispatch } from 'react-redux';
import { useIntl } from 'react-intl';
import QUERIES from '../queryKeys';
import { castPollVote, getPollData } from '../../polls/polls';
import { PostMetadata } from '../../hive/hive.types';
import { Poll, PollChoice } from '../../polls/polls.types';
import { useAppSelector } from '../../../hooks';
import parseToken from '../../../utils/parseToken';
import { vestsToHp } from '../../../utils/conversions';
import { updatePollVoteCache } from '../../../redux/actions/cacheActions';
import { CacheStatus, PollVoteCache } from '../../../redux/reducers/cacheReducer';
import { toastNotification } from '../../../redux/actions/uiAction';
/** hook used to return post poll */
export const useGetPollQuery = (_author?: string, _permlink?: string, metadata?: PostMetadata) => {
const [author, setAuthor] = useState(_author);
const [permlink, setPermlink] = useState(_permlink);
const [author, setAuthor] = useState(_author);
const [permlink, setPermlink] = useState(_permlink);
// post process initial post if available
const _initialPollData = useMemo(() => {
// TODO: convert metadata to Poll data;
// post process initial post if available
const _initialPollData = useMemo(() => {
//TODO: convert metadata to Poll data;
return null;
}, [metadata]);
const query = useQuery(
[QUERIES.POST.GET_POLL, author, permlink],
async () => {
if (!author || !permlink) {
return null;
}, [metadata]);
}
const query = useQuery(
[QUERIES.POST.GET_POLL, author, permlink],
async () => {
if (!author || !permlink) {
return null;
}
try {
const pollData = await getPollData(author, permlink);
if (!pollData) {
new Error('Poll data unavailable');
}
try {
const pollData = await getPollData(author, permlink);
if (!pollData) {
new Error('Poll data unavailable');
}
return pollData;
} catch (err) {
console.warn('Failed to get post', err);
throw err;
}
},
{
initialData: _initialPollData,
cacheTime: 30 * 60 * 1000, // keeps cache for 30 minutes
},
);
return pollData
} catch (err) {
console.warn('Failed to get post', err);
throw err;
}
},
{
initialData: _initialPollData,
cacheTime: 30 * 60 * 1000, // keeps cache for 30 minutes
},
);
// TODO: use injectPollVoteCache here for simplifity and code reuseability
const data = useInjectPollVoteCache(query.data);
//TODO: use injectPollVoteCache here for simplifity and code reuseability
const data = useInjectPollVoteCache(query.data);
return {
...query,
data,
setAuthor,
setPermlink,
};
return {
...query,
data,
setAuthor,
setPermlink,
};
};
export function useVotePollMutation(poll: Poll | null) {
// const { activeUser } = useMappedStore();
const intl = useIntl();
const dispatch = useDispatch();
const queryClient = useQueryClient()
const currentAccount = useAppSelector(state => state.account.currentAccount);
const pollVotesCollection = useAppSelector(state => state.cache.pollVotesCollection);
const pinHash = useAppSelector(state => state.application.pin);
const globalProps = useAppSelector(state => state.account.globalProps)
// const { activeUser } = useMappedStore();
const intl = useIntl();
const dispatch = useDispatch();
const queryClient = useQueryClient();
const currentAccount = useAppSelector((state) => state.account.currentAccount);
const pollVotesCollection = useAppSelector((state) => state.cache.pollVotesCollection);
const pinHash = useAppSelector((state) => state.application.pin);
const globalProps = useAppSelector((state) => state.account.globalProps);
return useMutation({
mutationKey: [QUERIES.POST.SIGN_POLL_VOTE, poll?.author, poll?.permlink],
mutationFn: async ({ choices }: { choices: number[] }) => {
if (!poll || !currentAccount) {
throw new Error('Failed to register vote');
}
return useMutation({
mutationKey: [QUERIES.POST.SIGN_POLL_VOTE, poll?.author, poll?.permlink],
mutationFn: async ({ choices }: { choices: number[] }) => {
if (!(choices instanceof Array)) {
throw new Error('Invalid vote');
}
// eslint-disable-next-line no-return-await
return await castPollVote(poll.poll_trx_id, choices, currentAccount, pinHash);
},
retry: 3,
onMutate: ({ choices }) => {
// update redux
const userHp =
Math.round(
vestsToHp(parseToken(currentAccount.vesting_shares), globalProps.hivePerMVests) * 1000,
) / 1000;
const postPath = `${poll?.author || ''}/${poll?.permlink || ''}`;
const curTime = new Date().getTime();
const vote = {
choices,
userHp,
username: currentAccount.username,
votedAt: curTime,
expiresAt: curTime + 120000,
status: CacheStatus.PENDING,
} as PollVoteCache;
dispatch(updatePollVoteCache(postPath, vote));
},
if (!poll || !currentAccount) {
throw new Error("Failed to register vote")
}
onSuccess: (status) => {
console.log('vote response', status);
// update poll cache here
const postPath = `${poll?.author || ''}/${poll?.permlink || ''}`;
const voteCache: PollVoteCache = pollVotesCollection[postPath];
if (voteCache) {
voteCache.status = status ? CacheStatus.PUBLISHED : CacheStatus.FAILED;
dispatch(updatePollVoteCache(postPath, voteCache));
}
},
onError: (err) => {
// reverse mutation here
const postPath = `${poll?.author || ''}/${poll?.permlink || ''}`;
const voteCache: PollVoteCache = pollVotesCollection[postPath];
if (voteCache) {
voteCache.status = CacheStatus.FAILED;
dispatch(updatePollVoteCache(postPath, voteCache));
}
if (!(choices instanceof Array)) {
throw new Error("Invalid vote")
}
dispatch(toastNotification(`${intl.formateMessage({ id: 'alert.fail' })}. ${err.message}`));
return await castPollVote(poll.poll_trx_id, choices, currentAccount, pinHash)
},
retry: 3,
onMutate: ({ choices }) => {
// update redux
const userHp = Math.round(vestsToHp(parseToken(currentAccount.vesting_shares), globalProps.hivePerMVests) * 1000) / 1000;
const postPath = `${poll?.author || ''}/${poll?.permlink || ''}`;
const curTime = new Date().getTime();
const vote = {
choices,
userHp,
username: currentAccount.username,
votedAt: curTime,
expiresAt: curTime + 120000,
status: CacheStatus.PENDING,
} as PollVoteCache;
dispatch(updatePollVoteCache(postPath, vote));
},
onSuccess: (status) => {
console.log("vote response", status);
//update poll cache here
const postPath = `${poll?.author || ''}/${poll?.permlink || ''}`;
const voteCache: PollVoteCache = pollVotesCollection[postPath];
if (voteCache) {
voteCache.status = status ? CacheStatus.PUBLISHED : CacheStatus.FAILED;
dispatch(updatePollVoteCache(postPath, voteCache))
}
},
onError: (err) => {
//reverse mutation here
const postPath = `${poll?.author || ''}/${poll?.permlink || ''}`;
const voteCache: PollVoteCache = pollVotesCollection[postPath];
if (voteCache) {
voteCache.status = CacheStatus.FAILED
dispatch(updatePollVoteCache(postPath, voteCache))
}
dispatch(toastNotification(`${intl.formateMessage({ id: 'alert.fail' })}. ${err.message}`))
queryClient.invalidateQueries([QUERIES.POST.GET_POLL, poll?.author, poll?.permlink])
}
});
queryClient.invalidateQueries([QUERIES.POST.GET_POLL, poll?.author, poll?.permlink]);
},
});
}
//used to create, update and remove poll vote entry from votes data
// used to create, update and remove poll vote entry from votes data
const useInjectPollVoteCache = (pollData: Poll | null) => {
const pollVotesCollection = useAppSelector((state) => state.cache.pollVotesCollection);
const lastUpdate = useAppSelector((state) => state.cache.lastUpdate);
const [retData, setRetData] = useState<Poll | null>(null);
const pollVotesCollection = useAppSelector((state) => state.cache.pollVotesCollection);
const lastUpdate = useAppSelector((state) => state.cache.lastUpdate);
const [retData, setRetData] = useState<Poll | null>(null);
useEffect(() => {
if (pollData && lastUpdate && lastUpdate.type === 'poll-vote') {
const _postPath = lastUpdate.postPath;
const _voteCache = pollVotesCollection[_postPath];
useEffect(() => {
if (pollData && lastUpdate && lastUpdate.type === 'poll-vote') {
const _postPath = lastUpdate.postPath;
const _voteCache = pollVotesCollection[_postPath];
const _comparePath = (item) => _postPath === `${item.author}/${item.permlink}`;
const _pathMatched = pollData && _comparePath(pollData);
const _comparePath = (item) => _postPath === `${item.author}/${item.permlink}`;
let _pathMatched = pollData && _comparePath(pollData)
// if poll available, inject cache and update state
if (_pathMatched) {
console.log('Injection: on cache change: ', _voteCache);
const data = injectPollVoteCache(pollData, _voteCache);
// if poll available, inject cache and update state
if (_pathMatched) {
console.log("Injection: on cache change: ", _voteCache)
const data = injectPollVoteCache(pollData, _voteCache);
console.log('updating data', data);
setRetData({ ...data });
}
}
}, [pollVotesCollection]);
console.log('updating data', data);
setRetData({ ...data });
}
}
}, [pollVotesCollection]);
useEffect(() => {
if (!pollData) {
setRetData(null);
return;
}
useEffect(() => {
if (!pollData) {
setRetData(null);
return;
}
const _path = `${pollData.author}/${pollData.permlink}`;
const voteCache = pollVotesCollection[_path];
const _path = `${pollData.author}/${pollData.permlink}`;
const voteCache = pollVotesCollection[_path];
const _cData = injectPollVoteCache(pollData, voteCache);
const _cData = injectPollVoteCache(pollData, voteCache);
// check if data follows old schema, migrate if nesseary
if (_cData.poll_voters instanceof Array && !!_cData.poll_voters[0].choice_num) {
_cData.poll_voters = _cData.poll_voters.map((voter) => ({
...voter,
choices: [voter.choice_num],
}));
}
//check if data follows old schema, migrate if nesseary
if(_cData.poll_voters instanceof Array && !!_cData.poll_voters[0].choice_num){
_cData.poll_voters = _cData.poll_voters.map(voter => ({...voter, choices:[voter.choice_num]}))
}
setRetData(_cData);
}, [pollData]);
return retData || pollData;
}
setRetData(_cData);
}, [pollData]);
return retData || pollData;
};
const injectPollVoteCache = (data: Poll, voteCache: PollVoteCache) => {
if (!data || !voteCache) {
return data;
}
if (!data || !voteCache) {
return data;
}
const { userHp, choices, username, status } = voteCache;
const { userHp, choices, username, status } = voteCache;
if (status === CacheStatus.FAILED) {
return data;
}
if (status === CacheStatus.FAILED) {
return data;
}
// extract previously votes choices and new choices
const existingVote = data.poll_voters?.find((pv) => pv.name === username);
const previousUserChoices = data.poll_choices?.filter((pc) =>
existingVote?.choices.includes(pc.choice_num),
);
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
const selectedChoices = data.poll_choices.filter((pc) => choices.includes(pc.choice_num))!;
//extract previously votes choices and new choices
const existingVote = data.poll_voters?.find((pv) => pv.name === username);
const previousUserChoices = data.poll_choices?.filter((pc) => existingVote?.choices.includes(pc.choice_num));
const selectedChoices = data.poll_choices?.filter((pc) => choices.includes(pc.choice_num))!!;
// filtered list to separate untoched multiple choice e.g from old [1,2,3] new [3,4,5], removed would be [1, 2] , new would be [4, 5]
const removedChoices = previousUserChoices.filter(
(pc) => !selectedChoices.some((element) => element.choice_num === pc.choice_num),
);
const newChoices = selectedChoices.filter(
(pc) => !previousUserChoices.some((element) => element.choice_num === pc.choice_num),
);
//filtered list to separate untoched multiple choice e.g from old [1,2,3] new [3,4,5], removed would be [1, 2] , new would be [4, 5]
const removedChoices = previousUserChoices.filter(pc => !selectedChoices.some(element => element.choice_num === pc.choice_num));
const newChoices = selectedChoices.filter(pc => !previousUserChoices.some(element => element.choice_num === pc.choice_num));
// votes that were not affected by new vote
const notTouchedChoices = data.poll_choices?.filter(
(pc) =>
![
...removedChoices.map((pc) => pc.choice_num),
...newChoices.map((pc) => pc.choice_num),
].includes(pc.choice_num),
);
//votes that were not affected by new vote
const notTouchedChoices = data.poll_choices?.filter(
(pc) => ![
...removedChoices.map(pc => pc.choice_num),
...newChoices.map(pc => pc.choice_num)
].includes(pc.choice_num)
);
const otherVoters = data.poll_voters?.filter((pv) => pv.name !== username) ?? [];
const otherVoters =
data.poll_voters?.filter((pv) => pv.name !== username) ?? [];
let { poll_choices } = data;
poll_choices = [
...notTouchedChoices,
...removedChoices.map((pc) => ({
...pc,
votes: {
total_votes: (pc?.votes?.total_votes ?? 0) - 1,
hive_hp: (pc?.votes?.hive_hp ?? 0) - userHp,
hive_hp_incl_proxied: (pc?.votes?.hive_hp ?? 0) - userHp,
},
})),
...newChoices.map((pc) => ({
...pc,
votes: {
total_votes: (pc?.votes?.total_votes ?? 0) + 1,
hive_hp: (pc?.votes?.hive_hp ?? 0) + userHp,
hive_hp_incl_proxied: (pc?.votes?.hive_hp ?? 0) + userHp,
},
})),
]
.filter((el) => !!el)
.sort((a, b) => (a?.choice_num < b?.choice_num ? -1 : 1)) as PollChoice[];
let poll_choices = data.poll_choices;
poll_choices = [
...notTouchedChoices,
...removedChoices.map(pc => ({
...pc,
votes: {
total_votes: (pc?.votes?.total_votes ?? 0) - 1,
hive_hp: (pc?.votes?.hive_hp ?? 0) - userHp,
hive_hp_incl_proxied: (pc?.votes?.hive_hp ?? 0) - userHp
}
})),
...newChoices.map((pc => ({
...pc,
votes: {
total_votes: (pc?.votes?.total_votes ?? 0) + 1,
hive_hp: (pc?.votes?.hive_hp ?? 0) + userHp,
hive_hp_incl_proxied: (pc?.votes?.hive_hp ?? 0) + userHp
}
})))
].filter((el) => !!el).sort(((a, b) => a?.choice_num < b?.choice_num ? -1 : 1)) as PollChoice[]
return {
...data,
poll_choices,
poll_voters: [
...otherVoters,
{ name: username, choices }
]
} as Poll;
}
return {
...data,
poll_choices,
poll_voters: [...otherVoters, { name: username, choices }],
} as Poll;
};

View File

@ -1,128 +1,112 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import QUERIES from "../queryKeys";
import { useAppSelector } from "../../../hooks";
import { useDispatch } from "react-redux";
import { setRcOffer, toastNotification } from "../../../redux/actions/uiAction";
import { useIntl } from "react-intl";
import { getPostReblogs, reblog } from "../../hive/dhive";
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useDispatch } from 'react-redux';
import { useIntl } from 'react-intl';
import { get } from 'lodash';
import { PointActivityIds } from "../../ecency/ecency.types";
import { useUserActivityMutation } from "../pointQueries";
import QUERIES from '../queryKeys';
import { useAppSelector } from '../../../hooks';
import { setRcOffer, toastNotification } from '../../../redux/actions/uiAction';
import { getPostReblogs, reblog } from '../../hive/dhive';
import { PointActivityIds } from '../../ecency/ecency.types';
import { useUserActivityMutation } from '../pointQueries';
/** hook used to return post poll */
export const useGetReblogsQuery = (author: string, permlink: string) => {
const query = useQuery<string[]>(
[QUERIES.POST.GET_REBLOGS, author, permlink],
async () => {
if (!author || !permlink) {
return null;
}
const query = useQuery<string[]>(
[QUERIES.POST.GET_REBLOGS, author, permlink],
async () => {
if (!author || !permlink) {
return null;
}
try {
const reblogs = await getPostReblogs(author, permlink);
if (!reblogs) {
new Error('Reblog data unavailable');
}
return reblogs
} catch (err) {
console.warn('Failed to get post', err);
return []
}
},
{
initialData: [],
cacheTime: 30 * 60 * 1000, // keeps cache for 30 minutes
},
);
return query;
};
export function useReblogMutation(author: string, permlink: string) {
// const { activeUser } = useMappedStore();
const intl = useIntl();
const dispatch = useDispatch();
const queryClient = useQueryClient()
const currentAccount = useAppSelector(state => state.account.currentAccount);
const pinHash = useAppSelector(state => state.application.pin);
const userActivityMutation = useUserActivityMutation();
return useMutation({
mutationKey: [QUERIES.POST.REBLOG_POST],
mutationFn: async ({ undo }:{undo:boolean}) => {
if (!author || !permlink || !currentAccount) {
throw new Error("Not enough data to reblog post")
}
const resp = await reblog(currentAccount, pinHash, author, permlink, undo)
// track user activity points ty=130
userActivityMutation.mutate({
pointsTy: PointActivityIds.REBLOG,
transactionId: resp.id,
});
return resp;
},
retry: 3,
onSuccess: (resp, vars) => {
console.log("reblog response", resp);
//update poll cache here
queryClient.setQueryData<ReturnType<typeof useGetReblogsQuery>["data"]>(
[QUERIES.POST.GET_REBLOGS, author, permlink],
(data) => {
if (!data || !resp) {
return data;
}
const _curIndex = data.indexOf(currentAccount.username)
if(vars.undo){
data.splice(_curIndex, 1)
}
else if (_curIndex < 0) {
data.splice(0, 0, currentAccount.username);
}
return [...data] as ReturnType<typeof useGetReblogsQuery>["data"];
}
)
},
onError: (error) => {
if (String(get(error, 'jse_shortmsg', '')).indexOf('has already reblogged') > -1) {
dispatch(
toastNotification(
intl.formatMessage({
id: 'alert.already_rebloged',
}),
),
);
} else {
if (error && error.jse_shortmsg.split(': ')[1].includes('wait to transact')) {
// when RC is not enough, offer boosting account
dispatch(setRcOffer(true));
} else {
// when other errors
dispatch(toastNotification(intl.formatMessage({ id: 'alert.fail' })));
}
}
try {
const reblogs = await getPostReblogs(author, permlink);
if (!reblogs) {
new Error('Reblog data unavailable');
}
return reblogs;
} catch (err) {
console.warn('Failed to get post', err);
return [];
}
},
{
initialData: [],
cacheTime: 30 * 60 * 1000, // keeps cache for 30 minutes
},
);
});
return query;
};
export function useReblogMutation(author: string, permlink: string) {
// const { activeUser } = useMappedStore();
const intl = useIntl();
const dispatch = useDispatch();
const queryClient = useQueryClient();
const currentAccount = useAppSelector((state) => state.account.currentAccount);
const pinHash = useAppSelector((state) => state.application.pin);
const userActivityMutation = useUserActivityMutation();
return useMutation({
mutationKey: [QUERIES.POST.REBLOG_POST],
mutationFn: async ({ undo }: { undo: boolean }) => {
if (!author || !permlink || !currentAccount) {
throw new Error('Not enough data to reblog post');
}
const resp = await reblog(currentAccount, pinHash, author, permlink, undo);
// track user activity points ty=130
userActivityMutation.mutate({
pointsTy: PointActivityIds.REBLOG,
transactionId: resp.id,
});
return resp;
},
retry: 3,
onSuccess: (resp, vars) => {
console.log('reblog response', resp);
// update poll cache here
queryClient.setQueryData<ReturnType<typeof useGetReblogsQuery>['data']>(
[QUERIES.POST.GET_REBLOGS, author, permlink],
(data) => {
if (!data || !resp) {
return data;
}
const _curIndex = data.indexOf(currentAccount.username);
if (vars.undo) {
data.splice(_curIndex, 1);
} else if (_curIndex < 0) {
data.splice(0, 0, currentAccount.username);
}
return [...data] as ReturnType<typeof useGetReblogsQuery>['data'];
},
);
},
onError: (error) => {
if (String(get(error, 'jse_shortmsg', '')).indexOf('has already reblogged') > -1) {
dispatch(
toastNotification(
intl.formatMessage({
id: 'alert.already_rebloged',
}),
),
);
} else {
if (error && error.jse_shortmsg.split(': ')[1].includes('wait to transact')) {
// when RC is not enough, offer boosting account
dispatch(setRcOffer(true));
} else {
// when other errors
dispatch(toastNotification(intl.formatMessage({ id: 'alert.fail' })));
}
}
},
});
}

View File

@ -1,4 +1,10 @@
import { QueryKey, UseMutationOptions, useMutation, useQueries, useQueryClient } from '@tanstack/react-query';
import {
QueryKey,
UseMutationOptions,
useMutation,
useQueries,
useQueryClient,
} from '@tanstack/react-query';
import { useEffect, useMemo, useRef, useState } from 'react';
import { unionBy, isArray } from 'lodash';
@ -169,7 +175,9 @@ export const useWavesQuery = (host: string) => {
throw new Error('Failed to parse waves');
}
_threadedComments.filter((item) => item.net_rshares >= 0 && !item.stats?.gray && !item.stats.hide);
_threadedComments.filter(
(item) => item.net_rshares >= 0 && !item.stats?.gray && !item.stats.hide,
);
_threadedComments.sort((a, b) => (new Date(a.created) > new Date(b.created) ? -1 : 1));
_threadedComments.forEach((item) => {
wavesIndexCollection.current[`${item.author}/${item.permlink}`] = pagePermlink;
@ -181,14 +189,12 @@ export const useWavesQuery = (host: string) => {
};
const _fetchNextPage = () => {
if (!_lastItem || _lastItem.isFetching) {
return;
}
const _nextPagePermlink = permlinksBucket[activePermlinks.length];
if (_nextPagePermlink && !activePermlinks.includes(_nextPagePermlink)) {
console.log('updating next page permlink', _nextPagePermlink);
activePermlinks.push(_nextPagePermlink);
@ -211,23 +217,22 @@ export const useWavesQuery = (host: string) => {
const _data = unionBy(...wavesQueries.map((query) => query.data), 'url');
const _filteredData = useMemo(
() => _data.filter((post) =>
{
() =>
_data.filter((post) => {
let _status = true;
//discard wave if author is muted
// discard wave if author is muted
if (isArray(mutes) && mutes.indexOf(post?.author) > 0) {
_status = false;
}
//discard if wave is downvoted or marked gray
else if (post.net_rshares < 0 || post.stats?.gray || post.stats?.hide) {
_status = false
}
return _status
// discard if wave is downvoted or marked gray
else if (post.net_rshares < 0 || post.stats?.gray || post.stats?.hide) {
_status = false;
}
return _status;
}),
// (isArray(mutes) ? mutes.indexOf(post?.author) < 0 : true)),
// (isArray(mutes) ? mutes.indexOf(post?.author) < 0 : true)),
[mutes, _data],
);
@ -296,16 +301,19 @@ export const usePublishWaveMutation = () => {
const _host = cacheCommentData.parent_author;
// update query data
const containerQueriesData: [QueryKey, string[] | undefined][] = queryClient.getQueriesData([QUERIES.WAVES.INITIAL_CONTAINERS, _host]);
const containerQueriesData: [QueryKey, string[] | undefined][] = queryClient.getQueriesData([
QUERIES.WAVES.INITIAL_CONTAINERS,
_host,
]);
if (!containerQueriesData[0][1]) {
return;
}
//get query data of first waves container
// get query data of first waves container
const _containerKey: string = containerQueriesData[0][1][0];
const _queryKey = [QUERIES.WAVES.GET, _host, _containerKey, 0]
const queryData: any[] | undefined = queryClient.getQueryData(_queryKey)
const _queryKey = [QUERIES.WAVES.GET, _host, _containerKey, 0];
const queryData: any[] | undefined = queryClient.getQueryData(_queryKey);
console.log('query data', queryData);

View File

@ -28,9 +28,9 @@ const QUERIES = {
GET: 'QUERY_GET_POST',
GET_POLL: 'QUERY_GET_POLL',
GET_DISCUSSION: 'QUERY_GET_DISCUSSION',
SIGN_POLL_VOTE:"SIGN_POLL_VOTE",
GET_REBLOGS:"GET_REBLOGS",
REBLOG_POST:"REBLOG_POST"
SIGN_POLL_VOTE: 'SIGN_POLL_VOTE',
GET_REBLOGS: 'GET_REBLOGS',
REBLOG_POST: 'REBLOG_POST',
},
LEADERBOARD: {
GET: 'QUERY_GET_LEADERBOARD',

View File

@ -297,7 +297,7 @@ export const useActivitiesQuery = (assetId: string) => {
})),
});
const _lastItem = queries[queries.length - 1]
const _lastItem = queries[queries.length - 1];
const _refresh = async () => {
setIsRefreshing(true);
@ -336,7 +336,6 @@ export const useActivitiesQuery = (assetId: string) => {
return unionBy(..._dataArrs, 'trxIndex');
}, [_lastItem?.data]);
return {
data: _data,
isRefreshing,

View File

@ -14,7 +14,7 @@ import {
UPDATE_CLAIM_CACHE,
DELETE_CLAIM_CACHE_ENTRY,
UPDATE_ANNOUNCEMENTS_META,
UPDATE_POLL_VOTE_CACHE
UPDATE_POLL_VOTE_CACHE,
} from '../constants/constants';
export enum CacheStatus {
@ -39,7 +39,7 @@ export interface VoteCache {
export interface PollVoteCache {
choices: number[];
userHp:number;
userHp: number;
username: string;
votedAt: number;
expiresAt: number;
@ -169,7 +169,10 @@ const cacheReducer = (state = initialState, action) => {
if (!state.pollVotesCollection) {
state.pollVotesCollection = {};
}
state.pollVotesCollection = { ...state.pollVotesCollection, [payload.postPath]: payload.pollVote };
state.pollVotesCollection = {
...state.pollVotesCollection,
[payload.postPath]: payload.pollVote,
};
return {
...state, // spread operator in requried here, otherwise persist do not register change
lastUpdate: {

View File

@ -52,11 +52,10 @@ let enhancers;
if (__DEV__) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const createDebugger = require('redux-flipper').default;
// eslint-disable-next-line @typescript-eslint/no-var-requires
const Reactotron = require('../../../reactotron-config').default;
middleware.push(createDebugger());
enhancers = compose(
applyMiddleware(...middleware),
Reactotron.createEnhancer());
enhancers = compose(applyMiddleware(...middleware), Reactotron.createEnhancer());
} else {
enhancers = applyMiddleware(...middleware);
}

View File

@ -1,4 +1,4 @@
import React, { useEffect, useMemo, useState } from 'react';
import React from 'react';
import { FlatList, SafeAreaView } from 'react-native';
import { useIntl } from 'react-intl';
@ -13,51 +13,49 @@ import AccountListContainer from '../../../containers/accountListContainer';
import globalStyles from '../../../globalStyles';
import { getTimeFromNow } from '../../../utils/time';
const AccountList = ({ route }) => {
const intl = useIntl();
const intl = useIntl();
const users = route.params?.users || [];
const users = route.params?.users || [];
const headerTitle = route.params?.title || intl.formatMessage({
id: 'account_list.title',
const headerTitle =
route.params?.title ||
intl.formatMessage({
id: 'account_list.title',
});
return (
<AccountListContainer data={users}>
{({ data, filterResult, handleSearch, handleOnUserPress }) => (
<SafeAreaView style={[globalStyles.container, { paddingBottom: 40 }]}>
<BasicHeader
title={`${headerTitle} (${data && data.length})`}
backIconName="close"
isHasSearch
handleOnSearch={(text) => handleSearch(text, 'account')}
/>
<FlatList
data={filterResult || data}
keyExtractor={(item) => item.account}
removeClippedSubviews={false}
renderItem={({ item, index }) => renderUserListItem(item, index, handleOnUserPress)}
/>
</SafeAreaView>
)}
</AccountListContainer>
);
return (
<AccountListContainer data={users}>
{({ data, filterResult, handleSearch, handleOnUserPress }) => (
<SafeAreaView style={[globalStyles.container, { paddingBottom: 40 }]}>
<BasicHeader
title={`${headerTitle} (${data && data.length})`}
backIconName="close"
isHasSearch
handleOnSearch={(text) => handleSearch(text, 'account')}
/>
<FlatList
data={filterResult || data}
keyExtractor={(item) => item.account}
removeClippedSubviews={false}
renderItem={({ item, index }) => renderUserListItem(item, index, handleOnUserPress)}
/>
</SafeAreaView>
)}
</AccountListContainer>
);
};
export default gestureHandlerRootHOC(AccountList);
const renderUserListItem = (item, index, handleOnUserPress) => {
return (
<UserListItem
index={index}
username={item.account}
description={getTimeFromNow(item.timestamp)}
handleOnPress={() => handleOnUserPress(item.account)}
isClickable
/>
);
};
return (
<UserListItem
index={index}
username={item.account}
description={getTimeFromNow(item.timestamp)}
handleOnPress={() => handleOnUserPress(item.account)}
isClickable
/>
);
};

View File

@ -47,7 +47,7 @@ const AssetDetailsScreen = ({ navigation, route }: AssetDetailsScreenProps) => {
const quote: QuoteItem = useAppSelector((state) =>
state.wallet.quotes ? state.wallet.quotes[coinId] : {},
);
const username = useAppSelector(state => state.wallet.username);
const username = useAppSelector((state) => state.wallet.username);
const isPinCodeOpen = useAppSelector((state) => state.application.isPinCodeOpen);
// state
@ -154,7 +154,8 @@ const AssetDetailsScreen = ({ navigation, route }: AssetDetailsScreenProps) => {
if (baseActivity) {
navigateParams = {
...navigateParams,
referredUsername: baseActivity.receiver !== username ? baseActivity.receiver : baseActivity.sender,
referredUsername:
baseActivity.receiver !== username ? baseActivity.receiver : baseActivity.sender,
initialAmount: `${Math.abs(parseAsset(baseActivity.value.trim()).amount)}`,
initialMemo: baseActivity.memo,
};

View File

@ -4,7 +4,7 @@ import get from 'lodash/get';
// Components
import { gestureHandlerRootHOC } from 'react-native-gesture-handler';
import { EmptyScreen, Header, PostCard, PostCardPlaceHolder, PostPlaceHolder, TabbedPosts } from '../../../components';
import { Header, TabbedPosts } from '../../../components';
// Container
import { AccountContainer } from '../../../containers';
@ -15,8 +15,6 @@ import styles from './feedStyles';
import { getDefaultFilters, getFilterMap } from '../../../constants/options/filters';
import { useAppSelector } from '../../../hooks';
import LaunchScreen from '../../launch/screen/launchScreen';
import { CommentPlaceHolder } from '../../../components/basicUIElements';
const FeedScreen = () => {
const mainTabs = useAppSelector(

View File

@ -123,78 +123,78 @@ const LoginScreen = ({
}}
/>
<KeyboardAwareScrollView
enableAutoAutomaticScroll={Platform.OS === 'ios'}
contentContainerStyle={styles.formWrapper}
enableOnAndroid={true}
>
<FormInput
rightIconName="at"
leftIconName="close"
iconType="MaterialCommunityIcons"
isValid={isUsernameValid}
onChange={_handleUsernameChange}
placeholder={intl.formatMessage({
id: 'login.username',
})}
isEditable
type="username"
isFirstImage
value={username}
inputStyle={styles.input}
onBlur={() => _checkUsernameIsValid(username)}
/>
<FormInput
rightIconName="lock"
leftIconName="close"
isValid={isUsernameValid}
onChange={_handleOnPasswordChange}
placeholder={intl.formatMessage({
id: 'login.password',
})}
isEditable
secureTextEntry
type="password"
numberOfLines={1}
value={password}
inputStyle={styles.input}
/>
<InformationArea
description={intl.formatMessage({
id: 'login.description',
})}
link={ECENCY_TERMS_URL}
iconName="information-circle-outline"
/>
<MainButton
onPress={() => handleOnPressLogin(username, password)}
iconName="person"
iconColor="white"
text={intl.formatMessage({
id: 'login.login',
})}
textStyle={styles.mainBtnText}
isDisable={!isUsernameValid || password.length < 2 || username.length < 2}
isLoading={isLoading}
wrapperStyle={styles.loginBtnWrapper}
bodyWrapperStyle={styles.loginBtnBodyWrapper}
height={50}
iconStyle={styles.loginBtnIconStyle}
/>
<OrDivider />
<MainButton
onPress={() => _handleOnModalToggle()}
renderIcon={_renderHiveicon()}
text={intl.formatMessage({
id: 'login.login_with_hs',
})}
textStyle={styles.hsLoginBtnText}
wrapperStyle={styles.loginBtnWrapper}
bodyWrapperStyle={styles.loginBtnBodyWrapper}
height={48}
style={styles.hsLoginBtnStyle}
/>
<KeyboardAwareScrollView
enableAutoAutomaticScroll={Platform.OS === 'ios'}
contentContainerStyle={styles.formWrapper}
enableOnAndroid={true}
>
<FormInput
rightIconName="at"
leftIconName="close"
iconType="MaterialCommunityIcons"
isValid={isUsernameValid}
onChange={_handleUsernameChange}
placeholder={intl.formatMessage({
id: 'login.username',
})}
isEditable
type="username"
isFirstImage
value={username}
inputStyle={styles.input}
onBlur={() => _checkUsernameIsValid(username)}
/>
<FormInput
rightIconName="lock"
leftIconName="close"
isValid={isUsernameValid}
onChange={_handleOnPasswordChange}
placeholder={intl.formatMessage({
id: 'login.password',
})}
isEditable
secureTextEntry
type="password"
numberOfLines={1}
value={password}
inputStyle={styles.input}
/>
<InformationArea
description={intl.formatMessage({
id: 'login.description',
})}
link={ECENCY_TERMS_URL}
iconName="information-circle-outline"
/>
<MainButton
onPress={() => handleOnPressLogin(username, password)}
iconName="person"
iconColor="white"
text={intl.formatMessage({
id: 'login.login',
})}
textStyle={styles.mainBtnText}
isDisable={!isUsernameValid || password.length < 2 || username.length < 2}
isLoading={isLoading}
wrapperStyle={styles.loginBtnWrapper}
bodyWrapperStyle={styles.loginBtnBodyWrapper}
height={50}
iconStyle={styles.loginBtnIconStyle}
/>
<OrDivider />
<MainButton
onPress={() => _handleOnModalToggle()}
renderIcon={_renderHiveicon()}
text={intl.formatMessage({
id: 'login.login_with_hs',
})}
textStyle={styles.hsLoginBtnText}
wrapperStyle={styles.loginBtnWrapper}
bodyWrapperStyle={styles.loginBtnBodyWrapper}
height={48}
style={styles.hsLoginBtnStyle}
/>
<View style={styles.footerButtons}>
<Text style={styles.noAccountText}>
{intl.formatMessage({
@ -207,8 +207,8 @@ const LoginScreen = ({
})}
</Text>
</View>
</KeyboardAwareScrollView>
</KeyboardAwareScrollView>
<Modal
isOpen={isModalOpen}
isFullScreen

View File

@ -54,7 +54,7 @@ export default EStyleSheet.create({
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
marginVertical:8,
marginVertical: 8,
paddingHorizontal: 24,
backgroundColor: '$primaryBackgroundColor',
},

View File

@ -82,16 +82,16 @@ class PinCodeContainer extends Component {
const _hasHardware = await LocalAuthentication.hasHardwareAsync();
const _isEnrolled = await LocalAuthentication.isEnrolledAsync();
if (isReset || !isBiometricEnabled || !_hasHardware || !_isEnrolled ) {
if (isReset || !isBiometricEnabled || !_hasHardware || !_isEnrolled) {
return;
}
const authResp = await LocalAuthentication.authenticateAsync({
promptMessage: intl.formatMessage({ id: 'pincode.biometric_desc' }),
})
});
if (!authResp.success) {
throw new Error("Authentication Failed");
throw new Error('Authentication Failed');
}
console.log('successfully passed biometric auth');

View File

@ -2,6 +2,7 @@ import React, { useMemo, useState } from 'react';
import { FlatList, RefreshControl, SafeAreaView } from 'react-native';
import { useIntl } from 'react-intl';
import { gestureHandlerRootHOC } from 'react-native-gesture-handler';
import Animated, { BounceInRight } from 'react-native-reanimated';
import { useAppSelector } from '../../../hooks';
import showLoginAlert from '../../../utils/showLoginAlert';
@ -15,9 +16,7 @@ import AccountListContainer from '../../../containers/accountListContainer';
import globalStyles from '../../../globalStyles';
import styles from '../styles/reblogScreen.styles';
import { getTimeFromNow } from '../../../utils/time';
import Animated, { BounceInRight } from 'react-native-reanimated';
import { reblogQueries } from '../../../providers/queries';
;
const renderUserListItem = (item, index, handleOnUserPress) => {
return (
@ -40,15 +39,12 @@ const ReblogScreen = ({ route }) => {
const isLoggedIn = useAppSelector((state) => state.application.isLoggedIn);
const isDarkTheme = useAppSelector((state) => state.application.isDarkTheme);
const [isReblogging, setIsReblogging] = useState(false);
const reblogsQuery = reblogQueries.useGetReblogsQuery(author, permlink);
const reblogMutation = reblogQueries.useReblogMutation(author, permlink);
//map reblogs data for account list
// map reblogs data for account list
const { reblogs, deleteEnabled } = useMemo(() => {
let _reblogs: any[] = [];
let _deleteEnabled = false;
@ -58,18 +54,18 @@ const ReblogScreen = ({ route }) => {
}
return {
reblogs: _reblogs,
deleteEnabled: _deleteEnabled
}
}, [reblogsQuery.data?.length])
deleteEnabled: _deleteEnabled,
};
}, [reblogsQuery.data?.length]);
const headerTitle = intl.formatMessage({
id: 'reblog.title',
});
const _actionBtnTitle = intl.formatMessage({ id: deleteEnabled ? 'reblog.reblog_delete' : 'reblog.reblog_post' })
const _actionBtnIcon = deleteEnabled ? "repeat-off" : "repeat";
const _actionBtnTitle = intl.formatMessage({
id: deleteEnabled ? 'reblog.reblog_delete' : 'reblog.reblog_post',
});
const _actionBtnIcon = deleteEnabled ? 'repeat-off' : 'repeat';
const _handleReblogPost = async () => {
if (!isLoggedIn) {
@ -79,15 +75,12 @@ const ReblogScreen = ({ route }) => {
if (isLoggedIn) {
setIsReblogging(true);
await reblogMutation.mutateAsync({ undo:deleteEnabled });
await reblogMutation.mutateAsync({ undo: deleteEnabled });
setIsReblogging(false);
}
}
};
const _renderFloatingButton = () => {
return (
<Animated.View style={styles.floatingContainer} entering={BounceInRight.delay(300)}>
<MainButton
@ -102,15 +95,10 @@ const ReblogScreen = ({ route }) => {
);
};
return (
<AccountListContainer data={reblogs}>
{({ data, filterResult, handleSearch, handleOnUserPress }) => (
<SafeAreaView style={[globalStyles.container, { paddingBottom: 40 }]}>
{/* Your content goes here */}
<BasicHeader
title={`${headerTitle} (${data && data.length})`}
@ -122,9 +110,7 @@ const ReblogScreen = ({ route }) => {
data={filterResult || data}
keyExtractor={(item) => item.account}
removeClippedSubviews={false}
renderItem={({ item, index }) =>
renderUserListItem(item, index, handleOnUserPress)
}
renderItem={({ item, index }) => renderUserListItem(item, index, handleOnUserPress)}
refreshControl={
<RefreshControl
refreshing={reblogsQuery.isLoading || reblogsQuery.isFetching}
@ -133,15 +119,14 @@ const ReblogScreen = ({ route }) => {
tintColor={!isDarkTheme ? '#357ce6' : '#96c0ff'}
titleColor="#fff"
colors={['#fff']}
/>}
/>
}
/>
{_renderFloatingButton()}
</SafeAreaView>
)
}
</AccountListContainer >
)}
</AccountListContainer>
);
};

View File

@ -1,9 +1,9 @@
import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
floatingContainer: {
position: 'absolute',
right: 16,
bottom: 56,
}
})
floatingContainer: {
position: 'absolute',
right: 16,
bottom: 56,
},
});

View File

@ -1,12 +1,12 @@
import React, { PureComponent, Fragment } from 'react';
import React, { PureComponent } from 'react';
import { StyleSheet, View } from 'react-native';
import { gestureHandlerRootHOC } from 'react-native-gesture-handler';
import Animated, { SlideInRight } from 'react-native-reanimated';
import { RedeemContainer, PointsContainer } from '../../../containers';
import { Promote, PostBoost } from '../../../components';
import BoostPlus from '../children/boostPlus';
import styles from '../styles/redeemScreen.styles';
import Animated, { Easing, SlideInDown, SlideInRight, SlideInUp } from 'react-native-reanimated';
class RedeemScreen extends PureComponent {
constructor(props) {
@ -34,54 +34,59 @@ class RedeemScreen extends PureComponent {
}) => (
<RedeemContainer>
{({ handleOnSubmit, SCPath, isSCModalOpen, handleOnSCModalClose, isLoading }) => {
let _retView = null;
switch (redeemType) {
case 'promote':
_retView = <Promote
isLoading={isLoading}
accounts={accounts}
currentAccountName={currentAccountName}
balance={balance}
navigationParams={navigationParams}
handleOnSubmit={handleOnSubmit}
redeemType={redeemType}
isSCModalOpen={isSCModalOpen}
handleOnSCModalClose={handleOnSCModalClose}
SCPath={SCPath}
getESTMPrice={getESTMPrice}
/>
_retView = (
<Promote
isLoading={isLoading}
accounts={accounts}
currentAccountName={currentAccountName}
balance={balance}
navigationParams={navigationParams}
handleOnSubmit={handleOnSubmit}
redeemType={redeemType}
isSCModalOpen={isSCModalOpen}
handleOnSCModalClose={handleOnSCModalClose}
SCPath={SCPath}
getESTMPrice={getESTMPrice}
/>
);
break;
case 'boost':
_retView = <PostBoost
isLoading={isLoading}
accounts={accounts}
currentAccountName={currentAccountName}
balance={balance}
navigationParams={navigationParams}
handleOnSubmit={handleOnSubmit}
redeemType={redeemType}
isSCModalOpen={isSCModalOpen}
handleOnSCModalClose={handleOnSCModalClose}
SCPath={SCPath}
getESTMPrice={getESTMPrice}
user={user}
/>
_retView = (
<PostBoost
isLoading={isLoading}
accounts={accounts}
currentAccountName={currentAccountName}
balance={balance}
navigationParams={navigationParams}
handleOnSubmit={handleOnSubmit}
redeemType={redeemType}
isSCModalOpen={isSCModalOpen}
handleOnSCModalClose={handleOnSCModalClose}
SCPath={SCPath}
getESTMPrice={getESTMPrice}
user={user}
/>
);
break;
case 'boost_plus':
_retView = <BoostPlus
isLoading={isLoading}
accounts={accounts}
currentAccountName={currentAccountName}
balance={balance}
navigationParams={navigationParams}
handleOnSubmit={handleOnSubmit}
redeemType={redeemType}
isSCModalOpen={isSCModalOpen}
handleOnSCModalClose={handleOnSCModalClose}
SCPath={SCPath}
getESTMPrice={getESTMPrice}
/>
_retView = (
<BoostPlus
isLoading={isLoading}
accounts={accounts}
currentAccountName={currentAccountName}
balance={balance}
navigationParams={navigationParams}
handleOnSubmit={handleOnSubmit}
redeemType={redeemType}
isSCModalOpen={isSCModalOpen}
handleOnSCModalClose={handleOnSCModalClose}
SCPath={SCPath}
getESTMPrice={getESTMPrice}
/>
);
break;
}
@ -89,7 +94,7 @@ class RedeemScreen extends PureComponent {
<Animated.View style={StyleSheet.absoluteFill} entering={SlideInRight}>
{_retView}
</Animated.View>
)
);
}}
</RedeemContainer>
)}

View File

@ -1,8 +1,8 @@
import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
container: {
flex: 1,
backgroundColor: '$primaryBackgroundColor'
},
})
container: {
flex: 1,
backgroundColor: '$primaryBackgroundColor',
},
});

View File

@ -165,9 +165,9 @@ export const AssetCard = ({
chartData.length > 0 ? (
<View style={styles.cardFooter}>
<Text style={styles.textCurValue}>{`${currencySymbol} ${currentValue.toFixed(2)}`}</Text>
<Text style={changePercent > 0 ? styles.textDiffPositive : styles.textDiffNegative}>{`${
changePercent >= 0 ? '+' : ''
}${changePercent.toFixed(1)}%`}</Text>
<Text style={changePercent > 0 ? styles.textDiffPositive : styles.textDiffNegative}>
{`${changePercent >= 0 ? '+' : ''}${changePercent.toFixed(1)}%`}
</Text>
</View>
) : (
<View style={{ height: 12 }} />

View File

@ -17,10 +17,9 @@ import LaunchScreen from '../../launch';
import styles from '../children/WelcomeScreenStyles';
const WelcomeScreen = () => {
//NOTE: I have no logical explanation for this, but only this solution
//makes sure first screen is renderd from stack, otherwise it's always
//blank white screen no matter what we try.
// NOTE: I have no logical explanation for this, but only this solution
// makes sure first screen is renderd from stack, otherwise it's always
// blank white screen no matter what we try.
require('@esteemapp/dhive');
const intl = useIntl();

View File

@ -303,7 +303,7 @@ const reduxMigrations = {
8: (state) => {
state.cache.pollVotesCollection = {};
return state;
}
},
};
export default {

View File

@ -50,7 +50,7 @@ export const parsePost = (
// extract cover image and thumbnail from post body
post.image = catchPostImage(post, 600, 500, webp ? 'webp' : 'match');
post.thumbnail = catchPostImage(post, 10, 7, webp ? 'webp' : 'match');
post.thumbnail = catchPostImage(post, 10, 7, webp ? 'webp' : 'match');
// find and inject thumbnail ratio
if (post.json_metadata.image_ratios) {

View File

@ -87,7 +87,6 @@ export const getTimeFromNow = (value, isWithoutUtc) => {
return moment.utc(value).fromNow();
};
export const getDaysPassedSince = (value) => {
if (!value) {
return 0;
@ -95,7 +94,7 @@ export const getDaysPassedSince = (value) => {
const created = moment(value);
return moment().diff(created, 'days');
}
};
export const getFormatedCreatedDate = (value) => {
if (!value) {

View File

@ -248,7 +248,7 @@ export const groomingEngineHistory = (transaction: HistoryItem): CoinActivity |
receiver: to,
icon: 'local-activity',
expires: '',
repeatable : RepeatableTransfers[operation] || false
repeatable: RepeatableTransfers[operation] || false,
};
switch (result.textKey) {
@ -1113,7 +1113,7 @@ export const groomingPointsTransactionData = (transaction) => {
result.trxIndex = transaction.id;
result.repeatable = RepeatableTransfers[transaction.textKey] || false;
result.sender = transaction.sender;
result.receiver = transaction.receiver;
result.receiver = transaction.receiver;
return result;
};

137
yarn.lock
View File

@ -2759,30 +2759,6 @@
prompts "^2.4.2"
semver "^7.5.2"
"@react-native-community/eslint-config@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@react-native-community/eslint-config/-/eslint-config-2.0.0.tgz#35dcc529a274803fc4e0a6b3d6c274551fb91774"
integrity sha512-vHaMMfvMp9BWCQQ0lNIXibOJTcXIbYUQ8dSUsMOsrXgVkeVQJj88OwrKS00rQyqwMaC4/a6HuDiFzYUkGKOpVg==
dependencies:
"@react-native-community/eslint-plugin" "^1.1.0"
"@typescript-eslint/eslint-plugin" "^3.1.0"
"@typescript-eslint/parser" "^3.1.0"
babel-eslint "^10.1.0"
eslint-config-prettier "^6.10.1"
eslint-plugin-eslint-comments "^3.1.2"
eslint-plugin-flowtype "2.50.3"
eslint-plugin-jest "22.4.1"
eslint-plugin-prettier "3.1.2"
eslint-plugin-react "^7.20.0"
eslint-plugin-react-hooks "^4.0.4"
eslint-plugin-react-native "^3.8.1"
prettier "^2.0.2"
"@react-native-community/eslint-plugin@^1.1.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@react-native-community/eslint-plugin/-/eslint-plugin-1.3.0.tgz#9e558170c106bbafaa1ef502bd8e6d4651012bf9"
integrity sha512-+zDZ20NUnSWghj7Ku5aFphMzuM9JulqCW+aPXT6IfIXFbb8tzYTTOSeRFOtuekJ99ibW2fUCSsjuKNlwDIbHFg==
"@react-native-community/netinfo@^11.1.1":
version "11.2.1"
resolved "https://registry.yarnpkg.com/@react-native-community/netinfo/-/netinfo-11.2.1.tgz#177f5409f2ac41fd51ede6e4994307bb0edb2178"
@ -3507,18 +3483,6 @@
dependencies:
"@types/yargs-parser" "*"
"@typescript-eslint/eslint-plugin@^3.1.0":
version "3.10.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.10.1.tgz#7e061338a1383f59edc204c605899f93dc2e2c8f"
integrity sha512-PQg0emRtzZFWq6PxBcdxRH3QIQiyFO3WCVpRL3fgj5oQS3CDs3AeAKfv4DxNhzn8ITdNJGJ4D3Qw8eAJf3lXeQ==
dependencies:
"@typescript-eslint/experimental-utils" "3.10.1"
debug "^4.1.1"
functional-red-black-tree "^1.0.1"
regexpp "^3.0.0"
semver "^7.3.2"
tsutils "^3.17.1"
"@typescript-eslint/eslint-plugin@^5.57.1":
version "5.62.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db"
@ -3544,17 +3508,6 @@
"@typescript-eslint/typescript-estree" "1.13.0"
eslint-scope "^4.0.0"
"@typescript-eslint/experimental-utils@3.10.1":
version "3.10.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-3.10.1.tgz#e179ffc81a80ebcae2ea04e0332f8b251345a686"
integrity sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw==
dependencies:
"@types/json-schema" "^7.0.3"
"@typescript-eslint/types" "3.10.1"
"@typescript-eslint/typescript-estree" "3.10.1"
eslint-scope "^5.0.0"
eslint-utils "^2.0.0"
"@typescript-eslint/parser@^1.10.2":
version "1.13.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-1.13.0.tgz#61ac7811ea52791c47dc9fd4dd4a184fae9ac355"
@ -3565,17 +3518,6 @@
"@typescript-eslint/typescript-estree" "1.13.0"
eslint-visitor-keys "^1.0.0"
"@typescript-eslint/parser@^3.1.0":
version "3.10.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-3.10.1.tgz#1883858e83e8b442627e1ac6f408925211155467"
integrity sha512-Ug1RcWcrJP02hmtaXVS3axPPTTPnZjupqhgj+NnZ6BCkwSImWk/283347+x9wN+lqOdK9Eo3vsyiyDHgsmiEJw==
dependencies:
"@types/eslint-visitor-keys" "^1.0.0"
"@typescript-eslint/experimental-utils" "3.10.1"
"@typescript-eslint/types" "3.10.1"
"@typescript-eslint/typescript-estree" "3.10.1"
eslint-visitor-keys "^1.1.0"
"@typescript-eslint/parser@^5.57.1":
version "5.62.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7"
@ -3604,11 +3546,6 @@
debug "^4.3.4"
tsutils "^3.21.0"
"@typescript-eslint/types@3.10.1":
version "3.10.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.10.1.tgz#1d7463fa7c32d8a23ab508a803ca2fe26e758727"
integrity sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ==
"@typescript-eslint/types@5.62.0":
version "5.62.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f"
@ -3622,20 +3559,6 @@
lodash.unescape "4.0.1"
semver "5.5.0"
"@typescript-eslint/typescript-estree@3.10.1":
version "3.10.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.10.1.tgz#fd0061cc38add4fad45136d654408569f365b853"
integrity sha512-QbcXOuq6WYvnB3XPsZpIwztBoquEYLXh2MtwVU+kO8jgYCiv4G5xrSP/1wg4tkvrEE+esZVquIPX/dxPlePk1w==
dependencies:
"@typescript-eslint/types" "3.10.1"
"@typescript-eslint/visitor-keys" "3.10.1"
debug "^4.1.1"
glob "^7.1.6"
is-glob "^4.0.1"
lodash "^4.17.15"
semver "^7.3.2"
tsutils "^3.17.1"
"@typescript-eslint/typescript-estree@5.62.0":
version "5.62.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b"
@ -3663,13 +3586,6 @@
eslint-scope "^5.1.1"
semver "^7.3.7"
"@typescript-eslint/visitor-keys@3.10.1":
version "3.10.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-3.10.1.tgz#cd4274773e3eb63b2e870ac602274487ecd1e931"
integrity sha512-9JgC82AaQeglebjZMgYR5wgmfUdUc+EitGUUMW8u2nDckaeimzW+VsoLV6FoimPv2id3VQzfjwBxEMVz08ameQ==
dependencies:
eslint-visitor-keys "^1.1.0"
"@typescript-eslint/visitor-keys@5.62.0":
version "5.62.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e"
@ -4225,7 +4141,7 @@ babel-core@^7.0.0-bridge.0:
resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece"
integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==
babel-eslint@^10.0.1, babel-eslint@^10.1.0:
babel-eslint@^10.0.1:
version "10.1.0"
resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232"
integrity sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==
@ -6258,7 +6174,7 @@ eslint-config-airbnb@^17.1.0:
object.assign "^4.1.0"
object.entries "^1.1.0"
eslint-config-prettier@^6.10.1, eslint-config-prettier@^6.7.0:
eslint-config-prettier@^6.7.0:
version "6.15.0"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz#7f93f6cb7d45a92f1537a70ecc06366e1ac6fed9"
integrity sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw==
@ -6286,7 +6202,7 @@ eslint-module-utils@^2.8.0:
dependencies:
debug "^3.2.7"
eslint-plugin-eslint-comments@^3.1.1, eslint-plugin-eslint-comments@^3.1.2, eslint-plugin-eslint-comments@^3.2.0:
eslint-plugin-eslint-comments@^3.1.1, eslint-plugin-eslint-comments@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.2.0.tgz#9e1cd7b4413526abb313933071d7aba05ca12ffa"
integrity sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==
@ -6294,13 +6210,6 @@ eslint-plugin-eslint-comments@^3.1.1, eslint-plugin-eslint-comments@^3.1.2, esli
escape-string-regexp "^1.0.5"
ignore "^5.0.5"
eslint-plugin-flowtype@2.50.3:
version "2.50.3"
resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.50.3.tgz#61379d6dce1d010370acd6681740fd913d68175f"
integrity sha512-X+AoKVOr7Re0ko/yEXyM5SSZ0tazc6ffdIOocp2fFUlWoDt7DV0Bz99mngOkAFLOAWjqRA5jPwqUCbrx13XoxQ==
dependencies:
lodash "^4.17.10"
eslint-plugin-ft-flow@^2.0.1:
version "2.0.3"
resolved "https://registry.yarnpkg.com/eslint-plugin-ft-flow/-/eslint-plugin-ft-flow-2.0.3.tgz#3b3c113c41902bcbacf0e22b536debcfc3c819e8"
@ -6332,11 +6241,6 @@ eslint-plugin-import@^2.17.2:
semver "^6.3.1"
tsconfig-paths "^3.14.2"
eslint-plugin-jest@22.4.1:
version "22.4.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-22.4.1.tgz#a5fd6f7a2a41388d16f527073b778013c5189a9c"
integrity sha512-gcLfn6P2PrFAVx3AobaOzlIEevpAEf9chTpFZz7bYfc7pz8XRv7vuKTIE4hxPKZSha6XWKKplDQ0x9Pq8xX2mg==
eslint-plugin-jest@^22.5.1:
version "22.21.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-22.21.0.tgz#8137294645866636160487d9764224b9a43e2eb1"
@ -6373,13 +6277,6 @@ eslint-plugin-jsx-a11y@^6.2.1:
object.entries "^1.1.7"
object.fromentries "^2.0.7"
eslint-plugin-prettier@3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.2.tgz#432e5a667666ab84ce72f945c72f77d996a5c9ba"
integrity sha512-GlolCC9y3XZfv3RQfwGew7NnuFDKsfI4lbvRK+PIIo23SFH+LemGs4cKwzAaRa+Mdb+lQO/STaIayno8T5sJJA==
dependencies:
prettier-linter-helpers "^1.0.0"
eslint-plugin-prettier@^3.1.1:
version "3.4.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.1.tgz#e9ddb200efb6f3d05ffe83b1665a716af4a387e5"
@ -6399,7 +6296,7 @@ eslint-plugin-react-hooks@^2.3.0:
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-2.5.1.tgz#4ef5930592588ce171abeb26f400c7fbcbc23cd0"
integrity sha512-Y2c4b55R+6ZzwtTppKwSmK/Kar8AdLiC2f9NADCuxbcTgPPg41Gyqa6b9GppgXSvCtkRw43ZE86CT5sejKC6/g==
eslint-plugin-react-hooks@^4.0.4, eslint-plugin-react-hooks@^4.6.0:
eslint-plugin-react-hooks@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3"
integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==
@ -6409,7 +6306,7 @@ eslint-plugin-react-native-globals@^0.1.1:
resolved "https://registry.yarnpkg.com/eslint-plugin-react-native-globals/-/eslint-plugin-react-native-globals-0.1.2.tgz#ee1348bc2ceb912303ce6bdbd22e2f045ea86ea2"
integrity sha512-9aEPf1JEpiTjcFAmmyw8eiIXmcNZOqaZyHO77wgm0/dWfT/oxC1SrIq8ET38pMxHYrcB6Uew+TzUVsBeczF88g==
eslint-plugin-react-native@^3.7.0, eslint-plugin-react-native@^3.8.1:
eslint-plugin-react-native@^3.7.0:
version "3.11.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react-native/-/eslint-plugin-react-native-3.11.0.tgz#c73b0886abb397867e5e6689d3a6a418682e6bac"
integrity sha512-7F3OTwrtQPfPFd+VygqKA2VZ0f2fz0M4gJmry/TRE18JBb94/OtMxwbL7Oqwu7FGyrdeIOWnXQbBAveMcSTZIA==
@ -6424,7 +6321,7 @@ eslint-plugin-react-native@^4.0.0:
dependencies:
eslint-plugin-react-native-globals "^0.1.1"
eslint-plugin-react@^7.13.0, eslint-plugin-react@^7.20.0, eslint-plugin-react@^7.30.1:
eslint-plugin-react@^7.13.0, eslint-plugin-react@^7.30.1:
version "7.33.2"
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz#69ee09443ffc583927eafe86ffebb470ee737608"
integrity sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==
@ -6446,7 +6343,7 @@ eslint-plugin-react@^7.13.0, eslint-plugin-react@^7.20.0, eslint-plugin-react@^7
semver "^6.3.1"
string.prototype.matchall "^4.0.8"
eslint-scope@5.1.1, eslint-scope@^5.0.0, eslint-scope@^5.1.1:
eslint-scope@5.1.1, eslint-scope@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
@ -6485,13 +6382,6 @@ eslint-utils@^1.3.1:
dependencies:
eslint-visitor-keys "^1.1.0"
eslint-utils@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27"
integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==
dependencies:
eslint-visitor-keys "^1.1.0"
eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e"
@ -9510,7 +9400,7 @@ lodash.uniq@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==
lodash@>4.17.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.17.5:
lodash@>4.17.0, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.17.5:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@ -11030,7 +10920,7 @@ prettier-linter-helpers@^1.0.0:
dependencies:
fast-diff "^1.1.2"
prettier@2.8.8, prettier@^2.0.2:
prettier@2.8.8:
version "2.8.8"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"
integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==
@ -12214,11 +12104,6 @@ regexpp@^2.0.1:
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f"
integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==
regexpp@^3.0.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
regexpu-core@^5.3.1:
version "5.3.2"
resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b"
@ -12584,7 +12469,7 @@ semver@^6.3.0, semver@^6.3.1:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
semver@^7.0.0, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4:
semver@^7.0.0, semver@^7.3.5, semver@^7.3.7, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4:
version "7.5.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
@ -13749,7 +13634,7 @@ tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
tsutils@^3.17.1, tsutils@^3.21.0:
tsutils@^3.21.0:
version "3.21.0"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==