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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
/**
 *
 * `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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
BaseLayout = styled(ScrollView)`
  flex: 1;
  background-color: #fafafa;
`
Example #9
Source File: AvatarRow.tsx    From lexicon with MIT License 5 votes vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
/**
 * 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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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>
  );
}