mirror of
https://github.com/ecency/ecency-mobile.git
synced 2024-11-26 09:13:33 +03:00
Merge pull request #2882 from ecency/nt/recover-lint
removed conflicting eslint parser
This commit is contained in:
commit
64d9a86de6
@ -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"
|
||||
}
|
||||
}
|
@ -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",
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
@ -6,7 +6,6 @@ import { useSelector } from 'react-redux';
|
||||
import styles from './listItemPlaceHolderStyles';
|
||||
|
||||
const CommentPlaceHolderView = () => {
|
||||
|
||||
const _width = 300;
|
||||
|
||||
const animationStyle = {
|
||||
|
@ -10,7 +10,7 @@ const PostCardPlaceHolder = () => {
|
||||
|
||||
const animationStyle = {
|
||||
width: _width,
|
||||
height: _width * 2
|
||||
height: _width * 2,
|
||||
};
|
||||
|
||||
const isDarkTheme = useSelector((state) => state.application.isDarkTheme);
|
||||
|
@ -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>
|
||||
|
@ -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}>
|
||||
|
@ -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>}
|
||||
|
@ -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"
|
||||
|
@ -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,
|
||||
},
|
||||
});
|
||||
|
@ -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)}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
|
@ -1 +1 @@
|
||||
export * from './imageViewer';
|
||||
export * from './imageViewer';
|
||||
|
@ -260,5 +260,5 @@ export {
|
||||
WebViewModal,
|
||||
OrDivider,
|
||||
PostTranslationModal,
|
||||
ImageViewer
|
||||
ImageViewer,
|
||||
};
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
@ -44,7 +44,6 @@ export const PostCardActionsPanel = ({ content, handleCardInteraction }: Props)
|
||||
permlink,
|
||||
},
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
||||
|
@ -80,7 +80,7 @@ export default EStyleSheet.create({
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginRight: -8,
|
||||
paddingBottom:5
|
||||
paddingBottom: 5,
|
||||
},
|
||||
pushPinIcon: {
|
||||
color: '$primaryRed',
|
||||
|
@ -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}
|
||||
|
@ -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}
|
||||
|
@ -1,2 +1,2 @@
|
||||
export * from './pollHeader';
|
||||
export * from './pollChoices';
|
||||
export * from './pollChoices';
|
||||
|
@ -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>;
|
||||
};
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
@ -1 +1 @@
|
||||
export * from './container/postPoll';
|
||||
export * from './container/postPoll';
|
||||
|
@ -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,
|
||||
});
|
||||
|
@ -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,
|
||||
});
|
||||
|
@ -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,
|
||||
});
|
||||
|
@ -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 = () => {
|
||||
|
@ -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',
|
||||
},
|
||||
)}
|
||||
|
@ -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}
|
||||
|
@ -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';
|
||||
|
@ -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}
|
||||
|
@ -145,8 +145,7 @@ export const QuickReplyModalContent = forwardRef(
|
||||
|
||||
// handle submit reply
|
||||
const _submitPost = async () => {
|
||||
|
||||
if(isSubmitting){
|
||||
if (isSubmitting) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -28,4 +28,4 @@ export default EStyleSheet.create({
|
||||
},
|
||||
elevation: 3,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -71,4 +71,4 @@ const ToggleSwitchView = ({ onColor, offColor, latchBack, onToggle, ...props }:
|
||||
);
|
||||
};
|
||||
|
||||
export default ToggleSwitchView;
|
||||
export default ToggleSwitchView;
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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);
|
||||
|
@ -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'));
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
);
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 = () => {
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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' })));
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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,
|
||||
|
@ -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: {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -54,7 +54,7 @@ export default EStyleSheet.create({
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginVertical:8,
|
||||
marginVertical: 8,
|
||||
paddingHorizontal: 24,
|
||||
backgroundColor: '$primaryBackgroundColor',
|
||||
},
|
||||
|
@ -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');
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -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,
|
||||
},
|
||||
});
|
||||
|
@ -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>
|
||||
)}
|
||||
|
@ -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',
|
||||
},
|
||||
});
|
||||
|
@ -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 }} />
|
||||
|
@ -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();
|
||||
|
@ -303,7 +303,7 @@ const reduxMigrations = {
|
||||
8: (state) => {
|
||||
state.cache.pollVotesCollection = {};
|
||||
return state;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default {
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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
137
yarn.lock
@ -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==
|
||||
|
Loading…
Reference in New Issue
Block a user