mirror of
https://github.com/ecency/ecency-mobile.git
synced 2024-12-31 17:54:52 +03:00
Merge branch 'development' into nt/wallet-redesign
This commit is contained in:
commit
3e2de98395
@ -63,6 +63,7 @@ allprojects {
|
||||
includeGroup("com.facebook.fbjni")
|
||||
includeGroup("com.henninghall.android")
|
||||
includeGroup("org.matomo.sdk")
|
||||
includeModule("com.yqritc", "android-scalablevideoview")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -335,6 +335,11 @@ PODS:
|
||||
- React
|
||||
- react-native-version-number (0.3.6):
|
||||
- React
|
||||
- react-native-video (5.2.0):
|
||||
- React-Core
|
||||
- react-native-video/Video (= 5.2.0)
|
||||
- react-native-video/Video (5.2.0):
|
||||
- React-Core
|
||||
- react-native-webview (11.17.1):
|
||||
- React-Core
|
||||
- React-RCTActionSheet (0.63.4):
|
||||
@ -496,6 +501,7 @@ DEPENDENCIES:
|
||||
- react-native-splash-screen (from `../node_modules/react-native-splash-screen`)
|
||||
- react-native-udp (from `../node_modules/react-native-udp`)
|
||||
- react-native-version-number (from `../node_modules/react-native-version-number`)
|
||||
- react-native-video (from `../node_modules/react-native-video`)
|
||||
- react-native-webview (from `../node_modules/react-native-webview`)
|
||||
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
|
||||
- React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`)
|
||||
@ -621,6 +627,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native-udp"
|
||||
react-native-version-number:
|
||||
:path: "../node_modules/react-native-version-number"
|
||||
react-native-video:
|
||||
:path: "../node_modules/react-native-video"
|
||||
react-native-webview:
|
||||
:path: "../node_modules/react-native-webview"
|
||||
React-RCTActionSheet:
|
||||
@ -736,6 +744,7 @@ SPEC CHECKSUMS:
|
||||
react-native-splash-screen: 200d11d188e2e78cea3ad319964f6142b6384865
|
||||
react-native-udp: ff9d13e523f2b58e6bc5d4d32321ac60671b5dc9
|
||||
react-native-version-number: b415bbec6a13f2df62bf978e85bc0d699462f37f
|
||||
react-native-video: a4c2635d0802f983594b7057e1bce8f442f0ad28
|
||||
react-native-webview: 162b6453d074e0b1c7025242bb7a939b6f72b9e7
|
||||
React-RCTActionSheet: 89a0ca9f4a06c1f93c26067af074ccdce0f40336
|
||||
React-RCTAnimation: 1bde3ecc0c104c55df246eda516e0deb03c4e49b
|
||||
|
@ -101,9 +101,10 @@
|
||||
"react-native-level-fs": "^3.0.0",
|
||||
"react-native-linear-gradient": "^2.4.2",
|
||||
"react-native-matomo-sdk": "feruzm/react-native-matomo-sdk",
|
||||
"react-native-media-controls": "^2.3.0",
|
||||
"react-native-modal": "^11.5.6",
|
||||
"react-native-modal-popover": "^2.1.0",
|
||||
"react-native-modal-dropdown": "^1.0.2",
|
||||
"react-native-modal-popover": "^2.1.0",
|
||||
"react-native-modal-translucent": "^5.0.0",
|
||||
"react-native-navigation-bar-color": "^1.0.0",
|
||||
"react-native-os": "^1.0.1",
|
||||
@ -119,6 +120,7 @@
|
||||
"react-native-safe-area-context": "^3.1.9",
|
||||
"react-native-screens": "^2.9.0",
|
||||
"react-native-scrollable-tab-view": "ecency/react-native-scrollable-tab-view",
|
||||
"react-native-slider": "^0.11.0",
|
||||
"react-native-snap-carousel": "^3.8.0",
|
||||
"react-native-splash-screen": "^3.2.0",
|
||||
"react-native-svg": "^12.1.1",
|
||||
@ -129,6 +131,7 @@
|
||||
"react-native-vector-icons": "^6.6.0",
|
||||
"react-native-version": "^4.0.0",
|
||||
"react-native-version-number": "^0.3.5",
|
||||
"react-native-video": "^5.2.0",
|
||||
"react-native-webview": "^11.17.1",
|
||||
"react-native-youtube-iframe": "^2.1.1",
|
||||
"react-navigation": "^4.0.10",
|
||||
|
@ -8,9 +8,11 @@ import ActionModalView, { ActionModalRef } from '../view/actionModalView';
|
||||
export interface ActionModalData {
|
||||
title:string,
|
||||
body:string,
|
||||
para?: string,
|
||||
buttons:AlertButton[],
|
||||
headerImage?:Source,
|
||||
onClosed:()=>void,
|
||||
headerContent?: React.ReactNode,
|
||||
}
|
||||
|
||||
|
||||
|
@ -42,12 +42,19 @@ const ActionModalView = ({onClose, data}: ActionModalViewProps, ref) => {
|
||||
title,
|
||||
body,
|
||||
buttons,
|
||||
headerImage
|
||||
headerImage,
|
||||
para,
|
||||
headerContent
|
||||
} = data;
|
||||
|
||||
|
||||
const _renderContent = (
|
||||
<View style={styles.container}>
|
||||
{
|
||||
headerContent && (
|
||||
headerContent
|
||||
)
|
||||
}
|
||||
{
|
||||
headerImage && (
|
||||
<FastImage
|
||||
@ -61,7 +68,10 @@ const ActionModalView = ({onClose, data}: ActionModalViewProps, ref) => {
|
||||
<View style={styles.textContainer}>
|
||||
<Text style={styles.title}>{title}</Text>
|
||||
{!!body && (
|
||||
<Text style={styles.bodyText}>{body}</Text>
|
||||
<>
|
||||
<Text style={styles.bodyText}>{body}</Text>
|
||||
<Text style={styles.bodyText}>{para}</Text>
|
||||
</>
|
||||
)}
|
||||
|
||||
|
||||
|
@ -37,7 +37,7 @@ import { TouchableWithoutFeedback } from "react-native-gesture-handler";
|
||||
}
|
||||
|
||||
const imgStyle = {
|
||||
width:imgWidth,
|
||||
width:imgWidth - 10,
|
||||
height:imgHeight,
|
||||
backgroundColor: onLoadCalled ? 'transparent' : EStyleSheet.value('$primaryGray')
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ export default EStyleSheet.create({
|
||||
},
|
||||
shadowColor: '#5f5f5fbf',
|
||||
shadowOpacity: 0.1,
|
||||
elevation: 3,
|
||||
// elevation: 3,
|
||||
},
|
||||
icon: {
|
||||
alignSelf: 'center',
|
||||
|
@ -49,7 +49,6 @@ export default EStyleSheet.create({
|
||||
},
|
||||
autocompleteLineContainer: {
|
||||
flexDirection: 'row',
|
||||
paddingHorizontal: 20,
|
||||
zIndex: 999,
|
||||
},
|
||||
autocompleteLabelText: {
|
||||
@ -72,7 +71,8 @@ export default EStyleSheet.create({
|
||||
},
|
||||
autocompleteLabelContainer: {
|
||||
flex: 1,
|
||||
padding: 10,
|
||||
paddingVertical: 10,
|
||||
paddingHorizontal: 16,
|
||||
justifyContent: 'center',
|
||||
color: '$primaryBlack',
|
||||
maxWidth: '$deviceWidth / 2.9',
|
||||
|
@ -145,17 +145,22 @@ class PostDropdownContainer extends PureComponent {
|
||||
});
|
||||
}
|
||||
|
||||
dispatch(showActionModal(
|
||||
intl.formatMessage({id:'report.confirm_report_title'}),
|
||||
intl.formatMessage({id:'report.confirm_report_body'}),
|
||||
[{
|
||||
text:intl.formatMessage({id:'alert.cancel'}),
|
||||
onPress:()=>{}
|
||||
},{
|
||||
text:intl.formatMessage({id:'alert.confirm'}),
|
||||
onPress:_onConfirm
|
||||
}]
|
||||
))
|
||||
dispatch(
|
||||
showActionModal({
|
||||
title: intl.formatMessage({ id: 'report.confirm_report_title' }),
|
||||
body: intl.formatMessage({ id: 'report.confirm_report_body' }),
|
||||
buttons: [
|
||||
{
|
||||
text: intl.formatMessage({ id: 'alert.cancel' }),
|
||||
onPress: () => {},
|
||||
},
|
||||
{
|
||||
text: intl.formatMessage({ id: 'alert.confirm' }),
|
||||
onPress: _onConfirm,
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
|
@ -368,9 +368,9 @@ const CommentBody = ({
|
||||
}}
|
||||
>
|
||||
<VideoPlayer
|
||||
mode={youtubeVideoId ? 'youtube' : 'url'}
|
||||
mode={youtubeVideoId ? 'youtube' : 'uri'}
|
||||
youtubeVideoId={youtubeVideoId}
|
||||
videoUrl={videoUrl}
|
||||
uri={videoUrl}
|
||||
startTime={videoStartTime}
|
||||
/>
|
||||
</ActionsSheetView>
|
||||
|
@ -294,9 +294,9 @@ const PostBody = ({ navigation, body, dispatch, onLoadEnd }) => {
|
||||
}}
|
||||
>
|
||||
<VideoPlayer
|
||||
mode={youtubeVideoId ? 'youtube' : 'url'}
|
||||
mode={youtubeVideoId ? 'youtube' : 'uri'}
|
||||
youtubeVideoId={youtubeVideoId}
|
||||
videoUrl={videoUrl}
|
||||
uri={videoUrl}
|
||||
startTime={videoStartTime}
|
||||
/>
|
||||
</ActionSheetView>
|
||||
|
@ -150,9 +150,9 @@ export const PostHtmlRenderer = memo(
|
||||
} else {
|
||||
return (
|
||||
<VideoPlayer
|
||||
mode={parsedTnode.youtubeId ? 'youtube' : 'url'}
|
||||
mode={parsedTnode.youtubeId ? 'youtube' : 'uri'}
|
||||
contentWidth={contentWidth}
|
||||
videoUrl={parsedTnode.videoHref}
|
||||
uri={parsedTnode.videoHref}
|
||||
youtubeVideoId={parsedTnode.youtubeId}
|
||||
startTime={parsedTnode.startTime}
|
||||
disableAutoplay={true}
|
||||
@ -176,7 +176,7 @@ export const PostHtmlRenderer = memo(
|
||||
}
|
||||
|
||||
//return divided width based on number td tags
|
||||
if (tnode.parent.tagName === 'td') {
|
||||
if (tnode.parent.tagName === 'td' || tnode.parent.tagName === 'th') {
|
||||
const cols = tnode.parent.parent.children.length;
|
||||
return contentWidth / cols;
|
||||
}
|
||||
@ -226,19 +226,6 @@ export const PostHtmlRenderer = memo(
|
||||
// iframe renderer for rendering iframes in body
|
||||
const _iframeRenderer = function IframeRenderer(props) {
|
||||
const iframeProps = useHtmlIframeProps(props);
|
||||
const checkSrcRegex = /(.*?)\.(mp4|webm|ogg)$/gi;
|
||||
const isVideoFormat = iframeProps.source.uri.match(checkSrcRegex);
|
||||
|
||||
//this hack help avoid autoplaying fullscreened iframe videos;
|
||||
const src = isVideoFormat ?
|
||||
{
|
||||
html: `
|
||||
<video width="100%" height="auto" controls>
|
||||
<source src="${iframeProps.source.uri}" type="video/mp4">
|
||||
</video>`,
|
||||
}:{
|
||||
uri: iframeProps.source.uri,
|
||||
};
|
||||
|
||||
//TODO: remove android check logic when fix for react-native-webiew scrollview crash is available
|
||||
//ref: https://github.com/react-native-webview/react-native-webview/issues/2364
|
||||
@ -254,9 +241,10 @@ export const PostHtmlRenderer = memo(
|
||||
)
|
||||
}else{
|
||||
return (
|
||||
|
||||
<VideoPlayer
|
||||
mode='source'
|
||||
source={src}
|
||||
mode='uri'
|
||||
uri={iframeProps.source.uri}
|
||||
contentWidth={contentWidth}
|
||||
/>
|
||||
);
|
||||
|
@ -47,7 +47,6 @@ export default EStyleSheet.create({
|
||||
},
|
||||
autocomplateLineContainer: {
|
||||
flexDirection: 'row',
|
||||
paddingHorizontal: 20,
|
||||
zIndex: 999,
|
||||
},
|
||||
autocomplateLabelText: {
|
||||
@ -70,7 +69,8 @@ export default EStyleSheet.create({
|
||||
},
|
||||
autocomplateLabelContainer: {
|
||||
flex: 1,
|
||||
padding: 10,
|
||||
paddingVertical: 10,
|
||||
paddingHorizontal: 16,
|
||||
justifyContent: 'center',
|
||||
color: '$primaryBlack',
|
||||
maxWidth: '$deviceWidth / 2.9',
|
||||
|
@ -3,13 +3,17 @@ import EStyleSheet from 'react-native-extended-stylesheet';
|
||||
export default EStyleSheet.create({
|
||||
container: {
|
||||
flexDirection: 'row',
|
||||
paddingHorizontal: 20,
|
||||
justifyContent: 'space-between',
|
||||
paddingHorizontal: 16,
|
||||
height: 40,
|
||||
marginVertical: 8,
|
||||
},
|
||||
leftPart: {
|
||||
flex: 1,
|
||||
padding: 10,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'flex-start',
|
||||
color: '$primaryBlack',
|
||||
height: 40,
|
||||
},
|
||||
text: {
|
||||
color: '$primaryBlack',
|
||||
@ -18,6 +22,6 @@ export default EStyleSheet.create({
|
||||
},
|
||||
rightPart: {
|
||||
flex: 2,
|
||||
padding: 10,
|
||||
// padding: 10,
|
||||
},
|
||||
});
|
||||
|
@ -7,8 +7,8 @@ import styles from './transferFormItemStyles';
|
||||
* @prop { type } name - Description....
|
||||
*/
|
||||
|
||||
const TransferFormItemView = ({ rightComponent, label }) => (
|
||||
<View style={styles.container}>
|
||||
const TransferFormItemView = ({ rightComponent, label, containerStyle }) => (
|
||||
<View style={[styles.container, containerStyle]}>
|
||||
<View style={styles.leftPart}>{label && <Text style={styles.text}>{label}</Text>}</View>
|
||||
<View style={styles.rightPart}>{rightComponent && rightComponent()}</View>
|
||||
</View>
|
||||
|
@ -1,39 +1,47 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useRef } from 'react';
|
||||
import { Dimensions } from 'react-native';
|
||||
import { View, StyleSheet, ActivityIndicator } from 'react-native';
|
||||
import WebView from 'react-native-webview';
|
||||
import YoutubeIframe, { InitialPlayerParams } from 'react-native-youtube-iframe';
|
||||
import { WebViewSource } from 'react-native-webview/lib/WebViewTypes';
|
||||
import Video from 'react-native-video';
|
||||
import MediaControls, { PLAYER_STATES } from 'react-native-media-controls';
|
||||
import EStyleSheet from 'react-native-extended-stylesheet';
|
||||
|
||||
interface VideoPlayerProps {
|
||||
mode: 'source'|'youtube'|'url';
|
||||
mode: 'uri' | 'youtube';
|
||||
contentWidth?: number;
|
||||
youtubeVideoId?: string;
|
||||
videoUrl?: string;
|
||||
startTime?: number;
|
||||
source?: WebViewSource;
|
||||
|
||||
uri?: string;
|
||||
//prop for youtube player
|
||||
disableAutoplay?:boolean;
|
||||
disableAutoplay?: boolean;
|
||||
}
|
||||
|
||||
const VideoPlayer = ({
|
||||
youtubeVideoId,
|
||||
videoUrl,
|
||||
startTime,
|
||||
source,
|
||||
contentWidth = Dimensions.get('screen').width,
|
||||
mode,
|
||||
disableAutoplay
|
||||
}: VideoPlayerProps) => {
|
||||
|
||||
const VideoPlayer = ({
|
||||
youtubeVideoId,
|
||||
startTime,
|
||||
uri,
|
||||
contentWidth = Dimensions.get('screen').width,
|
||||
mode,
|
||||
disableAutoplay,
|
||||
}: VideoPlayerProps) => {
|
||||
const PLAYER_HEIGHT = contentWidth * (9 / 16);
|
||||
const checkSrcRegex = /(.*?)\.(mp4|webm|ogg)$/gi;
|
||||
const isExtensionType = mode === 'uri' ? uri.match(checkSrcRegex) : false;
|
||||
|
||||
const videoPlayer = useRef(null);
|
||||
const [currentTime, setCurrentTime] = useState(0);
|
||||
const [duration, setDuration] = useState(0);
|
||||
const [isFullScreen, setIsFullScreen] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [paused, setPaused] = useState(true);
|
||||
const [playerState, setPlayerState] = useState(PLAYER_STATES.PAUSED);
|
||||
const [screenType, setScreenType] = useState('contain');
|
||||
|
||||
// react-native-youtube-iframe handlers
|
||||
const [shouldPlay, setShouldPlay] = useState(false);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const _onReady = () => {
|
||||
setLoading(false);
|
||||
setIsLoading(false);
|
||||
setShouldPlay(disableAutoplay ? false : true);
|
||||
console.log('ready');
|
||||
};
|
||||
@ -45,13 +53,101 @@ const VideoPlayer = ({
|
||||
|
||||
const _onError = () => {
|
||||
console.log('error!');
|
||||
setLoading(false);
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
const initialParams: InitialPlayerParams = {
|
||||
start: startTime,
|
||||
};
|
||||
|
||||
// react-native-video player handlers
|
||||
const onSeek = (seek) => {
|
||||
videoPlayer.current.seek(seek);
|
||||
};
|
||||
|
||||
const onPaused = (playerState) => {
|
||||
setPaused(!paused);
|
||||
setPlayerState(playerState);
|
||||
};
|
||||
|
||||
const onReplay = () => {
|
||||
setPlayerState(PLAYER_STATES.PLAYING);
|
||||
videoPlayer.current.seek(0);
|
||||
};
|
||||
|
||||
const onProgress = (data) => {
|
||||
if (!isLoading && playerState !== PLAYER_STATES.ENDED) {
|
||||
setCurrentTime(data.currentTime);
|
||||
}
|
||||
};
|
||||
|
||||
const onLoad = (data) => {
|
||||
setDuration(data.duration);
|
||||
videoPlayer.current.seek(0);
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
const onLoadStart = () => setIsLoading(true);
|
||||
|
||||
const onEnd = () => setPlayerState(PLAYER_STATES.ENDED);
|
||||
|
||||
const onError = () => alert('Error while playing');
|
||||
|
||||
const exitFullScreen = () => {
|
||||
setIsFullScreen(false);
|
||||
};
|
||||
|
||||
const enterFullScreen = () => {
|
||||
setIsFullScreen(true);
|
||||
};
|
||||
|
||||
const onFullScreen = () => {
|
||||
setIsFullScreen(true);
|
||||
if (screenType == 'contain') setScreenType('cover');
|
||||
else setScreenType('contain');
|
||||
};
|
||||
|
||||
const onSeeking = (currentTime) => setCurrentTime(currentTime);
|
||||
|
||||
const _renderVideoplayerWithControls = () => {
|
||||
return (
|
||||
<View style={{ flex: 1 }}>
|
||||
<Video
|
||||
source={{
|
||||
uri: uri,
|
||||
}}
|
||||
onEnd={onEnd}
|
||||
onLoad={onLoad}
|
||||
onLoadStart={onLoadStart}
|
||||
onProgress={onProgress}
|
||||
onError={onError}
|
||||
paused={paused}
|
||||
ref={videoPlayer}
|
||||
resizeMode={'cover'}
|
||||
fullscreen={isFullScreen}
|
||||
style={styles.mediaPlayer}
|
||||
volume={10}
|
||||
onFullscreenPlayerDidPresent={enterFullScreen}
|
||||
onFullscreenPlayerDidDismiss={exitFullScreen}
|
||||
/>
|
||||
<MediaControls
|
||||
duration={duration}
|
||||
isLoading={isLoading}
|
||||
mainColor={'#3c4449'}
|
||||
onFullScreen={onFullScreen}
|
||||
onPaused={onPaused}
|
||||
onReplay={onReplay}
|
||||
onSeek={onSeek}
|
||||
onSeeking={onSeeking}
|
||||
playerState={playerState}
|
||||
progress={currentTime}
|
||||
isFullScreen={isFullScreen}
|
||||
fadeOutDelay={paused ? Number.MAX_VALUE : 3000}
|
||||
containerStyle={{}}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{mode === 'youtube' && youtubeVideoId && (
|
||||
@ -67,29 +163,35 @@ const VideoPlayer = ({
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
{((mode === 'source' && source) || (mode === 'url' && videoUrl)) && (
|
||||
<View style={{ height: PLAYER_HEIGHT }}>
|
||||
<WebView
|
||||
scalesPageToFit={true}
|
||||
bounces={false}
|
||||
javaScriptEnabled={true}
|
||||
automaticallyAdjustContentInsets={false}
|
||||
onLoadEnd={() => {
|
||||
setLoading(false);
|
||||
}}
|
||||
onLoadStart={() => {
|
||||
setLoading(true);
|
||||
}}
|
||||
source={source || { uri: videoUrl }}
|
||||
style={{ width: contentWidth, height: PLAYER_HEIGHT}}
|
||||
startInLoadingState={true}
|
||||
onShouldStartLoadWithRequest={() => true}
|
||||
mediaPlaybackRequiresUserAction={true}
|
||||
allowsInlineMediaPlayback={true}
|
||||
/>
|
||||
{mode === 'uri' && uri && (
|
||||
<View style={[styles.playerWrapper, { height: PLAYER_HEIGHT }]}>
|
||||
{isExtensionType ? (
|
||||
_renderVideoplayerWithControls()
|
||||
) : (
|
||||
<WebView
|
||||
scalesPageToFit={true}
|
||||
bounces={false}
|
||||
javaScriptEnabled={true}
|
||||
automaticallyAdjustContentInsets={false}
|
||||
onLoadEnd={() => {
|
||||
setIsLoading(false);
|
||||
}}
|
||||
onLoadStart={() => {
|
||||
setIsLoading(true);
|
||||
}}
|
||||
source={{ uri: uri }}
|
||||
style={[styles.barkBackground, { width: contentWidth, height: PLAYER_HEIGHT }]}
|
||||
startInLoadingState={true}
|
||||
onShouldStartLoadWithRequest={() => true}
|
||||
mediaPlaybackRequiresUserAction={true}
|
||||
allowsInlineMediaPlayback={true}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
{loading && <ActivityIndicator style={styles.activityIndicator} />}
|
||||
{isLoading && (
|
||||
<ActivityIndicator size={'large'} color="white" style={styles.activityIndicator} />
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@ -104,9 +206,30 @@ const styles = StyleSheet.create({
|
||||
position: 'absolute',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
top: 0,
|
||||
top: 25,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
},
|
||||
toolbar: {
|
||||
marginTop: 30,
|
||||
backgroundColor: 'white',
|
||||
padding: 10,
|
||||
borderRadius: 5,
|
||||
},
|
||||
playerWrapper: {
|
||||
backgroundColor: 'black',
|
||||
},
|
||||
mediaPlayer: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
backgroundColor: 'black',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
barkBackground: {
|
||||
backgroundColor: 'black',
|
||||
},
|
||||
});
|
||||
|
@ -23,6 +23,7 @@ api.interceptors.request.use((request) => {
|
||||
|| request.url === '/auth-api/hs-token-refresh'
|
||||
|| request.url === '/private-api/promoted-entries'
|
||||
|| request.url.startsWith('private-api/leaderboard')
|
||||
|| request.url.startsWith('/private-api/received-vesting/')
|
||||
){
|
||||
return request
|
||||
}
|
||||
|
@ -574,7 +574,20 @@
|
||||
"incoming_funds": "Incoming Funds",
|
||||
"stop": "Stop",
|
||||
"sc_power_down_error": "This feature is not implemented for Hivesigner login, yet",
|
||||
"address_view": "View address"
|
||||
"address_view": "View address",
|
||||
"already_delegated": "Already delegated to ",
|
||||
"remain_hp": "Remaining HP",
|
||||
"account_detail_head": "Account Details",
|
||||
"account_detail_subhead": "Enter username for HIVE Power delegation",
|
||||
"delegat_detail_head": "Delegation Details",
|
||||
"delegat_detail_subhead": "New mount overwrites already delegated HIVE Power ",
|
||||
"new_amount": "New Amount",
|
||||
"review": "REVIEW",
|
||||
"confirm": "Confirm Delegation",
|
||||
"confirm_summary": "Delegate {hp} HP ({vests} VESTS) To @{delegator} from @{delegatee} ",
|
||||
"confirm_summary_para": "This will overwrite your previous delegation of {prev} HP to this user.",
|
||||
"username_alert": "Username Error!",
|
||||
"username_alert_detail": "Please select different username"
|
||||
},
|
||||
"boost": {
|
||||
"title": "Get Points",
|
||||
|
@ -439,10 +439,10 @@ class ProfileContainer extends Component {
|
||||
};
|
||||
|
||||
dispatch(
|
||||
showActionModal(
|
||||
intl.formatMessage({ id: 'report.confirm_report_title' }),
|
||||
intl.formatMessage({ id: 'report.confirm_report_body' }),
|
||||
[
|
||||
showActionModal({
|
||||
title: intl.formatMessage({ id: 'report.confirm_report_title' }),
|
||||
body: intl.formatMessage({ id: 'report.confirm_report_body' }),
|
||||
buttons: [
|
||||
{
|
||||
text: intl.formatMessage({ id: 'alert.cancel' }),
|
||||
onPress: () => {},
|
||||
@ -452,7 +452,7 @@ class ProfileContainer extends Component {
|
||||
onPress: _onConfirm,
|
||||
},
|
||||
],
|
||||
),
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -221,7 +221,15 @@ class TransferContainer extends Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { accounts, navigation, children, hivePerMVests, currentAccount } = this.props;
|
||||
const {
|
||||
accounts,
|
||||
navigation,
|
||||
children,
|
||||
hivePerMVests,
|
||||
currentAccount,
|
||||
actionModalVisible,
|
||||
dispatch,
|
||||
} = this.props;
|
||||
const { balance, fundType, selectedAccount, tokenAddress } = this.state;
|
||||
|
||||
const transferType = navigation.getParam('transferType', '');
|
||||
@ -229,6 +237,7 @@ class TransferContainer extends Component {
|
||||
return (
|
||||
children &&
|
||||
children({
|
||||
dispatch,
|
||||
accounts,
|
||||
balance,
|
||||
tokenAddress,
|
||||
@ -236,6 +245,7 @@ class TransferContainer extends Component {
|
||||
transferType,
|
||||
selectedAccount,
|
||||
hivePerMVests,
|
||||
actionModalVisible,
|
||||
fetchBalance: this.fetchBalance,
|
||||
getAccountsWithUsername: this._getAccountsWithUsername,
|
||||
transferToAccount: this._transferToAccount,
|
||||
@ -253,6 +263,7 @@ const mapStateToProps = (state) => ({
|
||||
currentAccount: state.account.currentAccount,
|
||||
pinCode: state.application.pin,
|
||||
hivePerMVests: state.account.globalProps.hivePerMVests,
|
||||
actionModalVisible: state.ui.actionModalVisible,
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(TransferContainer));
|
||||
|
@ -6,6 +6,15 @@ import bugsnagInstance from '../../config/bugsnag';
|
||||
import { SERVER_LIST } from '../../constants/options/api';
|
||||
import { parsePost } from '../../utils/postParser';
|
||||
import { extractMetadata, makeJsonMetadata } from '../../utils/editor';
|
||||
import { ReceivedVestingShare, ReceivedVestingShares } from './ecency.types';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* ************************************
|
||||
* CURRENCY APIS IMPLEMENTATION
|
||||
* ************************************
|
||||
*/
|
||||
|
||||
export const getCurrencyRate = (currency) =>
|
||||
api
|
||||
@ -27,6 +36,24 @@ export const getCurrencyTokenRate = (currency, token) =>
|
||||
});
|
||||
|
||||
|
||||
export const getReceivedVestingShares = async (username: string):Promise<ReceivedVestingShare[]> => {
|
||||
try{
|
||||
const res = await ecencyApi.get(`/private-api/received-vesting/${username}`);
|
||||
console.log("Vesting Shares User", username, res.data);
|
||||
if(!res.data || !res.data.list){
|
||||
throw new Error("No vesting shares for user")
|
||||
}
|
||||
return res.data.list;
|
||||
} catch (error){
|
||||
bugsnagInstance.notify(error);
|
||||
console.warn(error);
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* returns list of saved drafts on ecency server
|
||||
@ -733,3 +760,4 @@ export const signUp = async (username:string, email:string, referral?:string) =>
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
|
6
src/providers/ecency/ecency.types.ts
Normal file
6
src/providers/ecency/ecency.types.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export interface ReceivedVestingShare {
|
||||
delegator:string;
|
||||
delegatee:string;
|
||||
vesting_shares:string;
|
||||
timestamp:string;
|
||||
}
|
@ -23,15 +23,11 @@ export const toastNotification = (payload:string) => ({
|
||||
type: TOAST_NOTIFICATION,
|
||||
});
|
||||
|
||||
export const showActionModal = (title:string, body?:string, buttons?:AlertButton[], headerImage?:any, onClosed?:()=>void) => ({
|
||||
export const showActionModal = (payload:any) => ({
|
||||
payload: {
|
||||
actionModalVisible: new Date().getTime(),
|
||||
actionModalData: {
|
||||
title,
|
||||
body,
|
||||
buttons,
|
||||
headerImage,
|
||||
onClosed,
|
||||
...payload
|
||||
},
|
||||
},
|
||||
type: SHOW_ACTION_MODAL,
|
||||
|
@ -377,10 +377,13 @@ class ApplicationContainer extends Component {
|
||||
|
||||
if (parseVersionNumber(remoteVersion) > parseVersionNumber(VersionNumber.appVersion)) {
|
||||
dispatch(
|
||||
showActionModal(
|
||||
intl.formatMessage({ id: 'alert.update_available_title' }, { version: remoteVersion }),
|
||||
intl.formatMessage({ id: 'alert.update_available_body' }),
|
||||
[
|
||||
showActionModal({
|
||||
title: intl.formatMessage(
|
||||
{ id: 'alert.update_available_title' },
|
||||
{ version: remoteVersion },
|
||||
),
|
||||
body: intl.formatMessage({ id: 'alert.update_available_body' }),
|
||||
buttons: [
|
||||
{
|
||||
text: intl.formatMessage({ id: 'alert.remind_later' }),
|
||||
onPress: () => {
|
||||
@ -400,8 +403,8 @@ class ApplicationContainer extends Component {
|
||||
},
|
||||
},
|
||||
],
|
||||
require('../../../assets/phone-holding.png'),
|
||||
),
|
||||
headerImage: require('../../../assets/phone-holding.png'),
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
@ -22,7 +22,9 @@ const Transfer = ({ navigation }) => (
|
||||
accountType,
|
||||
currentAccountName,
|
||||
hivePerMVests,
|
||||
actionModalVisible,
|
||||
setWithdrawVestingRoute,
|
||||
dispatch,
|
||||
}) => {
|
||||
switch (transferType) {
|
||||
case 'transfer_token':
|
||||
@ -61,6 +63,8 @@ const Transfer = ({ navigation }) => (
|
||||
accountType={accountType}
|
||||
handleOnModalClose={handleOnModalClose}
|
||||
hivePerMVests={hivePerMVests}
|
||||
actionModalVisible={actionModalVisible}
|
||||
dispatch={dispatch}
|
||||
/>
|
||||
);
|
||||
case 'power_down':
|
||||
|
@ -1,11 +1,14 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import { View, Text } from 'react-native';
|
||||
import { View, Text, Platform, ScrollView, KeyboardAvoidingView, Alert } from 'react-native';
|
||||
import { WebView } from 'react-native-webview';
|
||||
import { injectIntl } from 'react-intl';
|
||||
import Slider from '@esteemapp/react-native-slider';
|
||||
import get from 'lodash/get';
|
||||
import { View as AnimatedView } from 'react-native-animatable';
|
||||
import { TouchableOpacity, FlatList } from 'react-native-gesture-handler';
|
||||
|
||||
// Constants
|
||||
import { debounce } from 'lodash';
|
||||
import AUTH_TYPE from '../../../constants/authType';
|
||||
import { hsOptions } from '../../../constants/hsOptions';
|
||||
|
||||
@ -20,47 +23,77 @@ import {
|
||||
Icon,
|
||||
Modal,
|
||||
} from '../../../components';
|
||||
|
||||
import parseToken from '../../../utils/parseToken';
|
||||
import { isEmptyDate } from '../../../utils/time';
|
||||
import { vestsToHp } from '../../../utils/conversions';
|
||||
|
||||
// Styles
|
||||
import styles from './transferStyles';
|
||||
import { OptionsModal } from '../../../components/atoms';
|
||||
// Redux
|
||||
import { showActionModal } from '../../../redux/actions/uiAction';
|
||||
// Utils
|
||||
import { getReceivedVestingShares } from '../../../providers/ecency/ecency';
|
||||
import parseToken from '../../../utils/parseToken';
|
||||
import { isEmptyDate } from '../../../utils/time';
|
||||
import { hpToVests, vestsToHp } from '../../../utils/conversions';
|
||||
import parseAsset from '../../../utils/parseAsset';
|
||||
|
||||
class DelegateScreen extends Component {
|
||||
_handleOnAmountChange = debounce(
|
||||
async (state, amount) => {
|
||||
let _amount = amount.toString();
|
||||
if (_amount.includes(',')) {
|
||||
_amount = amount.replace(',', '.');
|
||||
}
|
||||
|
||||
this._setState(state, _amount);
|
||||
},
|
||||
1000,
|
||||
{ leading: true },
|
||||
);
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
amount: 0,
|
||||
isAmountValid: true,
|
||||
hp: 0.0,
|
||||
isTransfering: false,
|
||||
from: props.currentAccountName,
|
||||
destination: '',
|
||||
steemConnectTransfer: false,
|
||||
usersResult: [],
|
||||
step: 1,
|
||||
delegatedHP: 0,
|
||||
};
|
||||
|
||||
this.startActionSheet = React.createRef();
|
||||
this.destinationTextInput = React.createRef();
|
||||
this.amountTextInput = React.createRef();
|
||||
}
|
||||
|
||||
// Component Life Cycles
|
||||
// Component Lifecycles
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (prevState.from !== this.state.from) {
|
||||
this._fetchReceivedVestingShare();
|
||||
}
|
||||
}
|
||||
|
||||
// Component Functions
|
||||
_setState = (key, value) => {
|
||||
const { getAccountsWithUsername, balance } = this.props;
|
||||
|
||||
if (key) {
|
||||
switch (key) {
|
||||
case 'destination':
|
||||
getAccountsWithUsername(value).then((res) => {
|
||||
const isValid = res.includes(value);
|
||||
|
||||
this.setState({ usersResult: [...res] });
|
||||
this.setState({ isUsernameValid: isValid });
|
||||
});
|
||||
this.setState({ [key]: value });
|
||||
if (!value) {
|
||||
this.setState({ destination: '', step: 1 });
|
||||
}
|
||||
|
||||
break;
|
||||
case 'amount':
|
||||
if (parseFloat(Number(value)) <= parseFloat(balance)) {
|
||||
if (parseFloat(value) <= parseFloat(balance)) {
|
||||
this.setState({ [key]: value });
|
||||
}
|
||||
break;
|
||||
@ -72,6 +105,29 @@ class DelegateScreen extends Component {
|
||||
}
|
||||
};
|
||||
|
||||
_handleAmountChange = (hp, availableVests) => {
|
||||
if (!hp) {
|
||||
this.setState({ step: 2, hp: 0.0, amount: 0, isAmountValid: false });
|
||||
return;
|
||||
}
|
||||
const parsedValue = parseFloat(hp);
|
||||
const { hivePerMVests } = this.props;
|
||||
const vestsForHp = hpToVests(hp, hivePerMVests);
|
||||
const totalHP = vestsToHp(availableVests, hivePerMVests).toFixed(3);
|
||||
if (Number.isNaN(parsedValue)) {
|
||||
this.setState({ amount: 0, hp: 0.0, step: 2, isAmountValid: false });
|
||||
} else if (parsedValue >= totalHP) {
|
||||
this.setState({
|
||||
amount: hpToVests(totalHP, hivePerMVests),
|
||||
hp: totalHP,
|
||||
step: 2,
|
||||
isAmountValid: false,
|
||||
});
|
||||
} else {
|
||||
this.setState({ amount: vestsForHp, hp: parsedValue, step: 2, isAmountValid: true });
|
||||
}
|
||||
};
|
||||
|
||||
_handleTransferAction = () => {
|
||||
const { transferToAccount, accountType } = this.props;
|
||||
const { from, destination, amount } = this.state;
|
||||
@ -85,22 +141,111 @@ class DelegateScreen extends Component {
|
||||
}
|
||||
};
|
||||
|
||||
_handleOnAmountChange = (state, amount) => {
|
||||
let _amount = amount.toString();
|
||||
if (_amount.includes(',')) {
|
||||
_amount = amount.replace(',', '.');
|
||||
_fetchReceivedVestingShare = async () => {
|
||||
try {
|
||||
const { hivePerMVests } = this.props;
|
||||
const delegateeUser = this.state.destination;
|
||||
const vestingShares = await getReceivedVestingShares(delegateeUser);
|
||||
if (vestingShares && vestingShares.length) {
|
||||
const curShare = vestingShares.find((item) => item.delegator === this.state.from);
|
||||
if (curShare) {
|
||||
const vest_shares = parseAsset(curShare.vesting_shares);
|
||||
this.setState({
|
||||
delegatedHP: vestsToHp(vest_shares.amount, hivePerMVests).toFixed(3),
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
delegatedHP: 0,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.setState({
|
||||
delegatedHP: 0,
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(err);
|
||||
}
|
||||
|
||||
this._setState(state, _amount);
|
||||
};
|
||||
|
||||
_handleOnDropdownChange = (value) => {
|
||||
const { fetchBalance } = this.props;
|
||||
|
||||
const { fetchBalance, intl } = this.props;
|
||||
const { destination } = this.state;
|
||||
if (value === destination) {
|
||||
Alert.alert(
|
||||
intl.formatMessage({ id: 'transfer.username_alert' }),
|
||||
intl.formatMessage({ id: 'transfer.username_alert_detail' }),
|
||||
);
|
||||
this.setState({ step: 1, destination: '' });
|
||||
return;
|
||||
}
|
||||
fetchBalance(value);
|
||||
this.setState({ from: value, amount: 0 });
|
||||
};
|
||||
|
||||
_handleSliderValueChange = (value, availableVestingShares) => {
|
||||
const { hivePerMVests } = this.props;
|
||||
if (value === availableVestingShares) {
|
||||
this.setState({ isAmountValid: false });
|
||||
}
|
||||
if (value !== availableVestingShares) {
|
||||
this.setState({
|
||||
step: 2,
|
||||
isAmountValid: true,
|
||||
amount: value,
|
||||
hp: vestsToHp(value, hivePerMVests).toFixed(3),
|
||||
});
|
||||
} else {
|
||||
this.setState({ amount: value, hp: vestsToHp(value, hivePerMVests).toFixed(3), step: 2 });
|
||||
}
|
||||
};
|
||||
|
||||
_handleNext = () => {
|
||||
const { step, hp, amount, destination, from, delegatedHP } = this.state;
|
||||
const { dispatch, intl } = this.props;
|
||||
if (step === 1) {
|
||||
// this.setState({ step: 2 });
|
||||
} else {
|
||||
// this.amountTextInput.current.blur();
|
||||
let body =
|
||||
intl.formatMessage(
|
||||
{ id: 'transfer.confirm_summary' },
|
||||
{
|
||||
hp: hp,
|
||||
vests: amount.toFixed(3),
|
||||
delegatee: from,
|
||||
delegator: destination,
|
||||
},
|
||||
) +
|
||||
(delegatedHP
|
||||
? `\n${intl.formatMessage(
|
||||
{ id: 'transfer.confirm_summary_para' },
|
||||
{
|
||||
prev: delegatedHP,
|
||||
},
|
||||
)}`
|
||||
: '');
|
||||
|
||||
dispatch(
|
||||
showActionModal({
|
||||
title: intl.formatMessage({ id: 'transfer.confirm' }),
|
||||
body,
|
||||
buttons: [
|
||||
{
|
||||
text: intl.formatMessage({ id: 'alert.cancel' }),
|
||||
onPress: () => console.log('Cancel'),
|
||||
},
|
||||
{
|
||||
text: intl.formatMessage({ id: 'alert.confirm' }),
|
||||
onPress: () => this._handleTransferAction(),
|
||||
},
|
||||
],
|
||||
headerContent: this._renderToFromAvatars(),
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
// Note: dropdown for user account selection. can be used in later implementaion
|
||||
_renderDropdown = (accounts, currentAccountName) => (
|
||||
<DropdownButton
|
||||
dropdownButtonStyle={styles.dropdownButtonStyle}
|
||||
@ -115,22 +260,130 @@ class DelegateScreen extends Component {
|
||||
/>
|
||||
);
|
||||
|
||||
_renderInput = (placeholder, state, keyboardType, isTextArea) => (
|
||||
<TextInput
|
||||
style={[isTextArea ? styles.textarea : styles.input]}
|
||||
onChangeText={(amount) => this._handleOnAmountChange(state, amount)}
|
||||
value={this.state[state]}
|
||||
placeholder={placeholder}
|
||||
placeholderTextColor="#c1c5c7"
|
||||
autoCapitalize="none"
|
||||
multiline={isTextArea}
|
||||
numberOfLines={isTextArea ? 4 : 1}
|
||||
keyboardType={keyboardType}
|
||||
_renderUsersDropdownItem = ({ item }) => {
|
||||
const username = item;
|
||||
const { from } = this.state;
|
||||
const { intl } = this.props;
|
||||
|
||||
const _onItemPress = () => {
|
||||
if (username === from) {
|
||||
Alert.alert(
|
||||
intl.formatMessage({ id: 'transfer.username_alert' }),
|
||||
intl.formatMessage({ id: 'transfer.username_alert_detail' }),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({ destination: username, usersResult: [], step: 2 }, () => {
|
||||
//since method uses destination from state it sould be called
|
||||
//after state has been updated successfully
|
||||
this._fetchReceivedVestingShare();
|
||||
});
|
||||
|
||||
this.destinationTextInput.current?.blur();
|
||||
};
|
||||
|
||||
return (
|
||||
<TouchableOpacity onPress={_onItemPress} style={styles.usersDropItemRow}>
|
||||
<UserAvatar username={username} noAction />
|
||||
<Text style={styles.usersDropItemRowText}>{username}</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
_renderUsersDropdown = () => (
|
||||
<FlatList
|
||||
data={this.state.usersResult}
|
||||
keyboardShouldPersistTaps="always"
|
||||
renderItem={this._renderUsersDropdownItem}
|
||||
keyExtractor={(item) => `searched-user-${item}`}
|
||||
style={styles.usersDropdown}
|
||||
ListFooterComponent={<View />}
|
||||
ListFooterComponentStyle={{ height: 20 }} //this fixes the last item visibility bug
|
||||
/>
|
||||
);
|
||||
|
||||
_renderInput = (placeholder, state, keyboardType, availableVestingShares, isTextArea) => {
|
||||
const { isAmountValid } = this.state;
|
||||
switch (state) {
|
||||
case 'from':
|
||||
return (
|
||||
<TextInput
|
||||
style={[styles.input]}
|
||||
value={this.state[state]}
|
||||
placeholder={placeholder}
|
||||
placeholderTextColor="#c1c5c7"
|
||||
autoCapitalize="none"
|
||||
multiline={isTextArea}
|
||||
numberOfLines={isTextArea ? 4 : 1}
|
||||
keyboardType={keyboardType}
|
||||
innerRef={this.destinationTextInput}
|
||||
editable={false}
|
||||
/>
|
||||
);
|
||||
case 'destination':
|
||||
return (
|
||||
<View style={styles.transferToContainer}>
|
||||
<TextInput
|
||||
style={[styles.input]}
|
||||
onChangeText={(value) => {
|
||||
this.setState({ destination: value, step: 1 });
|
||||
this._handleOnAmountChange(state, value);
|
||||
}}
|
||||
value={this.state[state]}
|
||||
placeholder={placeholder}
|
||||
placeholderTextColor="#c1c5c7"
|
||||
autoCapitalize="none"
|
||||
multiline={isTextArea}
|
||||
numberOfLines={isTextArea ? 4 : 1}
|
||||
keyboardType={keyboardType}
|
||||
autoFocus={true}
|
||||
innerRef={this.destinationTextInput}
|
||||
/>
|
||||
|
||||
<View style={styles.usersDropdownContainer}>
|
||||
{this.state.destination !== '' &&
|
||||
this.state.usersResult.length !== 0 &&
|
||||
this._renderUsersDropdown()}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
case 'amount':
|
||||
return (
|
||||
<TextInput
|
||||
style={[styles.amountInput, !isAmountValid && styles.error]}
|
||||
onChangeText={(amount) => {
|
||||
this._handleAmountChange(amount, availableVestingShares);
|
||||
}}
|
||||
value={this.state.hp}
|
||||
placeholder={placeholder}
|
||||
placeholderTextColor="#c1c5c7"
|
||||
autoCapitalize="none"
|
||||
multiline={isTextArea}
|
||||
numberOfLines={isTextArea ? 4 : 1}
|
||||
keyboardType={keyboardType}
|
||||
innerRef={this.amountTextInput}
|
||||
blurOnSubmit={false}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
null;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
_renderInformationText = (text) => <Text style={styles.amountText}>{text}</Text>;
|
||||
|
||||
_renderToFromAvatars = () => {
|
||||
const { destination, from } = this.state;
|
||||
return (
|
||||
<View style={styles.toFromAvatarsContainer}>
|
||||
<UserAvatar username={from} size="xl" style={styles.userAvatar} noAction />
|
||||
<Icon style={styles.icon} name="arrow-forward" iconType="MaterialIcons" />
|
||||
<UserAvatar username={destination} size="xl" style={styles.userAvatar} noAction />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
render() {
|
||||
const {
|
||||
intl,
|
||||
@ -139,9 +392,20 @@ class DelegateScreen extends Component {
|
||||
selectedAccount,
|
||||
handleOnModalClose,
|
||||
hivePerMVests,
|
||||
actionModalVisible,
|
||||
accountType,
|
||||
} = this.props;
|
||||
const { amount, isTransfering, from, destination, steemConnectTransfer } = this.state;
|
||||
const {
|
||||
amount,
|
||||
isTransfering,
|
||||
from,
|
||||
destination,
|
||||
steemConnectTransfer,
|
||||
step,
|
||||
delegatedHP,
|
||||
hp,
|
||||
isAmountValid,
|
||||
} = this.state;
|
||||
let availableVestingShares = 0;
|
||||
if (!isEmptyDate(get(selectedAccount, 'next_vesting_withdrawal'))) {
|
||||
// powering down
|
||||
@ -161,67 +425,126 @@ class DelegateScreen extends Component {
|
||||
const path = `sign/delegate-vesting-shares?delegator=${from}&delegatee=${destination}&vesting_shares=${encodeURIComponent(
|
||||
fixedAmount,
|
||||
)}`;
|
||||
const totalHP = vestsToHp(availableVestingShares, hivePerMVests);
|
||||
const _renderSlider = () => (
|
||||
<View style={styles.sliderBox}>
|
||||
<View style={styles.emptyBox} />
|
||||
<View style={styles.sliderContainer}>
|
||||
<Slider
|
||||
style={styles.slider}
|
||||
trackStyle={styles.track}
|
||||
thumbStyle={styles.thumb}
|
||||
minimumTrackTintColor="#357ce6"
|
||||
thumbTintColor="#007ee5"
|
||||
maximumValue={availableVestingShares}
|
||||
value={amount}
|
||||
onValueChange={(value) => this._handleSliderValueChange(value, availableVestingShares)}
|
||||
/>
|
||||
<View style={styles.sliderAmountContainer}>
|
||||
<Text style={styles.amountText}>{`MAX ${totalHP.toFixed(3)} HP`}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
||||
const spCalculated = vestsToHp(amount, hivePerMVests);
|
||||
const _renderStepOne = () => (
|
||||
<View style={styles.stepOneContainer}>
|
||||
<Text style={styles.sectionHeading}>
|
||||
{intl.formatMessage({ id: 'transfer.account_detail_head' })}
|
||||
</Text>
|
||||
<Text style={styles.sectionSubheading}>
|
||||
{intl.formatMessage({ id: 'transfer.account_detail_subhead' })}
|
||||
</Text>
|
||||
<TransferFormItem
|
||||
containerStyle={{ marginTop: 32 }}
|
||||
label={intl.formatMessage({ id: 'transfer.from' })}
|
||||
rightComponent={() =>
|
||||
this._renderInput(intl.formatMessage({ id: 'transfer.from' }), 'from', 'default')
|
||||
}
|
||||
/>
|
||||
<TransferFormItem
|
||||
label={intl.formatMessage({ id: 'transfer.to' })}
|
||||
rightComponent={() =>
|
||||
this._renderInput(
|
||||
intl.formatMessage({ id: 'transfer.to_placeholder' }),
|
||||
'destination',
|
||||
'default',
|
||||
)
|
||||
}
|
||||
containerStyle={styles.elevate}
|
||||
/>
|
||||
{this._renderToFromAvatars()}
|
||||
</View>
|
||||
);
|
||||
|
||||
const _renderStepTwo = () => (
|
||||
<AnimatedView animation="bounceInRight" delay={500} useNativeDriver>
|
||||
<View style={styles.stepTwoContainer}>
|
||||
<Text style={styles.sectionHeading}>
|
||||
{intl.formatMessage({ id: 'transfer.delegat_detail_head' })}
|
||||
</Text>
|
||||
<Text style={styles.sectionSubheading}>
|
||||
{intl.formatMessage({ id: 'transfer.delegat_detail_subhead' })}
|
||||
</Text>
|
||||
<View style={styles.alreadyDelegateRow}>
|
||||
<Text style={styles.sectionSubheading}>
|
||||
{`${intl.formatMessage({ id: 'transfer.already_delegated' })} @${destination}`}
|
||||
</Text>
|
||||
<Text style={styles.sectionSubheading}>{`${delegatedHP} HP`}</Text>
|
||||
</View>
|
||||
<TransferFormItem
|
||||
label={intl.formatMessage({ id: 'transfer.new_amount' })}
|
||||
rightComponent={() =>
|
||||
this._renderInput(
|
||||
intl.formatMessage({ id: 'transfer.amount' }),
|
||||
'amount',
|
||||
'decimal-pad',
|
||||
availableVestingShares,
|
||||
null,
|
||||
null,
|
||||
200,
|
||||
)
|
||||
}
|
||||
containerStyle={styles.paddBottom}
|
||||
/>
|
||||
{_renderSlider()}
|
||||
</View>
|
||||
</AnimatedView>
|
||||
);
|
||||
const _renderMainBtn = () => (
|
||||
<View style={styles.stepThreeContainer}>
|
||||
<MainButton
|
||||
style={styles.button}
|
||||
onPress={this._handleNext}
|
||||
isLoading={isTransfering}
|
||||
isDisable={!isAmountValid || step === 1}
|
||||
>
|
||||
<Text style={styles.buttonText}>
|
||||
{step === 2
|
||||
? intl.formatMessage({ id: 'transfer.review' })
|
||||
: intl.formatMessage({ id: 'transfer.next' })}
|
||||
</Text>
|
||||
</MainButton>
|
||||
</View>
|
||||
);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<BasicHeader title={intl.formatMessage({ id: 'transfer.delegate' })} />
|
||||
<View style={styles.container}>
|
||||
<View style={styles.topContent}>
|
||||
<UserAvatar username={from} size="xl" style={styles.userAvatar} noAction />
|
||||
<Icon style={styles.icon} name="arrow-forward" iconType="MaterialIcons" />
|
||||
<UserAvatar username={destination} size="xl" style={styles.userAvatar} noAction />
|
||||
</View>
|
||||
<View style={styles.middleContent}>
|
||||
<TransferFormItem
|
||||
label={intl.formatMessage({ id: 'transfer.from' })}
|
||||
rightComponent={() => this._renderDropdown(accounts, currentAccountName)}
|
||||
/>
|
||||
<TransferFormItem
|
||||
label={intl.formatMessage({ id: 'transfer.to' })}
|
||||
rightComponent={() =>
|
||||
this._renderInput(
|
||||
intl.formatMessage({ id: 'transfer.to_placeholder' }),
|
||||
'destination',
|
||||
'default',
|
||||
)
|
||||
}
|
||||
/>
|
||||
<TransferFormItem
|
||||
label={intl.formatMessage({ id: 'transfer.amount' })}
|
||||
rightComponent={() => this._renderInformationText(`${amount.toFixed(6)} VESTS`)}
|
||||
/>
|
||||
<KeyboardAvoidingView
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
||||
style={styles.fillSpace}
|
||||
keyboardShouldPersistTaps
|
||||
>
|
||||
<ScrollView keyboardShouldPersistTaps contentContainerStyle={styles.grow}>
|
||||
<View style={styles.container}>
|
||||
{step >= 1 && _renderStepOne()}
|
||||
{step >= 2 && _renderStepTwo()}
|
||||
|
||||
<TransferFormItem
|
||||
rightComponent={() => this._renderInformationText(`${spCalculated.toFixed(3)} HP`)}
|
||||
/>
|
||||
<Slider
|
||||
style={styles.slider}
|
||||
trackStyle={styles.track}
|
||||
thumbStyle={styles.thumb}
|
||||
minimumTrackTintColor="#357ce6"
|
||||
thumbTintColor="#007ee5"
|
||||
maximumValue={availableVestingShares}
|
||||
value={amount}
|
||||
onValueChange={(value) => {
|
||||
this.setState({ amount: value });
|
||||
}}
|
||||
/>
|
||||
<Text style={styles.informationText}>
|
||||
{intl.formatMessage({ id: 'transfer.amount_information' })}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.bottomContent}>
|
||||
<MainButton
|
||||
style={styles.button}
|
||||
onPress={() => this.startActionSheet.current.show()}
|
||||
isLoading={isTransfering}
|
||||
>
|
||||
<Text style={styles.buttonText}>{intl.formatMessage({ id: 'transfer.next' })}</Text>
|
||||
</MainButton>
|
||||
</View>
|
||||
</View>
|
||||
{_renderMainBtn()}
|
||||
</View>
|
||||
</ScrollView>
|
||||
</KeyboardAvoidingView>
|
||||
<OptionsModal
|
||||
ref={this.startActionSheet}
|
||||
options={[
|
||||
|
@ -194,7 +194,7 @@ const TransferView = ({
|
||||
<BasicHeader title={intl.formatMessage({ id: `transfer.${transferType}` })} />
|
||||
<View style={styles.container}>
|
||||
<ScrollView>
|
||||
<View style={styles.topContent}>
|
||||
<View style={[styles.toFromAvatarsContainer, { marginBottom: 16 }]}>
|
||||
<UserAvatar username={from} size="xl" style={styles.userAvatar} noAction />
|
||||
<Icon style={styles.icon} name="arrow-forward" iconType="MaterialIcons" />
|
||||
<UserAvatar username={destination} size="xl" style={styles.userAvatar} noAction />
|
||||
|
@ -4,13 +4,31 @@ export default EStyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: '$primaryBackgroundColor',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
topContent: {
|
||||
stepOneContainer: {
|
||||
zIndex: 2,
|
||||
paddingVertical: 16,
|
||||
borderRadius: 12,
|
||||
backgroundColor: '$primaryLightBackground',
|
||||
},
|
||||
stepTwoContainer: {
|
||||
paddingVertical: 16,
|
||||
marginTop: 16,
|
||||
|
||||
borderRadius: 12,
|
||||
backgroundColor: '$primaryLightBackground',
|
||||
},
|
||||
stepThreeContainer: {
|
||||
alignItems: 'center',
|
||||
marginTop: 16,
|
||||
marginVertical: 16,
|
||||
},
|
||||
toFromAvatarsContainer: {
|
||||
flexDirection: 'row',
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
paddingVertical: 16,
|
||||
marginTop: 16,
|
||||
},
|
||||
middleContent: {
|
||||
flex: 3,
|
||||
@ -25,11 +43,24 @@ export default EStyleSheet.create({
|
||||
borderWidth: 1,
|
||||
borderColor: '$borderColor',
|
||||
borderRadius: 8,
|
||||
padding: 10,
|
||||
paddingHorizontal: 10,
|
||||
color: '$primaryBlack',
|
||||
width: 172,
|
||||
minHeight: 35,
|
||||
},
|
||||
amountInput: {
|
||||
borderWidth: 1,
|
||||
borderColor: '$borderColor',
|
||||
borderRadius: 8,
|
||||
paddingLeft: 10,
|
||||
color: '$primaryBlack',
|
||||
flex: 2,
|
||||
minHeight: 35,
|
||||
},
|
||||
error: {
|
||||
borderWidth: 1,
|
||||
borderColor: 'red',
|
||||
},
|
||||
textarea: {
|
||||
borderWidth: 1,
|
||||
borderColor: '$borderColor',
|
||||
@ -44,7 +75,6 @@ export default EStyleSheet.create({
|
||||
},
|
||||
button: {
|
||||
width: '$deviceWidth / 3',
|
||||
marginTop: 30,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
fontWeight: 'bold',
|
||||
@ -94,26 +124,9 @@ export default EStyleSheet.create({
|
||||
flexGrow: 1,
|
||||
width: 150,
|
||||
},
|
||||
track: {
|
||||
height: 2,
|
||||
borderRadius: 1,
|
||||
},
|
||||
thumb: {
|
||||
width: 16,
|
||||
height: 16,
|
||||
borderRadius: 16 / 2,
|
||||
backgroundColor: '$primaryLightBackground',
|
||||
shadowColor: 'black',
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowRadius: 2,
|
||||
shadowOpacity: 0.35,
|
||||
elevation: 3,
|
||||
},
|
||||
slider: {
|
||||
marginHorizontal: 30,
|
||||
},
|
||||
|
||||
formButton: {
|
||||
padding: 12,
|
||||
height: 44,
|
||||
borderRadius: 5,
|
||||
backgroundColor: '$primaryBlue',
|
||||
marginTop: 5,
|
||||
@ -122,9 +135,6 @@ export default EStyleSheet.create({
|
||||
color: '$white',
|
||||
fontSize: 14,
|
||||
},
|
||||
amountText: {
|
||||
color: '$primaryBlue',
|
||||
},
|
||||
informationText: {
|
||||
alignSelf: 'center',
|
||||
color: '$iconColor',
|
||||
@ -195,4 +205,115 @@ export default EStyleSheet.create({
|
||||
nextPowerDown: {
|
||||
marginVertical: 5,
|
||||
},
|
||||
transferToContainer: {
|
||||
flex: 1,
|
||||
width: 172,
|
||||
position: 'relative',
|
||||
},
|
||||
usersDropdownContainer: {
|
||||
position: 'absolute',
|
||||
top: 40,
|
||||
width: 172,
|
||||
maxHeight: 250,
|
||||
zIndex: 999999,
|
||||
elevation: 3,
|
||||
},
|
||||
usersDropdown: {
|
||||
borderColor: '$primaryWhiteLightBackground',
|
||||
borderRadius: 5,
|
||||
shadowOpacity: 0.3,
|
||||
shadowColor: '$shadowColor',
|
||||
backgroundColor: '$primaryLightBackground',
|
||||
elevation: 3,
|
||||
},
|
||||
usersDropItemRow: {
|
||||
height: 50,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: 10,
|
||||
paddingVertical: 10,
|
||||
elevation: 3,
|
||||
},
|
||||
usersDropItemRowText: {
|
||||
color: '$primaryDarkGray',
|
||||
textAlign: 'left',
|
||||
marginLeft: 5,
|
||||
},
|
||||
paddBottom: {
|
||||
paddingBottom: 12,
|
||||
},
|
||||
fillSpace: {
|
||||
flex: 1,
|
||||
padding: 16,
|
||||
backgroundColor: '$primaryBackgroundColor',
|
||||
},
|
||||
elevate: {
|
||||
zIndex: 1,
|
||||
},
|
||||
sectionHeading: {
|
||||
paddingHorizontal: 16,
|
||||
marginBottom: 0,
|
||||
fontSize: 18,
|
||||
fontWeight: '700',
|
||||
color: '$primaryBlack',
|
||||
textAlign: 'left',
|
||||
},
|
||||
sectionSubheading: {
|
||||
paddingHorizontal: 16,
|
||||
fontSize: 14,
|
||||
color: '$primaryBlack',
|
||||
fontWeight: '600',
|
||||
textAlign: 'left',
|
||||
},
|
||||
alreadyDelegateRow: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
marginBottom: 32,
|
||||
},
|
||||
sliderBox: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
marginHorizontal: 12,
|
||||
},
|
||||
emptyBox: {
|
||||
flex: 1,
|
||||
},
|
||||
sliderContainer: {
|
||||
flex: 2,
|
||||
},
|
||||
track: {
|
||||
height: 2,
|
||||
borderRadius: 1,
|
||||
},
|
||||
thumb: {
|
||||
width: 20,
|
||||
height: 20,
|
||||
borderRadius: 20 / 2,
|
||||
backgroundColor: '$primaryLightBackground',
|
||||
shadowColor: 'black',
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowRadius: 2,
|
||||
shadowOpacity: 0.35,
|
||||
elevation: 3,
|
||||
},
|
||||
slider: {
|
||||
marginRight: 12,
|
||||
marginLeft: 8,
|
||||
},
|
||||
sliderAmountContainer: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-end',
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: 8,
|
||||
},
|
||||
amountText: {
|
||||
fontSize: 12,
|
||||
color: '$primaryBlack',
|
||||
fontWeight: '600',
|
||||
textAlign: 'left',
|
||||
},
|
||||
grow: {
|
||||
flexGrow: 1,
|
||||
},
|
||||
});
|
||||
|
@ -177,7 +177,7 @@ class TransferTokenView extends Component {
|
||||
<BasicHeader title={intl.formatMessage({ id: `transfer.${transferType}` })} />
|
||||
<View style={styles.container}>
|
||||
<ScrollView>
|
||||
<View style={styles.topContent}>
|
||||
<View style={[styles.toFromAvatarsContainer, { marginBottom: 16 }]}>
|
||||
<UserAvatar username={from} size="xl" style={styles.userAvatar} noAction />
|
||||
<Icon style={styles.icon} name="arrow-forward" iconType="MaterialIcons" />
|
||||
<UserAvatar username={destination} size="xl" style={styles.userAvatar} noAction />
|
||||
|
@ -5,13 +5,18 @@ export const vestsToHp = (vests, hivePerMVests) => {
|
||||
|
||||
return (vests / 1e6) * hivePerMVests;
|
||||
};
|
||||
|
||||
export const vestsToRshares = (vests:number, votingPower:number, weight:number) => {
|
||||
export const hpToVests = (hp, hivePerMVests) => {
|
||||
if (!hp || !hivePerMVests) {
|
||||
return 0;
|
||||
}
|
||||
return (hp * 1e6) / hivePerMVests;
|
||||
};
|
||||
export const vestsToRshares = (vests: number, votingPower: number, weight: number) => {
|
||||
if (!vests || !votingPower || !weight) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const finalVest = vests * 1e6;
|
||||
const power = (votingPower * weight / 10000) / 50
|
||||
return (power * finalVest / 1000)
|
||||
const power = (votingPower * weight) / 10000 / 50;
|
||||
return (power * finalVest) / 1000;
|
||||
};
|
||||
|
38
yarn.lock
38
yarn.lock
@ -3927,6 +3927,11 @@ elliptic@^6.5.2, elliptic@^6.5.3:
|
||||
minimalistic-assert "^1.0.1"
|
||||
minimalistic-crypto-utils "^1.0.1"
|
||||
|
||||
eme-encryption-scheme-polyfill@^2.0.1:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/eme-encryption-scheme-polyfill/-/eme-encryption-scheme-polyfill-2.0.3.tgz#2ca6e06480e06cceb5e50efd27943ac46c959878"
|
||||
integrity sha512-44CNFMsqzHdKHrzWxlS7xZ8KUHn5XutBqpmCuWzNIynmAyFInHrrD3ozv/RvK9ZhgV6QY6Easx8EWAmxteNodg==
|
||||
|
||||
emoji-regex@^7.0.1:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
|
||||
@ -6543,6 +6548,11 @@ jsx-ast-utils@^2.2.3:
|
||||
array-includes "^3.1.2"
|
||||
object.assign "^4.1.2"
|
||||
|
||||
keymirror@^0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/keymirror/-/keymirror-0.1.1.tgz#918889ea13f8d0a42e7c557250eee713adc95c35"
|
||||
integrity sha1-kYiJ6hP40KQufFVyUO7nE63JXDU=
|
||||
|
||||
kind-of@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-1.1.0.tgz#140a3d2d41a36d2efcfa9377b62c24f8495a5c44"
|
||||
@ -8643,6 +8653,11 @@ react-native-matomo-sdk@feruzm/react-native-matomo-sdk:
|
||||
version "0.4.1"
|
||||
resolved "https://codeload.github.com/feruzm/react-native-matomo-sdk/tar.gz/392b1cfca771b28005821ef909ffb9a2082156d9"
|
||||
|
||||
react-native-media-controls@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/react-native-media-controls/-/react-native-media-controls-2.3.0.tgz#c36e876a14d12982b7c6fb759201ff439117cbd0"
|
||||
integrity sha512-N10i12ZO+GIXjmdA9hQk/HuBm5u9xWPIOydG/2SvLEZcnY3xg3D8d2IJXRolzSm2jBRgEGSO4rMgRBU6MpdNMg==
|
||||
|
||||
react-native-modal-dropdown@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react-native-modal-dropdown/-/react-native-modal-dropdown-1.0.2.tgz#3e1efae5a5eacc42f44ac96468ea2f1b5bb0d759"
|
||||
@ -8778,6 +8793,13 @@ react-native-scrollable-tab-view@ecency/react-native-scrollable-tab-view:
|
||||
prop-types "^15.6.0"
|
||||
react-timer-mixin "^0.13.3"
|
||||
|
||||
react-native-slider@^0.11.0:
|
||||
version "0.11.0"
|
||||
resolved "https://registry.yarnpkg.com/react-native-slider/-/react-native-slider-0.11.0.tgz#b68a0bc43c8422b24cd57947cc5ac2bcdb58fadc"
|
||||
integrity sha512-jV9K87eu9uWr0uJIyrSpBLnCKvVlOySC2wynq9TFCdV9oGgjt7Niq8Q1A8R8v+5GHsuBw/s8vEj1AAkkUi+u+w==
|
||||
dependencies:
|
||||
prop-types "^15.5.6"
|
||||
|
||||
react-native-snap-carousel@^3.8.0:
|
||||
version "3.9.1"
|
||||
resolved "https://registry.yarnpkg.com/react-native-snap-carousel/-/react-native-snap-carousel-3.9.1.tgz#6fd9bd8839546c2c6043a41d2035afbc6fe0443e"
|
||||
@ -8875,6 +8897,15 @@ react-native-version@^4.0.0:
|
||||
resolve-from "^5.0.0"
|
||||
semver "^7.0.0"
|
||||
|
||||
react-native-video@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-native-video/-/react-native-video-5.2.0.tgz#c3f2c541775f4fda7e0d26d75a6fb59819b13d5e"
|
||||
integrity sha512-5SK1lxyzrCkZF+WuxUxLR1Pt65E0rsWB1w1GrGxSLdC9zWYBumcmuHl+wPJ7UQvznjaH2Ze7uU1R3arejI7+WQ==
|
||||
dependencies:
|
||||
keymirror "^0.1.1"
|
||||
prop-types "^15.7.2"
|
||||
shaka-player "^2.5.9"
|
||||
|
||||
react-native-webview@^11.17.1:
|
||||
version "11.17.1"
|
||||
resolved "https://registry.yarnpkg.com/react-native-webview/-/react-native-webview-11.17.1.tgz#a7c9d995d749539995a4fdad8aa6456bac77fc8f"
|
||||
@ -9662,6 +9693,13 @@ sha.js@^2.4.0, sha.js@^2.4.8:
|
||||
inherits "^2.0.1"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
shaka-player@^2.5.9:
|
||||
version "2.5.23"
|
||||
resolved "https://registry.yarnpkg.com/shaka-player/-/shaka-player-2.5.23.tgz#db92d1c6cf2314f0180a2cec11b0e2f2560336f5"
|
||||
integrity sha512-3MC9k0OXJGw8AZ4n/ZNCZS2yDxx+3as5KgH6Tx4Q5TRboTBBCu6dYPI5vp1DxKeyU12MBN1Zcbs7AKzXv2EnCg==
|
||||
dependencies:
|
||||
eme-encryption-scheme-polyfill "^2.0.1"
|
||||
|
||||
shallow-clone@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-1.0.0.tgz#4480cd06e882ef68b2ad88a3ea54832e2c48b571"
|
||||
|
Loading…
Reference in New Issue
Block a user