react-native-gesture-handler#ScrollView TypeScript Examples
The following examples show how to use
react-native-gesture-handler#ScrollView.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.tsx From react-native-meetio with MIT License | 6 votes |
Components = () => {
const insets = useSafeAreaInsets();
return (
<Box
flex={1}
style={{ paddingTop: insets.top }}
backgroundColor="lightBlueMagenta100"
>
<ScrollView
horizontal
snapToInterval={width}
decelerationRate="fast"
showsHorizontalScrollIndicator={false}
bounces={false}
>
<HeadersInputs />
<Cards1 />
<Cards2 />
</ScrollView>
</Box>
);
}
Example #2
Source File: EmptyStatesExample.tsx From frontatish with MIT License | 6 votes |
EmptyStates = ({ navigation }: any) => {
const emptyScreenComponentScreens = [
'EmptyStateGeneric',
'EmptyStateMFWatchlist',
'EmptyStateStocksWatchlist',
'EmptyStateOrders',
'EmptyStateMFDashboard',
'EmptyStateStocksDashboard',
'EmptyStateCart',
];
const Colors = useColors();
return (
<SafeAreaView style={{ flex: 1, backgroundColor: Colors.white }}>
<ScrollView
showsVerticalScrollIndicator={false}
contentContainerStyle={{ flexGrow: 1 }}
>
{emptyScreenComponentScreens.map((item) => (
<TouchableOpacity
onPress={() => navigation.navigate(item)}
style={[
styles.navButtonContainer,
{ borderBottomColor: Colors.font_3 },
]}
key={item}
>
<Text style={{ color: Colors.font_1 }}>{item}</Text>
</TouchableOpacity>
))}
</ScrollView>
</SafeAreaView>
);
}
Example #3
Source File: OverlayView.tsx From mobile with Apache License 2.0 | 6 votes |
AccessibleView = ({children}: {children: React.ReactNode}) => {
const accessibilityService = useAccessibilityService();
return accessibilityService.isScreenReaderEnabled ? (
<ScrollView style={styles.content} showsVerticalScrollIndicator={false}>
{children}
</ScrollView>
) : (
<View style={styles.content}>{children}</View>
);
}
Example #4
Source File: ChipRow.tsx From lexicon with MIT License | 6 votes |
/**
*
* `ChipRow` accepts an array of `Chip`s and renders them in a horizontal
* `ScrollView` with the proper layout (spacing).
*/
export function ChipRow(props: Props) {
const { spacing } = useTheme();
const { items, scrollViewProps = {} } = props;
const chipSpacingStyle = { marginEnd: spacing.m };
return (
<ScrollView
horizontal={true}
showsHorizontalScrollIndicator={false}
{...scrollViewProps}
>
{items.map((chipProps, index) => {
const isLastItem = index === items.length - 1;
const style = isLastItem ? undefined : chipSpacingStyle;
return <Chip key={index} style={style} {...chipProps} />;
})}
</ScrollView>
);
}
Example #5
Source File: HospitalDetail.tsx From wuhan2020-frontend-react-native-app with MIT License | 5 votes |
function HospitalDetail({ item }: PropTypes) {
const { supplies, contacts } = item;
return (
<ScrollView>
<H1 title={item.name} />
<View style={styles.horizontalContainer}>
<Text style={[styles.subtitle, { fontSize: 14 }]}>
{`${item.city} - ${item.province || ''}`}
</Text>
<Text style={styles.subtitle}>{item.district}</Text>
</View>
<View style={styles.container}>
<View style={{ paddingVertical: 4 }}>
<H2 title="联系人" />
</View>
<View>
{contacts.map(contact => (
<View style={styles.horizontalContainer}>
<Text>{contact.name || '无'}</Text>
<Text>电话:{contact.tel}</Text>
</View>
))}
</View>
</View>
<View style={styles.container}>
<View style={{ paddingVertical: 4 }}>
<H2 title="地址" />
</View>
<Text>{item.address}</Text>
</View>
<View style={styles.container}>
<View style={{ paddingVertical: 4 }}>
<H2 title="物资清单" />
</View>
<View>
{supplies.map(supply => (
<Supply item={supply} />
))}
</View>
</View>
<View style={styles.container}>
<View style={{ paddingVertical: 4 }}>
<H2 title="其他信息" />
</View>
<Text>{item.remark ? item.remark : '没有其他信息'}</Text>
</View>
</ScrollView>
);
}
Example #6
Source File: SleepChart.tsx From nyxo-app with GNU General Public License v3.0 | 5 votes |
SleepTimeChart: FC = () => {
const data = useAppSelector(getNightsAsDays)
const daysToShow = data.length
const chartWidth = (barWidth + 10) * daysToShow + paddingLeft + paddingRight
const xDomain: Date[] = extent(data, (date) => new Date(date.date)) as Date[]
const yDomain: number[] = [
min(data, (date) =>
min(date.night, (night) =>
subHours(new Date(night.startDate), 1).valueOf()
)
) as number,
max(data, (date) =>
max(date.night, (night) => addHours(new Date(night.endDate), 1).valueOf())
) as number
]
const scaleX = scaleTime()
.domain(xDomain)
.range([paddingLeft, chartWidth - paddingRight])
const scaleY = scaleTime()
.domain(yDomain)
.nice()
.range([10, chartHeight - 80])
const yTicks = scaleY.ticks(5)
const xTicks = scaleX.ticks(daysToShow)
return (
<Card>
<Title>STAT.TREND</Title>
<ScrollContainer>
<YTicksContainer
pointerEvents="auto"
width={chartWidth}
height={chartHeight}>
<YTicks scaleY={scaleY} chartWidth={chartWidth} ticks={yTicks} />
</YTicksContainer>
<ScrollView
style={{ transform: [{ scaleX: -1 }] }}
horizontal
showsHorizontalScrollIndicator={false}>
<View style={{ transform: [{ scaleX: -1 }] }}>
<Svg width={chartWidth} height={chartHeight}>
{/* <TargetBars
start={bedtimeWindow}
onPress={select}
barWidth={barWidth}
scaleX={scaleX}
scaleY={scaleY}
data={normalizedSleepData}
/> */}
<SleepBars
onPress={() => undefined}
barWidth={barWidth}
scaleX={scaleX}
scaleY={scaleY}
data={data}
/>
<XTicks
chartHeight={chartHeight}
scaleX={scaleX}
barWidth={barWidth}
ticks={xTicks}
/>
</Svg>
</View>
</ScrollView>
</ScrollContainer>
</Card>
)
}
Example #7
Source File: Explore.tsx From online-groceries-app with MIT License | 5 votes |
ExploreTab = ({navigation}: ExploreTabProps) => {
const ui_array = [
{id: 0},
{id: 1},
{id: 2},
{id: 3},
{id: 4},
{id: 5},
{id: 6},
{id: 7},
];
return (
<ScrollView style={styles.container}>
<Header title="Find Products" />
<View style={styles.searchBarBox}>
<SearchBar navigation={navigation} navigateTo="" />
</View>
<View style={styles.body}>
<FlatList
data={ui_array}
keyExtractor={(item) => item.id}
scrollEnabled={true}
numColumns={2}
renderItem={({item}) => {
return (
<CategoryCard
key={item.id}
bgColour="#F00"
borderColour="#0F0"
title="Teste"
image={ImageTest}
onPress={() => null}
/>
);
}}
/>
</View>
<View style={styles.scrollFooter} />
</ScrollView>
);
}
Example #8
Source File: styles.ts From safetraceapi with GNU General Public License v3.0 | 5 votes |
BaseLayout = styled(ScrollView)`
flex: 1;
background-color: #fafafa;
`
Example #9
Source File: AvatarRow.tsx From lexicon with MIT License | 5 votes |
export function AvatarRow(props: Props) {
const { navigate } = useNavigation<StackNavProp<'TabNav'>>();
const styles = useStyles();
const {
posters,
title,
size = 'xs',
titleStyle,
imageStyle,
style,
extended,
...otherProps
} = props;
const onPressAvatar = (username: string) => {
navigate('UserInformation', { username });
};
return (
<View style={[styles.container, style]} {...otherProps}>
<Text
color="textLight"
numberOfLines={1}
style={[styles.title, titleStyle]}
>
{title}
</Text>
{extended ? (
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
style={styles.avatarContainerScroll}
contentContainerStyle={styles.scrollViewContentContainer}
bounces={false}
>
{posters.map((item, index) => (
<Avatar
key={item.id}
src={item.avatar}
size={size}
label={item.username[0]}
style={[
imageStyle,
index !== posters.length - 1 ? styles.spacing : null,
]}
onPress={() => onPressAvatar(item.username)}
/>
))}
</ScrollView>
) : (
<View style={styles.avatarContainer}>
{posters.slice(0, 5).map((item, index) => (
<Avatar
key={item.id}
src={item.avatar}
size={size}
label={item.username[0]}
style={[
imageStyle,
index !== posters.length - 1 ? styles.spacing : null,
]}
onPress={() => onPressAvatar(item.username)}
/>
))}
</View>
)}
</View>
);
}
Example #10
Source File: CreateStepTwo.tsx From BitcoinWalletMobile with MIT License | 5 votes |
CreateStepTwo: React.FC<Props> = (props) => {
const dispatch = useDispatch()
const languageSelector = (state: WalletState) => state.language
const language = useSelector(languageSelector)
const [words, setWords] = useState([""])
const [didWriteDown, setDidWriteDown] = useState(false)
const insets = useSafeAreaInsets()
const setUpWallet = async () => {
dispatch(setNewlyCreated(true))
props.navigation.popToTop()
}
const getSeed = async () => {
let s = await RNSecureKeyStore.get('WALLET_SEED')
setWords(s.split(' '))
}
useEffect(() => {
getSeed()
}, [])
return (
<View style={styles.rootContainer}>
<View style={{ flex: 1 }}>
<Header screen={getTranslated(language).seed_phrase} action={() => { props.navigation.goBack() }} />
<Screen>
<View style={styles.viewContainer}>
<ScrollView contentContainerStyle={{ flexGrow: 1, justifyContent: 'space-between' }} style={{ height: '100%' }}>
<View style={{ flex: 1 }}>
<View style={styles.warningContainer}>
<View style={styles.iconWithText}>
<Image style={styles.icon} source={require('../../assets/images/warning.png')} />
<Text style={styles.warningTextInner}><Text style={styles.warningText}>{getTranslated(language).warning}! </Text>{getTranslated(language).warning_text_2}</Text>
</View>
</View>
<RecoveryWords screen="CreateStepTwo" words={words} />
<View style={{ marginBottom: insets.bottom + 30 }}>
<View style={styles.hasSavedContainer}>
<TouchableWithoutFeedback style={{ width: 32, height: 32 }} onPress={() => { setDidWriteDown(!didWriteDown) }}>
<CheckBox tintColors={{ true: '#F7931A', false: '#F7931A' }} style={styles.checkBox} tintColor="#F7931A" animationDuration={0} onFillColor="#F7931A" onTintColor="#F7931A" onCheckColor="#fff" boxType="square" disabled={false} value={didWriteDown} onValueChange={(newVal) => setDidWriteDown(newVal)} />
</TouchableWithoutFeedback>
<Text style={styles.hasSavedText}>{getTranslated(language).have_saved}</Text>
</View>
<View style={styles.buttonContainer}>
{didWriteDown &&
<ButtonPrimary text={getTranslated(language).wrote_it_down} action={setUpWallet} />
}
{!didWriteDown &&
<ButtonPrimaryDisabled text={getTranslated(language).wrote_it_down} action={() => { }} />
}
</View>
</View>
</View>
</ScrollView>
</View>
</Screen>
</View>
</View>
);
}
Example #11
Source File: CreateStepOne.tsx From BitcoinWalletMobile with MIT License | 5 votes |
CreateStepOne: React.FC<Props> = (props) => {
const languageSelector = (state: WalletState) => state.language
const language = useSelector(languageSelector)
const insets = useSafeAreaInsets()
const [isGenerating, setIsGenerating] = useState(false)
async function generateSeed() {
setIsGenerating(true)
setTimeout(() => {
setIsGenerating(false)
props.navigation.navigate('CreateStepTwo')
}, 2000)
}
useEffect(() => {
let listener = props.navigation.addListener('beforeRemove', (e) => {
if (!isGenerating) {
props.navigation.dispatch(e.data.action)
}
else {
e.preventDefault()
}
})
return listener
}, [props.navigation, isGenerating])
return (
<View style={{ flex: 1 }}>
{ isGenerating &&
<Loader title="Hold on..." subTitle="This might take a second" />
}
{ !isGenerating &&
<View style={{ flex: 1 }}>
<Header screen={getTranslated(language).create_new} action={() => { props.navigation.goBack() }} />
<Screen>
<View style={styles.rootContainer}>
<View style={styles.viewContainer}>
<ScrollView contentContainerStyle={{ flexGrow: 1, justifyContent: 'space-between' }}>
<View style={styles.warningContainer}>
<View style={styles.iconWithText}>
<Image style={styles.warningIcon} source={require('../../assets/images/warning.png')} />
<Text style={styles.warningText} >{getTranslated(language).warning}!</Text>
</View>
<Text style={styles.warningTextInner}>{getTranslated(language).we_will_generate}</Text>
<Text style={styles.warningTextInner} >{getTranslated(language).warning_text_1}</Text>
<View style={styles.iconWithText}>
<Image style={styles.icon} source={require('../../assets/images/write_it_down.png')} />
<Text style={styles.innerText} >{getTranslated(language).write_it_down}</Text>
</View>
<View style={styles.iconWithText}>
<Image style={styles.icon} source={require('../../assets/images/keep_it_safe.png')} />
<Text style={styles.innerText} >{getTranslated(language).keep_it_safe}</Text>
</View>
<View style={styles.iconWithText}>
<Image style={styles.icon} source={require('../../assets/images/do_not_loose_it.png')} />
<Text style={styles.innerText} >{getTranslated(language).do_not_lose_it}</Text>
</View>
</View>
<View style={[styles.buttonContainer, { marginBottom: insets.bottom + 30 }]}>
{!isGenerating && <ButtonPrimary text={getTranslated(language).next_button} action={() => generateSeed()} />}
</View>
</ScrollView>
</View>
</View>
</Screen>
</View>
}
</View>
);
}
Example #12
Source File: Receive.tsx From BitcoinWalletMobile with MIT License | 5 votes |
Receive: React.FC = () => {
const externalIndexSelector = (state: WalletState) => state.externalIndex
const externalIndex = useSelector(externalIndexSelector)
const externalAddressesSelector = (state: WalletState) => state.externalAddresses
const externalAddresses = useSelector(externalAddressesSelector)
const languageSelector = (state: WalletState) => state.language
const language = useSelector(languageSelector)
const [address, setAddress] = useState("Address")
const getExternalAddress = () => {
for (var i = 0; i < externalAddresses.length; i++) {
if (externalAddresses[i].index == externalIndex) {
setAddress(externalAddresses[i].address)
}
}
}
const copyAddressToClipboard = () => {
Clipboard.setString(address);
}
useEffect(() => {
getExternalAddress()
}, [externalIndex])
return (
<View style={styles.container}>
<Header screen={getTranslated(language).receive} />
<Screen>
<ScrollView>
<View>
<Text style={styles.subHeadingText}>{getTranslated(language).receive_only + " " + getTranslated(language).address_below}</Text>
<View style={styles.textArea}>
<Text style={styles.textAreaText}>{address}</Text>
</View>
<TouchableOpacity onPress={copyAddressToClipboard}>
<View style={{ flexDirection: 'row', justifyContent: 'center', alignItems: 'center', marginVertical: 20, }}>
<Image source={require('../assets/images/copy.png')} style={styles.icon} />
<Text style={styles.copyText}>{getTranslated(language).copy_button}</Text>
</View>
</TouchableOpacity>
</View>
<View style={styles.qrContainer}>
<LinearGradient style={{ flex: 1 }} useAngle={true} angle={180} angleCenter={{ x: 0.5, y: 0.5 }} colors={['#1f232e', '#13161f']}>
<View style={{ justifyContent: 'center', alignItems: 'center', marginTop: 15 }}>
<QRCode backgroundColor="#1F232E" color="#fff" size={160} value={address} />
</View>
</LinearGradient>
</View>
</ScrollView>
</Screen>
</View>
);
}
Example #13
Source File: FullscreenImageZoom.tsx From vsinder with Apache License 2.0 | 5 votes |
FullscreenImageZoomProvider: React.FC = ({ children }) => {
const srcRef = useRef("");
const [src, setSrc] = useState("");
srcRef.current = src;
useEffect(() => {
const onBackPress = () => {
if (srcRef.current) {
setSrc("");
return true;
} else {
return false;
}
};
BackHandler.addEventListener("hardwareBackPress", onBackPress);
return () =>
BackHandler.removeEventListener("hardwareBackPress", onBackPress);
}, []);
return (
<FullscreenImageZoomContext.Provider value={setSrc}>
{src ? (
<SafeAreaView
style={{
position: "absolute",
top: 0,
zIndex: 50,
flex: 1,
backgroundColor: "#000",
}}
>
<MyButton onPress={() => setSrc("")}>close</MyButton>
<ImageZoom
cropWidth={width}
cropHeight={height}
imageWidth={codeImageWidth}
imageHeight={codeImageHeight}
>
<ScrollView>
<Image
style={{
borderRadius: 9,
height: codeImageHeight,
width: codeImageWidth,
}}
resizeMode="contain"
source={{ uri: src }}
/>
</ScrollView>
</ImageZoom>
</SafeAreaView>
) : null}
{children}
</FullscreenImageZoomContext.Provider>
);
}
Example #14
Source File: exposure-notifications.tsx From protect-scotland with Apache License 2.0 | 5 votes |
ExposureNotificationsModal: FC<ModalProps> = (props) => {
const {t} = useTranslation();
const {status, askPermissions, isAuthorised} = useExposure();
const ensUnknown = status.state === StatusState.unknown;
const ensDisabled = status.state === StatusState.disabled;
const notAuthorised = isAuthorised === AuthorisedStatus.unknown;
return (
<Modal
{...props}
type="dark"
title={t('modals:exposureNotifications:title')}
buttons={
ensUnknown || notAuthorised
? [
{
variant: 'inverted',
action: async () => await askPermissions(),
hint: t('common:turnOnBtnHint'),
label: t('common:turnOnBtnLabel')
}
]
: [
{
variant: 'inverted',
action: () => goToSettingsAction(false, askPermissions),
hint: ensDisabled
? t('common:turnOnBtnHint')
: Platform.OS === 'android'
? t('common:turnOnBtnHint')
: t('common:goToSettingsHint'),
label: ensDisabled
? t('common:turnOnBtnLabel')
: Platform.OS === 'android'
? t('common:turnOnBtnLabel')
: t('common:goToSettings')
}
]
}>
<ScrollView>
<Markdown markdownStyles={modalMarkdownStyles}>
{ensUnknown || notAuthorised
? t('modals:exposureNotifications:turnOn')
: t(`modals:exposureNotifications:instructions${Platform.OS}`)}
</Markdown>
</ScrollView>
</Modal>
);
}
Example #15
Source File: CodeOfConductPage.tsx From GiveNGo with MIT License | 5 votes |
CodeOfConductPage = ({ navigation }: any) => {
return (
<ScrollView>
<Layout
style={{
flex: 1,
justifyContent: 'center',
backgroundColor: 'white-ish',
}}
>
<Card style={styles.card}>
<Text category="h5" style={styles.text}>
Give 'n' Go{'\n'} Code of Conduct
</Text>
<Text category="p1" style={styles.text}>
{'\n'}In the interest of fostering an open and welcoming environment
where people feel comfortable requesting help, we ask all
participants to make our community a harassment-free experience for
everyone, regardless of age, ethnicity, gender identity and
expression, body, size, disability, level of education,
socio-economic status, nationality, personal appearance, religion,
or sexual identity and orientation. {'\n'}
</Text>
<Text category="h5" style={styles.text}>
Our Standards
</Text>
<Text category="p1" style={styles.text}>
{'\n'}Above all, be a good neighbor. {'\n'}
</Text>
<Text category="p1" style={styles.text}>
On interactions with other members: {'\n'}
</Text>
<Text category="p1" style={styles.text}>
In using Give 'n' Go to request and donate items, you, and not Give
'n' Go, are responsible for your own decisions and actions. You
agree to share your location once you make a request so that a donor
can deliver any items that you have requested. You may choose on
your profile to remain anonymous if you would like your profile,
name, etc. to remain private. {'\n'}
{'\n'}
In addition, you alone are responsible for any documentation or of
tax-deductible donations. Give 'n' Go is not a party to transactions
or disputes between members.
</Text>
</Card>
<Button
onPress={() => navigation.navigate("Give'N'Go", { screen: 'Home' })}
>
Accept
</Button>
<Button
appearance="ghost"
onPress={() => navigation.navigate('Sign Up')}
>{`< Back`}</Button>
</Layout>
</ScrollView>
);
}
Example #16
Source File: InstagramFeed.tsx From react-native-gallery-toolkit with MIT License | 5 votes |
export default function InstagramFeed() {
const scrollViewRef = useRef<ScrollView | null>(null);
const activeItemIndex = useSharedValue(-1);
const { controlsStyles, setControlsHidden } = useControls();
const CellRendererComponent = useMemo<
FlatListProps<ListItemT>['CellRendererComponent']
>(
() => ({ children, index, style, ...props }) => {
const animatedStyles = useAnimatedStyle(() => {
const isActive =
activeItemIndex.value !== -1 &&
activeItemIndex.value === index;
return {
zIndex: isActive ? 1 : 0,
};
});
return (
<Animated.View {...props} style={animatedStyles}>
{children}
</Animated.View>
);
},
[],
);
return (
<>
<FlatList
contentContainerStyle={{
// paddingTop: APPBAR_HEIGHT + STATUSBAR_HEIGHT,
}}
initialNumToRender={2}
maxToRenderPerBatch={2}
data={data}
keyExtractor={({ id }) => id}
renderItem={(item) => (
<RenderItem
{...item}
scrollViewRef={scrollViewRef}
activeItemIndex={activeItemIndex}
setControlsHidden={setControlsHidden}
/>
)}
renderScrollComponent={(props) => (
// @ts-ignore
<ScrollView {...props} ref={scrollViewRef} />
)}
CellRendererComponent={CellRendererComponent}
/>
<Animated.View style={controlsStyles}>
<DetachedHeader.Container>
<DetachedHeader />
</DetachedHeader.Container>
</Animated.View>
</>
);
}
Example #17
Source File: NewMessagePreview.tsx From lexicon with MIT License | 4 votes |
export default function NewMessagePreview() {
const { setModal } = useModal();
const styles = useStyles();
const navigation = useNavigation<RootStackNavProp<'NewMessagePreview'>>();
const { navigate, goBack } = navigation;
const {
params: { title, raw, targetRecipients, userList },
} = useRoute<RootStackRouteProp<'NewMessagePreview'>>();
const storage = useStorage();
const username = storage.getItem('user')?.username ?? '';
const [imageUrls, setImageUrls] = useState<Array<string>>();
const shortUrls = getPostShortUrl(raw) ?? [];
const { getImageUrls } = useLookupUrls({
variables: { shortUrls },
onCompleted: ({ lookupUrls }) => {
setImageUrls(sortImageUrl(shortUrls, lookupUrls));
},
});
const { newMessage, loading } = useNewMessage({
onCompleted: () => {
navigate('Main', { screen: 'Messages' });
},
onError: (error) => {
errorHandlerAlert(error);
},
refetchQueries: [
{
query: MESSAGE,
variables: { username },
},
],
});
useEffect(() => {
if (shortUrls.length > 0) {
getImageUrls();
}
}, [getImageUrls, shortUrls.length]);
useEffect(
() =>
navigation.addListener('beforeRemove', (e) => {
if (loading) {
e.preventDefault();
}
}),
[loading, navigation],
);
const sendMessage = () => {
setModal(false);
newMessage({
variables: {
newPrivateMessageInput: {
title,
raw,
targetRecipients,
},
},
});
};
return (
<SafeAreaView style={styles.container}>
<CustomHeader
title={t('Preview')}
rightIcon="Send"
onPressRight={sendMessage}
isLoading={loading}
/>
{ios && (
<ModalHeader
title={t('Preview')}
left={
<HeaderItem
label={t('Cancel')}
onPressItem={goBack}
disabled={loading}
left
/>
}
right={
<HeaderItem
label={t('Send')}
onPressItem={sendMessage}
loading={loading}
/>
}
/>
)}
<AvatarRow
title={title}
posters={userList}
style={styles.participants}
extended
/>
<ScrollView>
<View style={styles.contentContainer}>
<ChatBubble
message={t('{message}', {
message: handleSpecialMarkdown(raw),
})}
imageUrls={imageUrls}
bgColor={'primary'}
nonClickable={true}
/>
</View>
</ScrollView>
</SafeAreaView>
);
}
Example #18
Source File: PostPreview.tsx From lexicon with MIT License | 4 votes |
export default function PostPreview() {
const { setModal } = useModal();
const styles = useStyles();
const { colors, spacing } = useTheme();
const navigation = useNavigation<RootStackNavProp<'PostPreview'>>();
const { navigate, reset, goBack } = navigation;
const {
params: {
reply,
postData,
focusedPostNumber,
editPostId,
editTopicId,
editedUser,
},
} = useRoute<RootStackRouteProp<'PostPreview'>>();
const storage = useStorage();
const channels = storage.getItem('channels');
const [imageUrls, setImageUrls] = useState<Array<string>>();
const { title, content } = postData;
const shortUrls = getPostShortUrl(content) ?? [];
const tags = 'tagIds' in postData ? postData.tagIds : [];
const images = 'images' in postData ? postData.images : undefined;
const navToPostDetail = ({
topicId,
selectedChannelId = ('post' in postData && postData.post?.channel.id) || 0,
focusedPostNumber,
}: StackRouteProp<'PostDetail'>['params']) => {
const prevScreen = 'PostPreview';
navigate('Main', {
screen: 'PostDetail',
params: {
topicId,
selectedChannelId,
focusedPostNumber,
prevScreen,
},
});
};
const { getImageUrls } = useLookupUrls({
variables: { shortUrls },
onCompleted: ({ lookupUrls }) => {
setImageUrls(sortImageUrl(shortUrls, lookupUrls));
},
});
const { newTopic, loading: newTopicLoading } = useNewTopic({
onCompleted: ({ newTopic: result }) => {
reset({
index: 0,
routes: [{ name: 'Main' }],
});
navToPostDetail({
topicId: result.topicId,
selectedChannelId: ('channelId' in postData && postData.channelId) || 0,
focusedPostNumber,
});
},
});
const { reply: replyTopic, loading: replyLoading } = useReplyTopic({
onCompleted: () => {
navToPostDetail({
topicId: ('topicId' in postData && postData.topicId) || 0,
focusedPostNumber,
});
},
onError: (error) => {
errorHandlerAlert(error);
},
});
const { editPost, loading: editPostLoading } = useEditPost({
onCompleted: () => {
!editTopicId && // if there's also editTopicId then don't do anything.
navToPostDetail({
topicId: ('topicId' in postData && postData.topicId) || 0,
focusedPostNumber,
});
},
onError: (error) => {
errorHandlerAlert(error);
},
});
const { editTopic, loading: editTopicLoading } = useEditTopic({
onCompleted: () => {
navToPostDetail({
topicId: editTopicId || 0,
focusedPostNumber,
});
},
onError: (error) => {
errorHandlerAlert(error);
},
});
const loading = reply
? replyLoading || editPostLoading
: newTopicLoading || editTopicLoading;
useEffect(() => {
if (shortUrls.length > 0) {
getImageUrls();
}
}, [getImageUrls, shortUrls.length]);
useEffect(
() =>
navigation.addListener('beforeRemove', (e) => {
if (loading) {
e.preventDefault();
}
}),
[loading, navigation],
);
const postToServer = () => {
setModal(false);
if (editPostId || editTopicId) {
if (editPostId) {
editPost({
variables: {
postId: editPostId,
postInput: {
raw: content,
},
},
});
}
if (editTopicId) {
editTopic({
variables: {
topicId: editTopicId,
topicInput: {
title,
categoryId: ('channelId' in postData && postData.channelId) || 0,
tags,
},
},
});
}
return;
}
if (reply) {
const post = 'post' in postData && postData.post;
replyTopic({
variables: {
raw: content,
topicId: ('topicId' in postData && postData.topicId) || 0,
replyToPostNumber: post ? post.postNumber : null,
},
});
} else {
newTopic({
variables: {
newTopicInput: {
title,
category: ('channelId' in postData && postData.channelId) || 0,
tags,
raw: content,
},
},
});
}
};
return (
<SafeAreaView style={styles.container}>
<CustomHeader
title={t('Preview')}
rightIcon="Send"
onPressRight={postToServer}
isLoading={loading}
/>
{ios && (
<ModalHeader
title={t('Preview')}
left={
<HeaderItem
label={t('Cancel')}
onPressItem={goBack}
disabled={loading}
left
/>
}
right={
<HeaderItem
label={t('Post')}
onPressItem={postToServer}
loading={loading}
/>
}
/>
)}
<ScrollView contentContainerStyle={styles.contentContainer}>
{reply ? (
<>
<IconWithLabel
icon="Replies"
color={colors.textLighter}
label={title}
fontStyle={styles.title}
style={styles.titleContainer}
numberOfLines={1}
/>
<Divider style={styles.spacingBottom} horizontalSpacing="xxl" />
</>
) : (
<Text style={styles.spacingBottom} variant="semiBold" size="l">
{title}
</Text>
)}
<Author
image={
editedUser
? editedUser.avatar
: storage.getItem('user')?.avatar || ''
}
title={
editedUser
? editedUser.username
: storage.getItem('user')?.username || ''
}
size="s"
style={styles.spacingBottom}
/>
{!reply && 'channelId' in postData && (
<PostGroupings
style={styles.spacingBottom}
channel={
channels?.find(({ id }) => id === postData.channelId) ||
mock.channels[0]
}
tags={tags}
/>
)}
{reply && 'post' in postData && postData.post && (
<RepliedPost replyTo={postData.post} />
)}
<Markdown
style={styles.markdown}
imageUrls={imageUrls}
content={content}
nonClickable={true}
/>
{shortUrls.length > 0 &&
!imageUrls &&
shortUrls.map((_url, index) => (
<View
key={index}
style={{
paddingVertical: spacing.l,
marginBottom: spacing.xl,
}}
>
<Image
source={DEFAULT_IMAGE}
style={{
width: '100%',
height: 200,
borderRadius: 4,
}}
/>
</View>
))}
{!reply &&
images?.map((image, index) => (
<CustomImage
src={image}
style={styles.spacingBottom}
key={`images-${index}`}
/>
))}
</ScrollView>
</SafeAreaView>
);
}
Example #19
Source File: index.tsx From krmanga with MIT License | 4 votes |
CategorySetting = ({ navigation, dispatch, categoryList, myCategoryList, isEdit }: IProps) => {
const [myCategories, setMyCategories] = useState<ICategory[]>(myCategoryList);
useEffect(() => {
navigation.setOptions({
headerRight: () => <HeaderRightBtn onSubmit={onSubmit} />
});
}, [myCategories]);
useEffect(() => {
return () => {
dispatch({
type: "categorySetting/setState",
payload: {
isEdit: false
}
});
};
}, []);
const onSubmit = () => {
dispatch({
type: "categorySetting/toggle",
payload: {
myCategoryList: myCategories
}
});
};
const onLongPress = useCallback(() => {
dispatch({
type: "categorySetting/setState",
payload: {
isEdit: true
}
});
}, []);
const onPress = (item: ICategory, index: number, selected: boolean) => {
const disabled = fixedItems.indexOf(index) > -1;
if (selected && disabled) {
return false;
}
if (isEdit) {
if (selected) {
setMyCategories(myCategories.filter(
(selectedItem) => selectedItem.id !== item.id
));
} else {
setMyCategories(myCategories.concat([item]));
}
}
};
const onClickItem = (data: ICategory[], item: ICategory) => {
onPress(item, data.indexOf(item), true);
};
const renderItem = (item: ICategory, index: number) => {
const disabled = fixedItems.indexOf(index) > -1;
return (
<Item
data={item}
isEdit={isEdit}
disabled={disabled}
selected
/>
);
};
const renderUnSelectedItem = (item: ICategory, index: number) => {
return (
<Touchable
key={item.id}
onPress={() => onPress(item, index, false)}
onLongPress={onLongPress}>
<Item
data={item}
isEdit={isEdit}
selected={false}
/>
</Touchable>
);
};
const onDataChange = useCallback((data: ICategory[]) => {
setMyCategories(data);
}, []);
return (
<ScrollView style={styles.container}>
<Text style={styles.classifyName}>我的分类</Text>
<View style={styles.classifyView}>
<DragSortableView
dataSource={myCategories}
fixedItems={fixedItems}
renderItem={renderItem}
sortable={isEdit}
keyExtractor={(item) => item.id}
onDataChange={onDataChange}
parentWidth={parentWidth}
childrenWidth={itemWidth}
childrenHeight={itemHeight}
marginChildrenTop={margin}
onClickItem={onClickItem}
/>
</View>
<View>
{Object.keys(categoryList).map((typeName) => {
return (
<View key={`typeName-${typeName}`}>
<View>
<Text style={styles.classifyName}>{typeName}</Text>
</View>
<View style={styles.classifyView}>
{categoryList[typeName].map(
(item: ICategory, index: number) => {
if (
myCategories.find(
(selectedItem) => selectedItem.id === item.id
)
) {
return null;
}
return renderUnSelectedItem(item, index);
}
)}
</View>
</View>
);
})}
</View>
</ScrollView>
);
}
Example #20
Source File: index.tsx From krmanga with MIT License | 4 votes |
function Download({ dispatch, book_id, chapterList, loading, refreshing }: IProps) {
const [downloadList, setDownloadList] = useState<number[]>([]);
useEffect(() => {
loadData(true);
return () => {
dispatch({
type: "download/setState",
payload: {
...initialState
}
});
};
}, []);
const loadData = (refreshing: boolean, callback?: () => void) => {
dispatch({
type: "download/fetchChapterList",
payload: {
book_id
},
callback
});
};
const renderItem = (item: IChapter, index: number) => {
const selected = downloadList.indexOf(item.chapter_num) > -1;
return (
<Touchable
key={`item-${item.id}key-${index}`}
onPress={() => onPress(item)}
>
<Item
data={item}
downloading={item.downloading}
disabled={item.disabled}
selected={selected}
/>
</Touchable>
);
};
const onPress = useCallback((item: IChapter) => {
if (item.disabled || item.downloading === true) {
return false;
}
const index = downloadList.indexOf(item.chapter_num);
if (index > -1) {
downloadList.splice(index, 1);
setDownloadList([...downloadList]);
} else {
if (downloadList.length == 5) {
Toast.show("最多同时下载五个任务", {
duration: Toast.durations.LONG,
position: Toast.positions.CENTER,
shadow: true,
animation: true
});
return false;
}
setDownloadList([...downloadList, item.chapter_num].sort((a, b) => {
return a - b;
}));
}
}, [downloadList]);
const debounce = (cb: any, wait: number) => {
let timeout = tempTimeout;
if (timeout !== null) {
clearTimeout(timeout);
}
tempTimeout = setTimeout(() => {
tempTimeout = null;
cb && cb();
}, wait);
};
const downTask = () => {
dispatch({
type: "download/downTask",
payload: {
book_id,
downloadList
},
changeDownload: () => {
setDownloadList([]);
},
callBack: (data: IChapter[]) => {
debounce(() => {
dispatch({
type: "download/setState",
payload: {
chapterList: data
}
});
}, 750);
}
});
dispatch({
type: "downloadManage/setScreenReload"
});
};
return (
(loading && refreshing) ? <DownloadPlaceholder /> :
<View style={styles.container}>
<ScrollView>
<View style={styles.main}>
{
chapterList.map((item: IChapter, index: number) => {
return renderItem(item, index);
})
}
</View>
</ScrollView>
<EditView
downTask={downTask}
downloadList={downloadList}
/>
</View>
);
}
Example #21
Source File: RootView.tsx From BitcoinWalletMobile with MIT License | 4 votes |
RootView: React.FC<Props> = (props) => {
const Tab = createBottomTabNavigator<WalletHomeNavigationParamList>();
const getIsActive = (state: WalletState) => state.isActive
const isActive = useSelector(getIsActive)
const languageSelector = (state: WalletState) => state.language
const language = useSelector(languageSelector)
const multiDeviceSelector = (state: WalletState) => state.multiDeviceSupport
const multiDevice = useSelector(multiDeviceSelector)
const isNewWalletSelector = (state: WalletState) => state.newlyCreated
const isNewWallet = useSelector(isNewWalletSelector)
const isWalletRestoringSelector = (state: WalletState) => state.isRestoring
const isWalletRestoring = useSelector(isWalletRestoringSelector)
const dispatch = useDispatch()
const setUpWallet = async (isRestoringOldWallet: boolean) => {
await wallet.setUpSeedAndRoot()
let zeroOrMinusOne = isRestoringOldWallet ? -1 : 0
// Populate first external address
let firstExternal = await wallet.getExternalAddress(0)
dispatch(addExternalAddress(new AddressLookup(0, firstExternal, zeroOrMinusOne, false)))
// Now let's populate external address lookaheads, if we're restoring
if(isRestoringOldWallet) {
for (var i = 0; i < 20; i++) {
let external = await wallet.getExternalAddress(i + 1)
dispatch(addExternalAddress(new AddressLookup(i + 1, external, zeroOrMinusOne, true)))
}
}
// Populate first internal address
let firstInternal = await wallet.getInternalAddress(0)
dispatch(addInternalAddress(new AddressLookup(0, firstInternal, zeroOrMinusOne, false)))
// Now let's populate internal address lookaheads, if we're restoring
if(isRestoringOldWallet) {
for (var i = 0; i < 20; i++) {
let internal = await wallet.getInternalAddress(i + 1)
dispatch(addInternalAddress(new AddressLookup(i + 1, internal, zeroOrMinusOne, true)))
}
}
dispatch(setIsActive(true))
dispatch(setNewlyCreated(false))
}
const sync = async () => {
try {
await wallet.synchronize(!multiDevice)
}
catch (e) {
console.log(e)
}
}
const generateSeed = async () => {
let words = bip39.generateMnemonic()
await RNSecureKeyStore.set("WALLET_SEED", words, { accessible: ACCESSIBLE.ALWAYS_THIS_DEVICE_ONLY })
}
const handleEffect = async () => {
if(!isActive && !isNewWallet && !isWalletRestoring) {
generateSeed()
}
if (!isActive && isNewWallet) {
setUpWallet(false)
}
if (!isActive && isWalletRestoring) {
setUpWallet(true)
}
if (isActive) {
await sync()
}
}
useEffect(() => {
handleEffect()
}, [isActive, isWalletRestoring, isNewWallet])
return (
<View style={{ flex: 1 }}>
{ (isWalletRestoring || isNewWallet) &&
<Loader title={isNewWallet ? 'Creating wallet' : getTranslated(language).restoring} subTitle="This might take a second" />
}
{
isActive && !isWalletRestoring && !isNewWallet &&
<Tab.Navigator tabBarOptions={{ labelStyle: { fontFamily: 'TitilliumWeb-Regular', fontWeight: '600', fontSize: 10, paddingBottom: Platform.OS == 'android' ? 5 : 0 }, inactiveBackgroundColor: '#090C14', activeTintColor: '#F7931A', activeBackgroundColor: '#090C14', style: { backgroundColor: '#090C14', borderTopColor: '#1F232E' } }}>
<Tab.Screen name="Overview" component={Overview} options={{ tabBarLabel: getTranslated(language).overview, tabBarIcon: (tabProps) => { return tabProps.focused ? <Image style={styles.tabIcon} source={require('../assets/images/collection-focus.png')} /> : <Image style={styles.tabIcon} source={require('../assets/images/collection.png')} />; } }} />
<Tab.Screen name="Send" component={shell} options={{ tabBarLabel: getTranslated(language).send, tabBarButton: (values) => (<TouchableOpacity {...values} onPress={() => props.navigation.push('Send')} />), tabBarIcon: (tabProps) => { return tabProps.focused ? <Image style={styles.tabIcon} source={require('../assets/images/send-focus.png')} /> : <Image style={styles.tabIcon} source={require('../assets/images/send.png')} />; } }} />
<Tab.Screen name="Receive" component={Receive} options={{ tabBarLabel: getTranslated(language).receive, tabBarIcon: (tabProps) => { return tabProps.focused ? <Image style={styles.tabIcon} source={require('../assets/images/receive-focus.png')} /> : <Image style={styles.tabIcon} source={require('../assets/images/receive.png')} />; } }} />
<Tab.Screen name="Settings" component={Settings} options={{ tabBarLabel: getTranslated(language).settings, tabBarIcon: (tabProps) => { return tabProps.focused ? <Image style={styles.tabIcon} source={require('../assets/images/gear-focus.png')} /> : <Image style={styles.tabIcon} source={require('../assets/images/gear.png')} />; } }} />
</Tab.Navigator>
}
{
!isActive && !isNewWallet && !isWalletRestoring &&
<View style={{ flex: 1 }}>
<Header screen={getTranslated(language).getting_started} currentLanguage={getLanguageBigName(language)} action={() => { props.navigation.navigate('PickerView', { type: "Choose Language" }) }} />
<Screen>
<ScrollView>
<View style={styles.container}>
<View style={styles.onboard}>
<Text style={styles.headingText}>{getTranslated(language).getting_started}</Text>
<View style={styles.buttonContainer}>
<LinearGradient useAngle={true} angle={180} angleCenter={{ x: 0.5, y: 0.5 }} colors={['#1f232e', '#13161f']}>
<TouchableOpacity style={styles.onboardButton} onPress={() => props.navigation.navigate('CreateStepOne')}>
<View style={styles.buttonContent}>
<Image style={styles.onboardIcon} source={require('../assets/images/create.png')} />
<View style={{ marginLeft: 20 }}>
<Text style={styles.onboardButtonText}>{getTranslated(language).create_new}</Text>
< Text style={styles.onboardButtonSubText}>{getTranslated(language).create_subtext}</Text>
</View>
</View>
</TouchableOpacity>
</LinearGradient>
</View>
<View style={styles.buttonContainer}>
<LinearGradient useAngle={true} angle={180} angleCenter={{ x: 0.5, y: 0.5 }} colors={['#1f232e', '#13161f']}>
<TouchableOpacity style={styles.onboardButton} onPress={() => props.navigation.navigate('Restore')}>
<View style={styles.buttonContent}>
<Image style={styles.onboardIcon} source={require('../assets/images/restore.png')} />
<View style={{ marginLeft: 20 }}>
<Text style={styles.onboardButtonText}>{getTranslated(language).restore_existing}</Text>
<Text style={styles.onboardButtonSubText}>{getTranslated(language).restore_subtext}</Text>
</View>
</View>
</TouchableOpacity>
</LinearGradient>
</View>
</View>
</View>
</ScrollView>
</Screen>
</View>
}
</View>
);
}
Example #22
Source File: insightDrawer.tsx From iotc-cpm-sample with MIT License | 4 votes |
/**
* This navigator doesn't actually navigate to any screen.
* It is used to have a drawer for chart management by levereging on what react-navigation already offers (gestures, styles...).
* @param props
*/
export default function InsightDrawer(props: DrawerProps) {
const {state, dispatch} = useContext(ConfigContext);
const {currentScreen} = props;
let icon: any = 'bluetooth';
if (state.healthManager) {
if (state.healthManager instanceof AppleHealthManager) {
icon = ({size}: {size: number}) => (
<Image
source={require('../assets/health_kit.png')}
style={{width: 60, height: 60}}
/>
);
} else if (state.healthManager instanceof GoogleFitManager) {
icon = ({size}: {size: number}) => (
<Image
source={require('../assets/google_fit.png')}
style={{width: size, height: size - 5}}
/>
);
}
}
if (
!state.device ||
!state.device.items ||
currentScreen !== Screens.INSIGHT_SCREEN
) {
return null;
}
return (
<SafeAreaView style={style.container}>
<View style={style.header}>
<IconButton
icon={icon}
size={30}
style={{marginLeft: -5, marginRight: 20}}
/>
<View style={{width: '60%', paddingBottom: 100}}>
<View style={{flexDirection: 'row'}}>
<Headline>Sync options</Headline>
<IconButton
onPress={() => {
props.close();
}}
icon="chevron-left"
style={{marginLeft: 40, marginTop: -5}}
/>
</View>
<Detail>Which kind of device data would you like to show?</Detail>
</View>
</View>
<Name style={{marginBottom: 20}}>{state.device.name}</Name>
<Divider />
<ScrollView>
{state.device.items.map((item, index) => (
<View style={style.itemContainer} key={`view-${item.id}`}>
<Item style={{width: 150}}>{item.name}</Item>
{/* pass extra parameter to the ref in order to process and enable only valid ids */}
<Switch
{...{refId: `${item.parentId}/${item.id}`}}
value={item.enabled}
onValueChange={async current => {
await item.enable(current);
// dispatch is needed to update state of device items
dispatch({
type: 'HEALTH_CONNECT',
payload: state.device,
});
}}
/>
</View>
))}
</ScrollView>
</SafeAreaView>
);
}
Example #23
Source File: SignUp.tsx From DoobooIAP with MIT License | 4 votes |
function Page(props: Props): ReactElement {
const { navigation } = props;
const [email, setEmail] = useState<string>('');
const [password, setPassword] = useState<string>('');
const [confirmPassword, setConfirmPassword] = useState<string>('');
const [name, setName] = useState<string>('');
const [statusMessage, setStatusMessage] = useState<string>('');
const [errorEmail, setErrorEmail] = useState<string>('');
const [errorPassword, setErrorPassword] = useState<string>('');
const [errorConfirmPassword, setErrorConfirmPassword] = useState<string>('');
const [errorName, setErrorName] = useState<string>('');
const [signingUp, setSigningUp] = useState<boolean>(false);
const { theme } = useThemeContext();
const requestSignUp = async (): Promise<void> => {
if (!validateEmail(email)) {
setErrorEmail(getString('EMAIL_FORMAT_NOT_VALID'));
}
if (password !== confirmPassword) {
setErrorConfirmPassword(getString('PASSWORD_MUST_MATCH'));
}
setSigningUp(true);
try {
await firebase.auth().createUserWithEmailAndPassword(email, password);
const currentUser = firebase.auth().currentUser;
if (currentUser) {
await Promise.all([
currentUser.updateProfile({
displayName: name,
}),
firebase
.firestore()
.collection('users')
.doc(currentUser.uid)
.set({
email,
name,
}),
currentUser.sendEmailVerification(),
]);
}
navigation.goBack();
Alert.alert(getString('SUCCESS'), getString('EMAIL_VERIFICATION_SENT'));
} catch (err) {
Alert.alert(getString('ERROR'), `${err.code}: ${err.message}`);
} finally {
setSigningUp(false);
}
};
return (
<Container>
<ScrollView style={{ alignSelf: 'stretch' }}>
<Wrapper>
<EditText
testID="input-email"
errorTestID="error-email"
textStyle={{
color: theme.font,
}}
borderColor={theme.font}
focusColor={theme.focused}
placeholderTextColor={theme.placeholder}
label={getString('EMAIL')}
placeholder="[email protected]"
value={email}
onChangeText={(text: string): void => {
setEmail(text);
setErrorEmail('');
}}
errorText={errorEmail}
onSubmitEditing={requestSignUp}
/>
<EditText
testID="input-password"
errorTestID="error-password"
textStyle={{
color: theme.font,
}}
borderColor={theme.font}
focusColor={theme.focused}
placeholderTextColor={theme.placeholder}
placeholder="********"
label={getString('PASSWORD')}
value={password}
onChangeText={(text: string): void => {
setPassword(text);
setErrorPassword('');
}}
style={{ marginTop: 32 }}
errorText={errorPassword}
onSubmitEditing={requestSignUp}
secureTextEntry={true}
/>
<EditText
testID="input-confirm-password"
errorTestID="error-confirm-password"
textStyle={{
color: theme.font,
}}
placeholder="********"
label={getString('PASSWORD_CONFIRM')}
value={confirmPassword}
onChangeText={(text: string): void => {
setConfirmPassword(text);
setErrorConfirmPassword('');
}}
style={{ marginTop: 32 }}
borderColor={theme.font}
focusColor={theme.focused}
placeholderTextColor={theme.placeholder}
errorText={errorConfirmPassword}
onSubmitEditing={requestSignUp}
secureTextEntry={true}
/>
<EditText
testID="input-name"
errorTestID="error-name"
textStyle={{
color: theme.font,
}}
label={getString('NAME')}
placeholder={getString('NAME_HINT')}
borderColor={theme.font}
focusColor={theme.focused}
placeholderTextColor={theme.placeholder}
value={name}
onChangeText={(text: string): void => {
setName(text);
setErrorName('');
}}
style={{ marginTop: 32 }}
errorText={errorName}
onSubmitEditing={requestSignUp}
/>
<ButtonWrapper>
<Button
testID="btn-sign-up"
isLoading={signingUp}
onPress={requestSignUp}
containerStyle={{
height: 52,
width: '50%',
backgroundColor: theme.primary,
}}
textStyle={{
color: theme.fontInverse,
fontSize: 16,
fontWeight: 'bold',
}}
text={getString('SIGN_UP')}
/>
</ButtonWrapper>
</Wrapper>
</ScrollView>
</Container>
);
}
Example #24
Source File: Transfer.tsx From hive-keychain-mobile with MIT License | 4 votes |
Transfer = ({
currency,
user,
loadAccount,
engine,
tokenBalance,
tokenLogo,
phishingAccounts,
}: Props) => {
const [to, setTo] = useState('');
const [amount, setAmount] = useState('');
const [memo, setMemo] = useState('');
const [recurrence, setRecurrence] = useState('');
const [exec, setExec] = useState('');
const [loading, setLoading] = useState(false);
const [step, setStep] = useState(1);
const [privacy, setPrivacy] = useState(PUBLIC);
const [isRecurrent, setRecurrent] = useState(false);
const sendTransfer = async () => {
setLoading(true);
let finalMemo = memo;
if (privacy === PRIVATE) {
const receiverMemoKey = (await getAccountKeys(to.toLowerCase())).memo;
finalMemo = await encodeMemo(user.keys.memo, receiverMemoKey, `#${memo}`);
}
if (!isRecurrent) {
await transfer(user.keys.active, {
amount: sanitizeAmount(amount, currency),
memo: finalMemo,
to: sanitizeUsername(to),
from: user.account.name,
});
} else {
await recurrentTransfer(user.keys.active, {
amount: sanitizeAmount(amount, currency),
memo: finalMemo,
to: sanitizeUsername(to),
from: user.account.name,
recurrence: +recurrence,
executions: +exec,
extensions: [],
});
}
};
const transferToken = async () => {
setLoading(true);
return await sendToken(user.keys.active, user.name, {
symbol: currency,
to: sanitizeUsername(to),
quantity: sanitizeAmount(amount),
memo: memo,
});
};
const onSend = async () => {
Keyboard.dismiss();
try {
if (!engine) {
await sendTransfer();
Toast.show(
translate(
isRecurrent
? 'toast.recurrent_transfer_success'
: 'toast.transfer_success',
),
Toast.LONG,
);
} else {
const {id} = await transferToken();
const {confirmed} = await tryConfirmTransaction(id);
Toast.show(
confirmed
? translate('toast.transfer_token_confirmed')
: translate('toast.transfer_token_unconfirmed'),
Toast.LONG,
);
}
loadAccount(user.account.name, true);
goBack();
} catch (e) {
Toast.show(
beautifyTransferError(e as any, {
to,
currency,
username: user.account.name,
}),
Toast.LONG,
);
setLoading(false);
}
};
const {color} = getCurrencyProperties(currency);
const {height} = useWindowDimensions();
const styles = getDimensionedStyles(color, height);
if (step === 1) {
return (
<Operation
logo={<SendArrowBlue />}
title={translate('wallet.operations.transfer.title')}>
<ScrollView>
<Separator />
<Balance
currency={currency}
account={user.account}
tokenBalance={tokenBalance}
tokenLogo={tokenLogo}
engine={engine}
setMax={(value: string) => {
setAmount(value);
}}
/>
<Separator />
<OperationInput
placeholder={translate('common.username').toUpperCase()}
leftIcon={<AccountLogoDark />}
autoCapitalize="none"
value={to}
onChangeText={setTo}
/>
<Separator />
<OperationInput
placeholder={'0.000'}
keyboardType="decimal-pad"
rightIcon={<Text style={styles.currency}>{currency}</Text>}
textAlign="right"
value={amount}
onChangeText={setAmount}
/>
<Separator />
<OperationInput
placeholder={translate('wallet.operations.transfer.memo')}
value={memo}
onChangeText={setMemo}
/>
<Separator />
<CustomRadioGroup
list={[PUBLIC, PRIVATE]}
selected={privacy}
onSelect={setPrivacy}
/>
<Separator height={20} />
<OptionsToggle
title="Recurrent transfers"
toggled={isRecurrent}
callback={(toggled) => {
setRecurrent(toggled);
}}>
<Separator />
<OperationInput
placeholder={translate('wallet.operations.transfer.recurrence')}
value={recurrence}
onChangeText={setRecurrence}
keyboardType={'number-pad'}
rightIcon={<Text>Hours</Text>}
leftIcon={<Text>Every</Text>}
/>
<Separator />
<OperationInput
placeholder={translate('wallet.operations.transfer.executions')}
value={exec}
onChangeText={setExec}
keyboardType={'number-pad'}
rightIcon={<Text>times</Text>}
/>
</OptionsToggle>
<Separator height={20} />
<ActiveOperationButton
title={translate('common.send')}
onPress={() => {
if (!amount.length || !to.length) {
Toast.show(
translate('wallet.operations.transfer.warning.missing_info'),
);
} else {
setStep(2);
}
}}
style={styles.send}
isLoading={loading}
/>
</ScrollView>
</Operation>
);
} else {
return (
<Operation
logo={<SendArrowBlue />}
title={translate('wallet.operations.transfer.title')}>
<ScrollView>
<Separator height={30} />
<Text style={styles.warning}>
{getTransferWarning(phishingAccounts, to, currency, !!memo).warning}
</Text>
<Separator />
<Text style={styles.title}>
{translate('wallet.operations.transfer.confirm.from')}
</Text>
<Text>{`@${user.account.name}`}</Text>
<Separator />
<Text style={styles.title}>
{translate('wallet.operations.transfer.confirm.to')}
</Text>
<Text>{`@${to} ${
getTransferWarning(phishingAccounts, to, currency, !!memo).exchange
? '(exchange)'
: ''
}`}</Text>
<Separator />
<Text style={styles.title}>
{translate('wallet.operations.transfer.confirm.amount')}
</Text>
<Text>{`${amount} ${currency}`}</Text>
{memo.length ? (
<>
<Separator />
<Text style={styles.title}>
{translate('wallet.operations.transfer.confirm.memo')}
</Text>
<Text>{`${memo} ${
privacy === PRIVATE ? '(encrypted)' : ''
}`}</Text>
</>
) : null}
<Separator />
{isRecurrent ? (
<>
<Text style={styles.title}>
{translate('wallet.operations.transfer.confirm.recurrence')}
</Text>
<Text>
{translate(
'wallet.operations.transfer.confirm.recurrenceData',
{exec, recurrence},
)}
</Text>
</>
) : null}
<Separator height={40} />
<View style={styles.buttonsContainer}>
<EllipticButton
title={translate('common.back')}
style={styles.back}
onPress={() => {
setStep(1);
}}
/>
<ActiveOperationButton
title={translate('common.confirm')}
onPress={onSend}
style={styles.confirm}
isLoading={loading}
/>
</View>
</ScrollView>
</Operation>
);
}
}
Example #25
Source File: index.tsx From hive-keychain-mobile with MIT License | 4 votes |
UrlModal = ({
isVisible,
toggle,
onNewSearch,
url,
setUrl,
history,
clearHistory,
}: Props) => {
const urlInput: MutableRefObject<TextInput> = useRef();
const insets = useSafeAreaInsets();
const styles = getStyles(insets);
if (isVisible && urlInput) {
setTimeout(() => {
const {current} = urlInput;
if (current && !current.isFocused()) {
current.focus();
}
}, SLIDE_TIME);
}
const onSubmitUrlFromInput = (
obj: NativeSyntheticEvent<TextInputSubmitEditingEventData>,
) => {
const url = obj.nativeEvent.text;
onSubmitUrl(url);
};
const onSubmitUrl = (url: string) => {
toggle(false);
// Add duckduck go search for url with no domain
if (url.includes(' ') || !url.includes('.')) {
onNewSearch(`https://duckduckgo.com/?q=${url.replace(/ /g, '+')}`);
} else {
const hasProtocol = url.match(/^[a-z]*:\/\//);
const sanitizedURL = hasProtocol ? url : `https://${url}`;
onNewSearch(sanitizedURL);
}
};
const dismissModal = () => {
toggle(false);
};
return (
<Modal
isVisible={isVisible}
style={styles.urlModal}
onBackdropPress={dismissModal}
onBackButtonPress={dismissModal}
animationIn="slideInDown"
animationOut="slideOutUp"
backdropOpacity={0.8}
animationInTiming={SLIDE_TIME}
animationOutTiming={SLIDE_TIME}
useNativeDriver>
<View style={styles.urlModalContent}>
<TextInput
keyboardType="web-search"
ref={urlInput}
autoCapitalize="none"
autoCorrect={false}
clearButtonMode="never"
onChangeText={setUrl}
onSubmitEditing={onSubmitUrlFromInput}
placeholder={translate('browser.search')}
returnKeyType="go"
style={styles.urlInput}
value={url}
selectTextOnFocus
/>
{url.length ? (
<TouchableOpacity
style={styles.option}
onPress={() => Share.share({message: url})}>
<ShareIcon width={16} height={16} />
</TouchableOpacity>
) : null}
{url.length ? (
<TouchableOpacity
style={styles.option}
onPress={() => Clipboard.setString(url)}>
<Copy width={16} height={16} />
</TouchableOpacity>
) : null}
{url.length ? (
<TouchableOpacity style={styles.option} onPress={() => setUrl('')}>
<Text style={styles.eraseText}>X</Text>
</TouchableOpacity>
) : null}
</View>
<ScrollView>
<UrlAutocomplete onSubmit={onSubmitUrl} input={url} history={history} />
{history.length ? (
<TouchableOpacity onPress={clearHistory}>
<Text style={styles.clearHistory}>
{translate('browser.history.clear')}
</Text>
</TouchableOpacity>
) : null}
</ScrollView>
</Modal>
);
}
Example #26
Source File: Cards1.tsx From react-native-meetio with MIT License | 4 votes |
Cards = () => {
const theme = useTheme<Theme>();
return (
<Box backgroundColor="lightBlueMagenta100" width={width}>
<ScrollView
showsVerticalScrollIndicator={false}
contentContainerStyle={{ height: height * 2 + 500 }}
>
<Box>
<Card
{...StyleSheet.absoluteFillObject}
backgroundColor="white"
height={290}
borderBottomLeftRadius={80}
overflow="visible"
top={1270}
>
<Box
flex={1}
justifyContent="center"
alignItems="center"
style={{ marginTop: 50 }}
>
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
<Card width={150} height={240} marginHorizontal="m">
<Image
source={Images.MEDITATION1}
style={StyleSheet.absoluteFillObject}
/>
<Box
flex={1}
justifyContent="center"
alignItems="center"
style={{ marginTop: 50 }}
>
<Box>
<Text
variant="title2"
fontSize={14}
lineHeight={18}
color="white"
>
Meditation
</Text>
<Box flexDirection="row" marginTop="m">
<Text
variant="text1"
fontSize={12}
lineHeight={15}
color="white"
>
4.6
</Text>
<Box justifyContent="center" style={{ marginLeft: 4 }}>
<Icons.Star />
</Box>
</Box>
</Box>
</Box>
</Card>
<Card width={150} height={240} marginHorizontal="m">
<Image
source={Images.YOGA1}
style={StyleSheet.absoluteFillObject}
/>
<Box
flex={1}
justifyContent="center"
alignItems="center"
style={{ marginTop: 50 }}
>
<Box>
<Text
variant="title2"
fontSize={14}
lineHeight={18}
color="white"
>
Yoga
</Text>
<Box flexDirection="row" marginTop="m">
<Text
variant="text1"
fontSize={12}
lineHeight={15}
color="white"
>
3.8
</Text>
<Box justifyContent="center" style={{ marginLeft: 4 }}>
<Icons.Star />
</Box>
</Box>
</Box>
</Box>
</Card>
<Card
width={150}
height={240}
marginHorizontal="m"
justifyContent="center"
alignItems="center"
>
<Image
source={Images.SUSHI}
style={StyleSheet.absoluteFillObject}
/>
<Box
flex={1}
justifyContent="center"
alignItems="center"
style={{ marginTop: 50 }}
>
<Box>
<Box>
<Text
variant="title2"
fontSize={14}
lineHeight={18}
color="white"
>
Sushi Place
</Text>
<Box flexDirection="row" marginTop="m">
<Text
variant="text1"
fontSize={12}
lineHeight={15}
color="white"
>
3.4
</Text>
<Box
justifyContent="center"
style={{ marginLeft: 4 }}
>
<Icons.Star />
</Box>
</Box>
</Box>
</Box>
</Box>
</Card>
</ScrollView>
</Box>
</Card>
<Card
{...StyleSheet.absoluteFillObject}
backgroundColor="darkBlueMagenta800"
height={290}
borderBottomLeftRadius={80}
overflow="visible"
top={1030}
>
<Box paddingHorizontal="xl" style={{ marginTop: 100 }}>
<Box>
<Box
flexDirection="row"
justifyContent="space-between"
height={50}
>
<Box flexDirection="row" height={50}>
<Image source={Images.AVATAR1} />
<Box marginLeft="m">
<Text variant="title2" color="white">
Abdullah Hadley
</Text>
<Text
variant="text1"
fontSize={12}
lineHeight={15}
color="white"
opacity={0.56}
>
8 Nov
</Text>
</Box>
</Box>
<Icons.Chevron />
</Box>
<Text
variant="text1"
fontSize={13}
lineHeight={22}
color="white"
opacity={0.64}
>
Believe in yourself, take on your challenges, dig deep within
yourself to conquer fears. Never let anyone bring you down.
You got to keep going.
</Text>
<Box
flexDirection="row"
justifyContent="flex-end"
marginTop="m"
>
<Box flexDirection="row">
<Text
variant="title2"
fontSize={12}
lineHeight={15}
color="lightBlue200"
style={{ padding: 4 }}
>
256
</Text>
<Icons.Comment strokeColor={theme.colors.blueMagenta} />
</Box>
<Box flexDirection="row" marginLeft="m">
<Text
variant="title2"
fontSize={12}
lineHeight={15}
color="lightBlue200"
style={{ padding: 4 }}
>
428
</Text>
<Icons.Like strokeColor={theme.colors.blueMagenta} />
</Box>
</Box>
</Box>
</Box>
</Card>
<Card
{...StyleSheet.absoluteFillObject}
backgroundColor="white"
height={350}
top={750}
borderBottomLeftRadius={80}
overflow="visible"
>
<Box paddingHorizontal="xl" style={{ marginTop: 100 }}>
<Box>
<Box
flexDirection="row"
justifyContent="space-between"
height={50}
>
<Box flexDirection="row">
<Image source={Images.AVATAR1} />
<Box marginLeft="m">
<Text variant="title2" color="darkBlueMagenta800">
Abdullah Hadley
</Text>
<Text
variant="text1"
fontSize={12}
lineHeight={15}
color="darkBlueMagenta700"
>
8 Nov
</Text>
</Box>
</Box>
<Icons.Chevron />
</Box>
<Box alignSelf="center">
<Text
variant="text1"
fontSize={13}
lineHeight={22}
color="blueMagenta400"
marginBottom="m"
>
When I was 5 years old, my mother always told me
</Text>
<Box alignItems="flex-end">
<Image source={Images.FILM} />
</Box>
</Box>
<Box
flexDirection="row"
justifyContent="flex-end"
marginTop="m"
>
<Box flexDirection="row">
<Text
variant="title2"
fontSize={12}
lineHeight={15}
color="blueMagenta400"
style={{ padding: 4 }}
>
256
</Text>
<Icons.Comment />
</Box>
<Box flexDirection="row" marginLeft="m">
<Text
variant="title2"
fontSize={12}
lineHeight={15}
color="blueMagenta400"
style={{ padding: 4 }}
>
428
</Text>
<Icons.Like />
</Box>
</Box>
</Box>
</Box>
</Card>
<Card
{...StyleSheet.absoluteFillObject}
height={338}
backgroundColor="darkBlueMagenta800"
borderBottomLeftRadius={80}
top={480}
>
<Box flex={1} justifyContent="center" paddingHorizontal="xl">
<Box style={{ marginTop: 150 }}>
<Text
variant="title3"
fontSize={11}
lineHeight={14}
color="white"
opacity={0.48}
>
FRIDAY 6:00 PM
</Text>
<Text
variant="title1"
lineHeight={30}
color="white"
paddingVertical="s"
>
Adobe XD Live Event in Europe
</Text>
</Box>
<Box flexDirection="row" marginTop="m">
<Box>
<Box {...StyleSheet.absoluteFillObject} left={15}>
<Image source={Images.AVATAR5} />
</Box>
<Box>
<Image source={Images.AVATAR3} />
</Box>
</Box>
<Box marginLeft="l" justifyContent="center">
<Text variant="italicText" color="white" opacity={0.48}>
Paul, Carl & 10 others
</Text>
</Box>
</Box>
</Box>
<View style={styles.xdIcon}>
<Icons.Xd />
</View>
</Card>
<Card
{...StyleSheet.absoluteFillObject}
height={351}
backgroundColor="lightPink"
borderBottomLeftRadius={80}
top={250}
>
<Box flex={1} justifyContent="center" paddingHorizontal="xl">
<Box style={{ marginTop: 150 }}>
<Text
variant="title3"
fontSize={11}
lineHeight={14}
color="white"
opacity={0.48}
>
TODAY 5:30 PM
</Text>
<Text
variant="title1"
lineHeight={30}
color="white"
paddingVertical="s"
>
Yoga and Meditation for Beginners
</Text>
</Box>
<Box flexDirection="row" marginTop="m">
<Box>
<Box {...StyleSheet.absoluteFillObject} left={15}>
{/* <Icons.Avatar2 /> */}
<Image source={Images.AVATAR4} />
</Box>
<Box>
<Image source={Images.AVATAR3} />
</Box>
</Box>
<Box marginLeft="l" justifyContent="center">
<Text variant="italicText" color="white" opacity={0.48}>
join Marie, John & 10 others
</Text>
</Box>
</Box>
</Box>
</Card>
<Card
backgroundColor="white"
height={318}
borderBottomLeftRadius={80}
overflow="visible"
>
<Box paddingHorizontal="xl" style={{ marginTop: 100 }}>
<Box>
<Box
flexDirection="row"
justifyContent="space-between"
height={50}
>
<Box flexDirection="row" height={50}>
<Image source={Images.AVATAR2} />
<Box marginLeft="m">
<Text variant="title2" color="darkBlueMagenta800">
Jerome Gaveau
</Text>
<Text
variant="text1"
fontSize={12}
lineHeight={15}
color="darkBlueMagenta700"
>
8 Nov
</Text>
</Box>
</Box>
<Icons.Chevron />
</Box>
<Text
variant="text1"
fontSize={13}
lineHeight={22}
color="blueMagenta400"
>
When one door of happiness closes, another opens, but often we
look so long at the closed door that we do not see the one
that has been opened for us.
</Text>
<Box
flexDirection="row"
justifyContent="flex-end"
marginTop="m"
>
<Box flexDirection="row">
<Text
variant="title2"
fontSize={12}
lineHeight={15}
color="blueMagenta400"
style={{ padding: 4 }}
>
256
</Text>
<Icons.Comment />
</Box>
<Box flexDirection="row" marginLeft="m">
<Text
variant="title2"
fontSize={12}
lineHeight={15}
color="blueMagenta400"
style={{ padding: 4 }}
>
428
</Text>
<Icons.Like />
</Box>
</Box>
</Box>
</Box>
</Card>
</Box>
<Box top={1300}>
<Card
width={219}
height={172}
backgroundColor="lightPink"
alignSelf="center"
borderWidth={1.5}
borderColor="darkBlueMagenta800"
borderTopLeftRadius={40}
borderBottomLeftRadius={10}
>
<Image
source={Images.JONATHAN_FORAGE}
style={{
...StyleSheet.absoluteFillObject,
width: undefined,
height: undefined,
}}
/>
<Box flex={1}>
<Box flex={0.6} />
<Box
flex={0.4}
backgroundColor="darkBlueMagenta700"
justifyContent="center"
>
<Box marginLeft="l">
<Text
variant="title1"
fontSize={14}
lineHeight={18}
color="white"
>
NomNom
</Text>
<Text
variant="text1"
fontSize={11}
lineHeight={14}
color="white"
opacity={0.56}
>
2mi, 5 stars
</Text>
</Box>
</Box>
</Box>
</Card>
<Card
width={width - 100}
height={185}
backgroundColor="darkBlueMagenta700"
alignSelf="center"
marginTop="xl"
borderRadius={40}
shadowOffset={{ width: 0, height: 4 }}
shadowRadius={16}
padding="l"
>
<Box>
<Box flexDirection="row" justifyContent="space-between">
<Text variant="title2" color="white">
Description
</Text>
<Icons.Chevron fillColor="#fff" />
</Box>
<Text variant="text2" color="blueMagenta" marginTop="m">
When one door of happiness closes, another opens, but often we
look so long at the closed door that we do not see the one that
has been opened for us.
</Text>
</Box>
</Card>
<Card
backgroundColor="white"
height={110}
marginTop="l"
borderBottomLeftRadius={80}
/>
<Box
height={56}
flexDirection="row"
justifyContent="flex-end"
style={{ marginTop: -20, marginRight: 20 }}
>
<Box
backgroundColor="white"
width={56}
height={56}
justifyContent="center"
alignItems="center"
borderRadius={56}
>
<Icons.Add />
</Box>
</Box>
</Box>
</ScrollView>
<Header
{...StyleSheet.absoluteFillObject}
title="CARDS"
titleVariant="title3"
backgroundColor="lightBlueMagenta100"
borderBottomLeftRadius={70}
/>
</Box>
);
}
Example #27
Source File: Restore.tsx From BitcoinWalletMobile with MIT License | 4 votes |
Restore: React.FC<Props> = (props) => {
const insets = useSafeAreaInsets()
const [isValid, setIsValid] = useState(false)
const [fieldsWithError, setFieldsWithError] = useState([-1])
const [words, setWords] = useState(["", "", "", "", "", "", "", "", "", "", "", ""])
const refs = [createRef<TextInput>(), createRef<TextInput>(), createRef<TextInput>(), createRef<TextInput>(), createRef<TextInput>(), createRef<TextInput>(), createRef<TextInput>(), createRef<TextInput>(), createRef<TextInput>(), createRef<TextInput>(), createRef<TextInput>(), createRef<TextInput>()]
const updateWord = (word: string, index: number) => {
if((index-1) >= 0) {
fixWord(index-1)
}
let newWords = [...words]
for (var i = 0; i < newWords.length; i++) {
if (index == i) {
newWords[i] = word
}
}
let errorFields = [...fieldsWithError]
if(bip39.wordlists.english.indexOf(word) == -1 && word != '') {
errorFields.push(index)
}
else {
if(fieldsWithError.indexOf(index) != -1) {
errorFields = fieldsWithError.filter((f) => f != index)
}
}
setFieldsWithError(errorFields)
setWords(newWords)
}
const fixWord = (index : number) => {
if (index < 0) {
return
}
let newWords = [...words]
newWords[index] = words[index].trim().toLowerCase()
let errorFields = [...fieldsWithError]
if(bip39.wordlists.english.indexOf(newWords[index]) == -1 && words[index] != '') {
errorFields.push(index)
}
else {
if(fieldsWithError.indexOf(index) != -1) {
errorFields = fieldsWithError.filter((f) => f != index)
}
}
setFieldsWithError(errorFields)
setWords(newWords)
}
const nextTextInput = (index: number) => {
fixWord(index-1)
refs[index].current?.focus()
}
const dispatch = useDispatch()
const languageSelector = (state: WalletState) => state.language
const language = useSelector(languageSelector)
useEffect(() => {
setIsValid(bip39.validateMnemonic(words.join(" ")))
}, [words])
const restoreWallet = async () => {
Keyboard.dismiss()
if(!isValid) {
return
}
try {
//Store the seed in the keychain
await RNSecureKeyStore.set("WALLET_SEED", words.join(" ").toLowerCase(), { accessible: ACCESSIBLE.ALWAYS_THIS_DEVICE_ONLY })
// State change to indicate we are restoring so the main wallet screen knows to do a full sync
dispatch(isRestoring(true))
// Let's go back
props.navigation.goBack()
}
catch {
}
}
return (
<View style={{ flex: 1 }}>
<View style={{ flex: 1 }}>
<Header screen={getTranslated(language).restore_existing} action={() => { props.navigation.goBack() }} />
<Screen>
<View style={{ flex: 1 }}>
<ScrollView contentContainerStyle={{ flexGrow: 1, justifyContent: 'space-between' }}>
<Text style={styles.subHeading}>{getTranslated(language).restore_notice}</Text>
<View style={{ flex: 1, marginTop: 20, flexDirection: 'row', flexWrap: 'wrap', marginHorizontal: 10, alignItems: 'flex-start' }}>
{words.map((word, index) => {
return (
<View key={index} style={{ flexDirection: 'row', alignItems: 'center', width: '50%', flexWrap: 'wrap', marginTop: 10, }}>
<Text style={styles.numberedLabel}>{index + 1}.</Text>
<TextInput
style={[styles.recoveryField, fieldsWithError.filter((f) => f == index).length > 0 ? styles.recoveryErrorBorder : styles.recoveryNormalBorder]}
ref={refs[index]}
returnKeyType="next"
keyboardType="ascii-capable"
autoCorrect={false}
autoCapitalize="none"
value={word}
onBlur={() => { fixWord(index) }}
onChangeText={(text) => updateWord(text, index)}
onSubmitEditing={() => { index < 11 ? nextTextInput(index + 1) : restoreWallet() }}
blurOnSubmit={false}
/>
</View>
)
})
}
</View>
<View style={{ marginBottom: insets.bottom + 30, marginLeft: 16 }}>
{!isValid &&
<ButtonPrimaryDisabled text={getTranslated(language).restore_button} action={restoreWallet} />
}
{isValid &&
<ButtonPrimary text={getTranslated(language).restore_button} action={restoreWallet} />
}
</View>
</ScrollView>
</View>
</Screen>
</View>
</View>
);
}
Example #28
Source File: Send.tsx From BitcoinWalletMobile with MIT License | 4 votes |
Send: React.FC<Props> = (props) => {
const insets = useSafeAreaInsets()
let btcTextRef = createRef<TextInput>()
let fiatTextRef = createRef<TextInput>()
const currencySelector = (state: WalletState) => state.currency
const currency = useSelector(currencySelector)
let canGetRates = true
const rateSelector = (state: WalletState) => {
if (state.fiatRates[currency] != undefined) {
return state.fiatRates[currency].last
}
else {
canGetRates = false
return 0
}
}
const dispatch = useDispatch()
const rate = useSelector(rateSelector)
const languageSelector = (state: WalletState) => state.language
const multiDeviceSelector = (state: WalletState) => state.multiDeviceSupport
const multiDevice = useSelector(multiDeviceSelector)
const language = useSelector(languageSelector)
const balanceSelector = (state: WalletState) => state.balance
const balance = useSelector(balanceSelector)
const utxosSelector = (state: WalletState) => state.utxos
const utxos = useSelector(utxosSelector)
const feeRatesSelector = (state: WalletState) => state.feeRates
const feeRates = useSelector(feeRatesSelector)
const [isSendingMax, setSendingMax] = useState(false)
const [confirmModalShowing, setConfirmModalShowing] = useState(false)
const [isSendingNotice, setisSendingNotice] = useState(false)
const [sentTransactionModalShowing, setSentTransactionModalShowing] = useState(false)
const [sendEnabled, setSendEnabled] = useState(false)
const [feeAmount, setFeeAmount] = useState("0")
const [feeAmountFiat, setFeeAmountFiat] = useState(new Intl.NumberFormat(language, { style: 'currency', currency: currency }).format(0))
const [totalAmount, setTotalAmount] = useState("0")
const [totalAmountFiat, setTotalAmountFiat] = useState(new Intl.NumberFormat(language, { style: 'currency', currency: currency }).format(0))
const [feeLevel, setFeeLevel] = useState(1)
const [address, setAddress] = useState("")
const [btcAmount, setBtcAmount] = useState("")
const [fiatAmount, setFiatAmount] = useState("")
const [addressInvalid, setAddressInvalid] = useState(false)
const [btcAmountInvalid, setBtcAmountInvalid] = useState(false)
const [fiatAmountInvalid, setFiatAmountInvalid] = useState(false)
const [dustError, setDustError] = useState(false)
const [cantSendAmountWithFee, setCantSendAmountWithFee] = useState(false)
const [notEnoughBalance, setNotEnoughBalance] = useState(false)
const [realFee, setRealFee] = useState(0)
const feeTitles = [getTranslated(language).low_priority, getTranslated(language).standard, getTranslated(language).important]
const feeDescriptions = [getTranslated(language).low_priority_desc, getTranslated(language).standard_desc, getTranslated(language).important_desc]
const btcToFiat = () => {
let fiat = new BigNumber(balance).multipliedBy(rate).toFixed(2)
if (isNaN(parseFloat(fiat))) {
fiat = "0";
}
if (!canGetRates) {
return "N/A"
}
return new Intl.NumberFormat(language, { style: 'currency', currency: currency }).format(parseFloat(fiat));
}
const checkAddress = (newAddress: string) => {
setAddress(newAddress)
if (newAddress == "") {
setAddressInvalid(false)
setSendEnabled(false)
return;
}
try {
bitcoin.address.toOutputScript(newAddress);
setAddressInvalid(false)
setAddress(newAddress)
if (!btcAmountInvalid && !fiatAmountInvalid && btcAmount != '' && fiatAmount != '' && !notEnoughBalance && !dustError && new BigNumber(btcAmount).gt(0)) {
setSendEnabled(true)
}
}
catch {
setAddressInvalid(true)
setSendEnabled(false)
}
}
const checkValidAndCalculateFiat = (newBtc: string) => {
setBtcAmount(newBtc)
if (newBtc == '') {
setFiatAmount("")
setNotEnoughBalance(false)
setCantSendAmountWithFee(false)
setBtcAmountInvalid(false)
setFiatAmountInvalid(false)
setFeeAmount('0')
setFeeAmountFiat(new Intl.NumberFormat(language, { style: 'currency', currency: currency }).format(0))
setTotalAmount("0")
setTotalAmountFiat(new Intl.NumberFormat(language, { style: 'currency', currency: currency }).format(0))
setSendEnabled(false)
return
}
if (isNaN(parseFloat(newBtc))) {
setBtcAmountInvalid(true)
setFiatAmountInvalid(true)
setSendEnabled(false)
setNotEnoughBalance(false)
setCantSendAmountWithFee(false)
return
}
if (new BigNumber(newBtc).multipliedBy(100000000).toNumber() <= 800 && new BigNumber(newBtc).toNumber() > 0) {
setDustError(true)
setNotEnoughBalance(false)
setCantSendAmountWithFee(false)
setBtcAmountInvalid(true)
setFiatAmountInvalid(true)
}
if (canGetRates) {
let fiat = (parseFloat(newBtc) * rate).toFixed(2);
if (isNaN(parseFloat(fiat))) {
setFiatAmount("")
}
else {
setFiatAmountInvalid(false)
setBtcAmountInvalid(false)
setFiatAmount(new Intl.NumberFormat().format(parseFloat(fiat)))
}
}
if (new BigNumber(newBtc).gt(new BigNumber(balance))) {
setNotEnoughBalance(true)
setCantSendAmountWithFee(false)
setDustError(false)
}
else {
setNotEnoughBalance(false)
setDustError(false)
setCantSendAmountWithFee(false)
setFeesAndTotalAmounts(newBtc, feeLevel)
}
}
const qrCodeCallback = (scannedAddress: string, scannedAmount: string | null) => {
setAddress(scannedAddress)
setAddressInvalid(false)
if (scannedAmount != null) {
setBtcAmount(scannedAmount.toString())
setSendEnabled(true)
checkValidAndCalculateFiat(scannedAmount.toString())
}
}
const checkValidAndCalculateBtc = (newFiat: string) => {
setFiatAmount(newFiat)
if (newFiat == '') {
setBtcAmount('')
setNotEnoughBalance(false)
setFiatAmountInvalid(false)
setBtcAmountInvalid(false)
setFeeAmount('0')
setFeeAmountFiat(new Intl.NumberFormat(language, { style: 'currency', currency: currency }).format(0))
setTotalAmount("0")
setTotalAmountFiat(new Intl.NumberFormat(language, { style: 'currency', currency: currency }).format(0))
setSendEnabled(false)
return
}
if (isNaN(parseFloat(newFiat))) {
setBtcAmountInvalid(true)
setFiatAmountInvalid(true)
setFeeAmount('0')
setDustError(false)
setCantSendAmountWithFee(false)
setFeeAmountFiat(new Intl.NumberFormat(language, { style: 'currency', currency: currency }).format(0))
setTotalAmount("0")
setTotalAmountFiat(new Intl.NumberFormat(language, { style: 'currency', currency: currency }).format(0))
setSendEnabled(false)
return
}
let fiat = newFiat
// Determine the format
let format = new Intl.NumberFormat(language).format(1.5);
// Are we in commas as decimal land?
let commasAsDecimal = format == "1,5";
// Let's adjust our format
if (commasAsDecimal) {
fiat = fiat.replace(".", "");
fiat = fiat.replace(",", ".");
}
if (canGetRates) {
let fiatRate = parseFloat(fiat.replace(/,/g, ""))
let newBtc = new BigNumber(fiatRate).dividedBy(rate).toFixed(8).toString()
if (btcToFiat() == new Intl.NumberFormat(language, { style: 'currency', currency: currency }).format(parseFloat(newFiat))) {
newBtc = balance
}
if (isNaN(parseFloat(newBtc))) {
setFiatAmountInvalid(true)
setBtcAmount("")
}
else {
setBtcAmount(newBtc)
if (new BigNumber(newBtc).multipliedBy(100000000).toNumber() <= 800 && new BigNumber(newBtc).gt(0)) {
setDustError(true)
setBtcAmountInvalid(true)
setFiatAmountInvalid(true)
return
}
setFiatAmountInvalid(false)
setBtcAmountInvalid(false)
}
if (new BigNumber(newBtc).gt(new BigNumber(balance))) {
setDustError(false)
setNotEnoughBalance(true)
}
else {
setNotEnoughBalance(false)
setDustError(false)
setFeesAndTotalAmounts(newBtc, feeLevel)
}
}
}
const setFeesAndTotalAmounts = (newAmount: string, feeRate: number) => {
if (notEnoughBalance || dustError) {
return
}
let isSendingMax = new BigNumber(newAmount).eq(new BigNumber(balance))
let networkFee = '0'
if (new BigNumber(newAmount).gt(0)) {
let target = undefined
let select = accumulative
if (isSendingMax) {
target = [{ address: "3E8ociqZa9mZUSwGdSmAEMAoAxBK3FNDcd" }]
select = split
}
else {
target = [{ address: "3E8ociqZa9mZUSwGdSmAEMAoAxBK3FNDcd", value: new BigNumber(newAmount).multipliedBy(100000000).toNumber() }]
}
let { inputs, outputs, fee } = select(utxos, target, feeRates[feeRate])
let total = "0"
if (!inputs || !outputs || fee == 0) {
setCantSendAmountWithFee(true)
}
else {
setCantSendAmountWithFee(false)
networkFee = isSendingMax ? new BigNumber(fee).dividedBy(100000000).negated().toFixed(8) : new BigNumber(fee).dividedBy(100000000).toFixed(8)
}
total = new BigNumber(networkFee).plus(newAmount).toFixed(8)
if (new BigNumber(newAmount).gt(new BigNumber(balance))) {
return
}
else {
setTotalAmount(total)
setFeeAmount(networkFee)
if (!addressInvalid && address != '') {
setSendEnabled(true)
}
if (canGetRates) {
setFeeAmountFiat(new Intl.NumberFormat(language, { style: 'currency', currency: currency }).format(parseFloat(networkFee) * rate))
setTotalAmountFiat(new Intl.NumberFormat(language, { style: 'currency', currency: currency }).format(parseFloat(total) * rate))
}
else {
setFeeAmountFiat("N/A")
setTotalAmountFiat("N/A")
}
}
}
else {
setSendEnabled(false)
setFeeAmount("0")
setTotalAmount("0")
setFeeAmountFiat(new Intl.NumberFormat(language, { style: 'currency', currency: currency }).format(0))
setTotalAmountFiat(new Intl.NumberFormat(language, { style: 'currency', currency: currency }).format(0))
}
}
const sendMax = () => {
setBtcAmount(balance.toString())
checkValidAndCalculateFiat((balance).toString())
}
const showQrCodeScanner = () => {
// @ts-ignore
props.navigation.navigate('ScanQRCode', { callBack: qrCodeCallback })
}
const sendTransaction = async () => {
let fee = feeRates[feeLevel]
let sendingMax = new BigNumber(btcAmount).eq(new BigNumber(balance))
await wallet.createTransaction(new BigNumber(btcAmount).multipliedBy(100000000).toNumber(), address, fee, sendingMax)
setRealFee(sendingMax ? 0 - wallet.lastTransaction.getFee() : wallet.lastTransaction.getFee())
if (sendingMax) {
setSendingMax(true)
}
else {
setSendingMax(false)
}
setConfirmModalShowing(true)
}
const sendTransactionForReal = async () => {
setConfirmModalShowing(false)
setisSendingNotice(true)
await wallet.broadcastLastTransaction()
let transaction = new Transaction(wallet.lastTransaction.extractTransaction().getId(), isSendingMax ? new BigNumber(btcAmount).negated().toString() : new BigNumber(totalAmount).negated().toString(), 0, new Date(), false)
dispatch(addTransaction(transaction))
setTimeout(async () => {
await wallet.synchronize(!multiDevice)
setisSendingNotice(false)
setBtcAmount('')
setFiatAmount('')
setSendEnabled(false)
setFeeLevel(1)
setAddress('')
setFeeAmount('0')
setFeeAmountFiat(new Intl.NumberFormat(language, { style: 'currency', currency: currency }).format(0))
setTotalAmount("0")
setTotalAmountFiat(new Intl.NumberFormat(language, { style: 'currency', currency: currency }).format(0))
setSentTransactionModalShowing(true)
}, 2000);
}
const changeFeeLevel = (feeLevel: number) => {
setFeeLevel(feeLevel)
setFeesAndTotalAmounts(btcAmount, feeLevel)
}
return (
<View style={{ flex: 1 }}>
{ isSendingNotice &&
<View style={{ position: 'absolute', backgroundColor: 'rgba(00, 00, 00, 0.7)', top: 0, left: 0, right: 0, bottom: 0, justifyContent: 'center', alignItems: 'center', zIndex: 50 }}>
<View style={{ justifyContent: 'center', alignItems: 'center', marginBottom: '25%' }}>
<Image style={styles.bar} source={require('../assets/images/loader.gif')} />
<Text style={styles.sendingText}>{getTranslated(language).sending}</Text>
<Text style={styles.sendingSubText}>This might take a second</Text>
</View>
</View>
}
<View style={styles.headerContainer}>
<View style={{ marginTop: insets.top, height: 56 }}>
<View style={styles.textContain}>
<TouchableOpacity disabled={Platform.OS == 'android'} onPress={() => { props.navigation.goBack() }}>
<View style={{ flexDirection: 'row', justifyContent: 'center', alignItems: 'center' }}>
{Platform.OS == 'ios' &&
<Image style={styles.arrow} source={require('../assets/images/arrow.png')} />
}
<Text style={styles.headerText}>{getTranslated(language).send}</Text>
</View>
</TouchableOpacity>
<View>
<View style={styles.btcBalanceRow}>
<Text style={styles.totalBalance}>{getTranslated(language).total_balance}:</Text>
<Text style={styles.btcBalance}>{balance.toString()}</Text>
<Text style={styles.currencyText}>BTC</Text>
</View>
<View style={styles.fiatBalanceRow}>
<Text style={styles.fiatBalance}>{btcToFiat()}</Text>
<Text style={styles.currencyText}>{currency}</Text>
</View>
</View>
</View>
</View>
</View>
<View style={styles.innerContainer}>
<View style={{ flex: 1 }}>
<Screen>
<ScrollView showsVerticalScrollIndicator={false} style={{ marginHorizontal: 20 }}>
<View style={{ flexDirection: 'row', justifyContent: 'space-between', marginTop: 16 }}>
<Text style={styles.addressText}>{getTranslated(language).send_to}</Text>
<TouchableOpacity onPress={showQrCodeScanner}>
<Image source={require('../assets/images/qrCodeIcon.png')} style={styles.qrIcon} />
</TouchableOpacity>
</View>
<Text style={styles.subHeading}>{getTranslated(language).bitcoin_address}</Text>
<View style={{ flex: 1 }}>
<TextInput value={address} onChangeText={checkAddress} keyboardType='ascii-capable' spellCheck={false} autoCorrect={false} autoCapitalize='none' style={[{ fontFamily: 'TitilliumWeb-Regular', fontSize: 14, lineHeight: 22, color: '#FFF' }, styles.textInput, { flex: 1 }, addressInvalid ? styles.textErrorBorder : styles.textNormalBorder]} />
</View>
<View style={styles.amounts}>
<View style={styles.amountContainer}>
<View style={{ flex: 1 }}>
<Text style={styles.subHeading}>{getTranslated(language).amount_to_send}</Text>
<TouchableWithoutFeedback onPress={() => { btcTextRef.current?.focus() }}>
<View style={[{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', }, styles.textInput, btcAmountInvalid ? styles.textErrorBorder : styles.textNormalBorder]}>
<TextInput ref={btcTextRef} style={styles.textInputText} placeholderTextColor="#7E858F" keyboardType="numeric" value={btcAmount} onChangeText={checkValidAndCalculateFiat} placeholder="0" />
<Text style={styles.currencyTextInput}>BTC</Text>
</View>
</TouchableWithoutFeedback>
</View>
<Image source={require('../assets/images/swap.png')} style={styles.swapIcon} />
<View style={{ flex: 1 }}>
<TouchableWithoutFeedback onPress={() => { fiatTextRef.current?.focus() }}>
<View style={[{ marginTop: 20, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }, styles.textInput, fiatAmountInvalid ? styles.textErrorBorder : styles.textNormalBorder]}>
<TextInput style={styles.textInputText} ref={fiatTextRef} placeholderTextColor="#7E858F" keyboardType="numeric" value={fiatAmount} onChangeText={checkValidAndCalculateBtc} placeholder="0" />
<Text style={styles.currencyTextInput}>{currency}</Text>
</View>
</TouchableWithoutFeedback>
</View>
</View>
</View>
<TouchableOpacity style={styles.sendMaxContainer} onPress={sendMax}>
<Text style={styles.sendMaxText}>{getTranslated(language).send_max}</Text>
</TouchableOpacity>
<Text style={[styles.subHeading, { marginTop: 10 }]}>{getTranslated(language).bitcoin_network_fee}</Text>
<View style={styles.feeLevelsContainer}>
{[0, 1, 2].map((index) => {
return (
<View key={index} style={[styles.feeLevel, index == feeLevel ? styles.orangeBorder : styles.feeNormalBorder]}>
<TouchableWithoutFeedback onPress={() => { changeFeeLevel(index) }}>
<LinearGradient useAngle={true} angle={180} angleCenter={{ x: 0.5, y: 0.5 }} colors={['#1f232e', '#13161f']}>
<View style={styles.feelabels}>
<Text style={styles.feeTitle}>{feeTitles[index]}</Text>
</View>
<Text style={styles.feeText}>{feeDescriptions[index]}</Text>
</LinearGradient>
</TouchableWithoutFeedback>
</View>
)
})
}
</View>
</ScrollView>
</Screen>
</View>
</View>
<View style={styles.totalsContainer}>
{notEnoughBalance &&
<View style={{ justifyContent: 'center', alignItems: 'center', marginBottom: 20 }}>
<Text style={styles.errorLabel} >{getTranslated(language).not_enough_balance}</Text>
</View>
}
{dustError &&
<View style={{ justifyContent: 'center', alignItems: 'center', marginBottom: 20 }}>
<Text style={styles.errorLabel} >{getTranslated(language).dust_error}</Text>
</View>
}
{cantSendAmountWithFee &&
<View style={{ justifyContent: 'center', alignItems: 'center', marginBottom: 20 }}>
<Text style={styles.errorLabel} >{getTranslated(language).not_enough}</Text>
</View>
}
{!dustError && !cantSendAmountWithFee && !notEnoughBalance &&
<View>
<View style={styles.totalsItem}>
<Text style={styles.totalsLabel} >{getTranslated(language).bitcoin_network_fee}</Text>
<Text style={styles.btcText}>{feeAmount} BTC</Text>
<Text style={styles.totalsLabel} >{feeAmountFiat + ' ' + currency}</Text>
</View>
<View style={styles.totalsItem}>
<Text style={styles.totalsLabel} >{getTranslated(language).total}</Text>
<Text style={styles.btcText}>{totalAmount} BTC</Text>
<Text style={styles.totalsLabel} >{totalAmountFiat + ' ' + currency}</Text>
</View>
<View style={{ flexDirection: 'row', marginLeft: 16, marginBottom: insets.bottom + 10 }}>
{sendEnabled &&
<ButtonPrimary text={getTranslated(language).send} action={sendTransaction} />
}
{!sendEnabled &&
<ButtonPrimaryDisabled text={getTranslated(language).send} action={() => { }} />
}
</View>
</View>
}
</View>
<ConfirmTransactionModal sendingMax={isSendingMax} btcAmount={btcAmount} recipient={address} feeAmount={realFee.toString()} sendCallback={sendTransactionForReal} isVisible={confirmModalShowing} hideModal={() => { setConfirmModalShowing(false) }} />
<Modal style={{ marginHorizontal: 0, justifyContent: 'flex-end', marginBottom: 0 }} isVisible={sentTransactionModalShowing} backdropTransitionOutTiming={0} >
{sentTransactionModalShowing &&
<View style={{ borderColor: '#2b2f3a', borderTopWidth: 1 }}>
<LinearGradient useAngle={true} angle={180} angleCenter={{ x: 0.5, y: 0.5 }} colors={['#13161F', '#090C14']}>
<View style={{ marginBottom: insets.bottom + 20, marginTop: 20, justifyContent: 'center', alignItems: 'center' }}>
<Image style={styles.icon} source={require('../assets/images/sent.png')} />
<Text style={styles.headerText}>Transaction sent</Text>
<Text style={styles.subHeading}>Your transaction has been sent.</Text>
<TouchableOpacity style={styles.button} onPress={() => { setSentTransactionModalShowing(false) }}>
<Text style={styles.buttonText}>{getTranslated(language).ok_button}</Text>
</TouchableOpacity>
</View>
</LinearGradient>
</View>
}
</Modal>
</View>
);
}
Example #29
Source File: Settings.tsx From vsinder-app with Apache License 2.0 | 4 votes |
Settings: React.FC<ProfileStackNav<"settings">> = ({
navigation,
}) => {
const [status, setStatus] = React.useState<"loading" | "">("");
const [mutate, { isLoading }] = useMutation(defaultMutationFn);
const cache = useQueryCache();
useEffect(() => {
if (status === "loading") {
Updates.fetchUpdateAsync()
.then(() => {
Updates.reloadAsync();
})
.catch((e) => {
setStatus("");
showMessage({
message: (e as Error).message,
duration: 10000,
type: "danger",
});
});
}
}, [status]);
if (isLoading || status === "loading") {
return <FullscreenLoading />;
}
return (
<ScreenWrapper noPadding>
<ScrollView alwaysBounceVertical={false}>
<Cell onPress={() => navigation.navigate("changeTheme")}>
change theme
</Cell>
<Cell
onPress={async () => {
try {
const update = await Updates.checkForUpdateAsync();
if (update.isAvailable) {
Alert.alert(
"Update Available",
"Would you like to update now?",
[
{
text: "Cancel",
style: "cancel",
},
{
text: "OK",
onPress: () => {
setStatus("loading");
},
},
],
{ cancelable: false }
);
} else {
Alert.alert(
"You're on the latest version",
"",
[
{
text: "OK",
onPress: () => {},
},
],
{ cancelable: false }
);
}
} catch (e) {
showMessage({
message: (e as Error).message,
duration: 10000,
type: "danger",
});
}
}}
>
check for update
</Cell>
<Cell
onPress={() => {
Alert.alert(
"Do you want to logout?",
"",
[
{
text: "Cancel",
onPress: () => {},
style: "cancel",
},
{
text: "OK",
onPress: () => {
setTokens("", "");
cache.setQueryData<MeResponse>("/me", { user: null });
},
},
],
{ cancelable: false }
);
}}
>
logout
</Cell>
<Cell
onPress={() => {
Alert.alert(
"Confirm delete account",
"",
[
{
text: "Cancel",
onPress: () => {},
style: "cancel",
},
{
text: "OK",
onPress: async () => {
try {
await mutate(["/account/delete", {}, "POST"]);
setTokens("", "");
cache.setQueryData<MeResponse>("/me", { user: null });
} catch {}
},
},
],
{ cancelable: false }
);
}}
>
delete account
</Cell>
<MyText
style={{ textAlign: "center", marginTop: 60, marginBottom: 40 }}
>
Version: {pkg.version}
</MyText>
</ScrollView>
</ScreenWrapper>
);
}