@expo/vector-icons#MaterialIcons TypeScript Examples

The following examples show how to use @expo/vector-icons#MaterialIcons. 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 selftrace with MIT License 6 votes vote down vote up
Icon = {
  Question: (props: Props) => <FontAwesome name="question" size={25} {...props} />,
  MapMarker: (props: Props) => <MaterialCommunityIcons name="map-marker" size={25} {...props} />,
  Earth: (props: Props) => <MaterialCommunityIcons name="earth" size={25} {...props} />,
  Lock: (props: Props) => <Foundation name="lock" size={25} {...props} />,
  Form: (props: Props) => <AntDesign name="form" size={23} {...props} />,
  Person: (props: Props) => <MaterialIcons name="person" size={25} {...props} />,
  MapMarkerMultiple: (props: Props) => (
    <MaterialCommunityIcons name="map-marker-multiple" size={23} {...props} />
  ),
}
Example #2
Source File: index.tsx    From tiktok-clone with MIT License 6 votes vote down vote up
Inbox: React.FC = () => {
  return (
    <Container>
      <Header>
        <Title>All activity</Title>
        <MaterialIcons name="arrow-drop-down" size={24} color="black" />
        <Feather
          style={{ position: 'absolute', right: 10, top: 10 }}
          name="send"
          size={24}
          color="black"
        />
      </Header>
    </Container>
  );
}
Example #3
Source File: index.tsx    From lets-fork-native with MIT License 6 votes vote down vote up
export default function Card(props: Props): React.ReactElement {
  const {
    loading, restaurant, textOpacity, leftOpacity, rightOpacity,
  } = props

  return (
    <View style={styles.container}>
      { !loading
        ? <Image style={styles.image} source={{ uri: restaurant.image_url, cache: 'force-cache' }} />
        : null}
      <View style={styles.overlay}>
        <View style={styles.header}>
          <Animated.View style={{ opacity: leftOpacity }}>
            <MaterialIcons name="favorite" color={colors.red} size={64} />
          </Animated.View>
          <Animated.View style={{ opacity: rightOpacity }}>
            <MaterialIcons name="close" color={colors.red} size={64} />
          </Animated.View>
        </View>
        <View>
          <Animated.View style={{ opacity: textOpacity }}>
            <Text style={styles.name}>{restaurant.name}</Text>
          </Animated.View>
          <View style={styles.yelp}>
            <Rating rating={restaurant.rating} size="lg" />
            <FontAwesome name="yelp" size={32} color={colors.white} />
          </View>
        </View>
      </View>
    </View>
  )
}
Example #4
Source File: HomeScreen.tsx    From lets-fork-native with MIT License 6 votes vote down vote up
HomeScreen = React.memo((props: Props): React.ReactElement => {
  const { navigation } = props

  return (
    <View style={styles.container}>
      <View style={styles.headerContainer}>
        <MaterialIcons style={styles.icon} name="restaurant" color={colors.white} size={40} />
        <Text style={styles.header}>Let&apos;s Fork</Text>
      </View>
      <View>
        <Button
          color="white"
          size="lg"
          onPress={(): void => navigation.navigate('Create')}
        >
          CREATE A PARTY
        </Button>
        <Button
          color="white"
          size="lg"
          onPress={(): void => navigation.navigate('Join')}
        >
          JOIN A PARTY
        </Button>
      </View>
    </View>
  )
})
Example #5
Source File: TutorialScreen.tsx    From lets-fork-native with MIT License 6 votes vote down vote up
TutorialScreen = React.memo((props: Props): React.ReactElement => {
  const { setShowApp } = props


  const renderItem = (item: Item): React.ReactElement => (
    <View style={styles.slide}>
      <Text style={styles.text}>{item.item.text}</Text>
    </View>
  )


  const onDone = (): void => {
    setShowApp(true)
    AsyncStorage.setItem('showApp', 'true')
  }

  return (
    <View style={styles.container}>
      <View style={styles.headerContainer}>
        <MaterialIcons style={styles.icon} name="restaurant" color={colors.white} size={26} />
        <RNText style={styles.header}>Let&apos;s Fork</RNText>
      </View>
      <AppIntroSlider
        renderItem={renderItem}
        data={slides}
        onDone={onDone}
      />
    </View>
  )
})
Example #6
Source File: contact-row.tsx    From beancount-mobile with MIT License 6 votes vote down vote up
export function ContactRow({
  onPress,
  name,
  emailOrNumber,
  selected,
}: ContactRowProps): JSX.Element {
  const theme = useTheme().colorTheme;
  const styles = getStyles(theme);
  return (
    <TouchableOpacity activeOpacity={0.9} onPress={onPress}>
      <View style={styles.rowContainer}>
        <MaterialIcons
          // eslint-disable-next-line @typescript-eslint/ban-ts-comment
          // @ts-ignore
          name={`radio-button-${selected ? "" : "un"}checked`}
          size={24}
          color={theme.primary}
        />
        <CommonMargin />
        <View style={{ flex: 1 }}>
          <Text style={styles.name}>{name || emailOrNumber}</Text>
          {name.length > 0 && (
            <Text style={styles.emailOrNum}> {emailOrNumber} </Text>
          )}
        </View>
      </View>
    </TouchableOpacity>
  );
}
Example #7
Source File: ReportUnMatchButton.tsx    From vsinder with Apache License 2.0 5 votes vote down vote up
ReportUnMatchButton: React.FC<ReportUnMatchButtonProps> = ({}) => {
  const { buttonBackground } = useTheme();
  const { showActionSheetWithOptions } = useActionSheet();
  const cache = useQueryCache();
  const navigation = useNavigation();
  const [mutate] = useMutation(defaultMutationFn, {
    onSuccess: () => {
      navigation.goBack();
      cache.setQueryData<MatchesResponse>("/matches/0", (m) => {
        return {
          matches: m?.matches.filter((x) => x.userId !== params.id) || [],
        };
      });
    },
  });
  const { params } = useRoute<MatchesStackNav<"messages">["route"]>();
  return (
    <ReportDialog
      onReportMessage={(message) => {
        mutate([
          "/report",
          { message, unmatchOrReject: "unmatch", userId: params.id },
          "POST",
        ]);
      }}
    >
      {(setOpen) => (
        <TouchableOpacity
          style={{
            flex: 1,
            flexDirection: "row",
            alignItems: "center",
            justifyContent: "center",
            paddingRight: 15,
          }}
          onPress={() => {
            const options = [
              "Report user",
              "Unmatch",
              "Report a bug",
              "Cancel",
            ];
            const destructiveButtonIndex = 0;
            const cancelButtonIndex = 3;

            showActionSheetWithOptions(
              {
                options,
                cancelButtonIndex,
                destructiveButtonIndex,
              },
              (buttonIndex) => {
                if (buttonIndex === 0) {
                  setOpen(true);
                } else if (buttonIndex === 1) {
                  mutate([`/unmatch`, { userId: params.id }, "POST"]);
                } else if (buttonIndex === 2) {
                  Linking.openURL("https://github.com/benawad/vsinder/issues");
                }
              }
            );
          }}
        >
          <MaterialIcons name="bug-report" size={27} color={buttonBackground} />
        </TouchableOpacity>
      )}
    </ReportDialog>
  );
}
Example #8
Source File: ReportUnMatchButton.tsx    From vsinder-app with Apache License 2.0 5 votes vote down vote up
ReportUnMatchButton: React.FC<ReportUnMatchButtonProps> = ({}) => {
  const { buttonBackground } = useTheme();
  const { showActionSheetWithOptions } = useActionSheet();
  const cache = useQueryCache();
  const navigation = useNavigation();
  const [mutate] = useMutation(defaultMutationFn, {
    onSuccess: () => {
      navigation.goBack();
      cache.setQueryData<MatchesResponse>("/matches/0", (m) => {
        return {
          matches: m?.matches.filter((x) => x.userId !== params.id) || [],
        };
      });
    },
  });
  const { params } = useRoute<MatchesStackNav<"messages">["route"]>();
  return (
    <ReportDialog
      onReportMessage={(message) => {
        mutate([
          "/report",
          { message, unmatchOrReject: "unmatch", userId: params.id },
          "POST",
        ]);
      }}
    >
      {(setOpen) => (
        <TouchableOpacity
          style={{
            flex: 1,
            flexDirection: "row",
            alignItems: "center",
            justifyContent: "center",
            paddingRight: 15,
          }}
          onPress={() => {
            const options = ["Report", "Unmatch", "Cancel"];
            const destructiveButtonIndex = 0;
            const cancelButtonIndex = 2;

            showActionSheetWithOptions(
              {
                options,
                cancelButtonIndex,
                destructiveButtonIndex,
              },
              (buttonIndex) => {
                if (buttonIndex === 0) {
                  setOpen(true);
                } else if (buttonIndex === 1) {
                  mutate([`/unmatch`, { userId: params.id }, "POST"]);
                }
              }
            );
          }}
        >
          <MaterialIcons name="bug-report" size={27} color={buttonBackground} />
        </TouchableOpacity>
      )}
    </ReportDialog>
  );
}
Example #9
Source File: index.tsx    From tiktok-clone with MIT License 5 votes vote down vote up
Me: React.FC = () => {
  return (
    <Container>
      <Header>
        <AntDesign
          style={{ position: 'absolute', left: 10, top: 10 }}
          name="adduser"
          size={24}
          color="black"
        />
        <Title>Matheus Castro</Title>
        <MaterialIcons name="arrow-drop-down" size={24} color="black" />
        <FontAwesome
          style={{ position: 'absolute', right: 13, top: 12 }}
          name="ellipsis-v"
          size={24}
          color="black"
        />
      </Header>
      <ScrollView>
        <Content>
          <Avatar source={avatar} />
          <Username>@matheuscastroweb</Username>
          <Stats>
            <StatsColumn>
              <StatsNumber>1950</StatsNumber>
              <StatsText>Following</StatsText>
            </StatsColumn>
            <Separator>|</Separator>
            <StatsColumn>
              <StatsNumber>650</StatsNumber>
              <StatsText>Followers</StatsText>
            </StatsColumn>
            <Separator>|</Separator>
            <StatsColumn>
              <StatsNumber>950</StatsNumber>
              <StatsText>Likes</StatsText>
            </StatsColumn>
          </Stats>
          <ProfileColumn>
            <ProfileEdit>
              <ProfileText>Edit profile</ProfileText>
            </ProfileEdit>
            <Bookmark name="bookmark" size={24} color="black" />
          </ProfileColumn>

          <StatsText>Tap to add bio</StatsText>
        </Content>
      </ScrollView>
    </Container>
  );
}
Example #10
Source File: index.tsx    From lets-fork-native with MIT License 5 votes vote down vote up
export default function Menu(props: Props): React.ReactElement {
  const { navigation } = props
  const [open, setOpen] = React.useState(false)

  return (
    <TouchableOpacity
      accessibilityRole="button"
      style={styles.button}
      onPress={(): void => setOpen(true)}
    >
      <MaterialIcons name="more-vert" color="black" size={24} />
      <Modal
        isVisible={open}
        onBackdropPress={(): void => setOpen(false)}
        animationIn="fadeIn"
        animationOut="fadeOut"
        backdropColor="transparent"
        testID="modal"
      >
        <View style={styles.modal}>
          <TouchableOpacity
            accessibilityRole="button"
            style={styles.item}
            onPress={(): void => {
              setOpen(false)
              navigation.navigate('Share')
            }}
          >
            {Platform.OS === 'ios' ? (
              <Ionicons name="ios-share-outline" color="black" size={24} />
            ) : (
              <MaterialIcons name="share" color="black" size={24} />
            )}
            <Text style={styles.text}>Share</Text>
          </TouchableOpacity>
          <TouchableOpacity
            accessibilityRole="button"
            style={styles.item}
            onPress={(): void => {
              setOpen(false)
              navigation.navigate('Match')
            }}
          >
            <MaterialIcons name="menu" color="black" size={24} />
            <Text style={styles.text}>Matches</Text>
          </TouchableOpacity>
        </View>
      </Modal>
    </TouchableOpacity>
  )
}
Example #11
Source File: index.tsx    From lets-fork-native with MIT License 5 votes vote down vote up
export default function MultiSelect(props: Props): React.ReactElement {
  const { handleSelect, items } = props
  const [open, setOpen] = React.useState(false)
  const [selected, setSelected] = React.useState<string[]>([])

  const handlePress = (id: string): void => {
    let sel = [...selected]

    if (sel.includes(id)) {
      sel = selected.filter((i) => i !== id)
    } else {
      sel = [...selected, id]
    }

    setSelected(sel)
    handleSelect(sel)
  }

  return (
    <View>
      <Modal isVisible={open} onBackdropPress={(): void => setOpen(false)} testID="modal">
        <ScrollView style={styles.scroll}>
          {
            items.map((c) => (
              <View key={c.id} style={styles.item}>
                <TouchableOpacity
                  accessibilityRole="button"
                  onPress={(): void => handlePress(c.id)}
                >
                  <Text
                    style={{
                      ...styles.itemText,
                      color: selected.includes(c.id) ? 'black' : '#808080',
                    }}
                  >
                    {c.name}
                  </Text>
                </TouchableOpacity>
                {
                  selected.includes(c.id)
                    && <Feather name="check" size={20} color="black" />
                }
              </View>
            ))
          }
        </ScrollView>
      </Modal>
      <TouchableOpacity
        accessibilityRole="button"
        style={styles.openButton}
        onPress={(): void => setOpen(true)}
      >
        <Text style={styles.openText}>
          { selected.length
            ? `${selected.length} ${selected.length === 1 ? 'category' : 'categories'} selected`
            : 'Filter by Categories (Optional)'}
        </Text>
        <MaterialIcons name="arrow-right" size={26} />
      </TouchableOpacity>
    </View>
  )
}
Example #12
Source File: index.tsx    From lets-fork-native with MIT License 5 votes vote down vote up
// Initially shown while waiting for users to join party
// But can also be accessed through Menu for additional
// users to join after party has started
export default function Share(props: Props): React.ReactElement {
  const [loading, setLoading] = React.useState(false)
  const { party, ws } = props
  const headerHeight = useHeaderHeight()
  const viewHeight = env.ADS ? height - headerHeight - 50 : height - headerHeight

  const handlePress = (): void => {
    Alert.alert(
      '',
      'No matches will be shown until another user joins your party',
      [
        {
          text: 'OK',
          onPress: (): void => {
            if (ws) {
              ws.send(JSON.stringify({ type: 'start-swiping' }))
              setLoading(true)
            }
          },
        },
      ],
      { cancelable: true },
    )
  }

  if (loading) {
    return (
      <View
        style={{
          ...styles.container,
          height: viewHeight,
        }}
      >
        <ActivityIndicator size="large" />
      </View>
    )
  }

  return (
    <View
      style={{
        ...styles.container,
        height: viewHeight,
      }}
    >
      <Text style={styles.text}>Share this code with friends to have them join your party.</Text>
      <Text style={styles.code}>{party.id}</Text>
      <QRCode
        size={200}
        // value={`http://192.168.178.76:8003/party/${party.id}`}
        value={`https://letsfork.app/party/${party.id}`}
      />
      {
        party.status === 'waiting'
        && <Button color="purple" size="lg" onPress={(): void => handlePress()}>START SWIPING</Button>
      }
      <TouchableOpacity
        accessibilityRole="button"
        onPress={(): Promise<ShareAction> => RNShare.share(
          { message: `Join my party on Let's Fork by clicking this link:\nhttps://letsfork.app/party/${party.id}\n\nor by opening the app and entering the code ${party.id}` },
        )}
      >
        {Platform.OS === 'ios' ? (
          <Ionicons name="ios-share-outline" size={32} />
        ) : (
          <MaterialIcons name="share" size={32} />
        )}
      </TouchableOpacity>
    </View>
  )
}
Example #13
Source File: MessagesScreen.tsx    From vsinder with Apache License 2.0 4 votes vote down vote up
MessagesScreen: React.FC<MatchesStackNav<"messages">> = ({
  route: { params },
  navigation,
}) => {
  const qKey = `/messages/${params.id}`;
  const {
    data,
    isLoading,
    isFetchingMore,
    fetchMore,
  } = useInfiniteQuery<IMessageResponse>(
    qKey,
    (key, cursor = "") =>
      defaultQueryFn(`${key}/${cursor}`).then((x) => ({
        hasMore: x.hasMore,
        messages: x.messages.map((m: Message) =>
          messageToGiftedMessage(m, { me: meData!.user!, them: params })
        ),
      })),
    {
      staleTime: 0,
      getFetchMore: ({ messages, hasMore }) =>
        hasMore && messages.length
          ? messages[messages.length - 1].createdAt
          : "",
    }
  );
  const { data: meData } = useQuery<MeResponse>("/me", defaultQueryFn);
  const cache = useQueryCache();
  const [mutate] = useMutation(defaultMutationFn);
  const {
    inputForeground,
    inputBackground,
    buttonBackground,
    buttonForeground,
    buttonSecondaryBackground,
    buttonSecondaryForeground,
    buttonForegroundDarker,
    buttonSecondaryForegroundDarker,
  } = useTheme();

  useOnWebSocket((e) => {
    if (e.type === "new-message" && e.message.senderId === params.id) {
      cache.setQueryData<IMessageResponse[]>(qKey, (d) => {
        return produce(d!, (x) => {
          x[0].messages = GiftedChat.append(x[0].messages, [
            messageToGiftedMessage(e.message, {
              me: meData!.user!,
              them: params,
            }),
          ]);
        });
      });
    } else if (e.type === "unmatch") {
      if (e.userId === params.id) {
        navigation.goBack();
      }
    }
  });

  useEffect(() => {
    getSocket().send(
      JSON.stringify({ type: "message-open", userId: params.id })
    );
    const d = cache.getQueryData<MatchesResponse>("/matches/0");
    if (d && d.matches.find((x) => x.userId === params.id && !x.read)) {
      cache.setQueryData<MatchesResponse>("/matches/0", {
        matches: d.matches.map((m) =>
          m.userId === params.id ? { ...m, read: true } : m
        ),
      });
    }

    return () => {
      getSocket().send(JSON.stringify({ type: "message-open", userId: null }));
    };
  }, []);

  if (isLoading) {
    return <FullscreenLoading />;
  }

  if (!meData?.user) {
    return null;
  }

  const messages = data ? data.map((x) => x.messages).flat() : [];

  return (
    <ScreenWrapper noPadding>
      <GiftedChat
        alignTop
        loadEarlier={data?.[data?.length - 1]?.hasMore}
        onPressAvatar={(u) =>
          navigation.navigate("viewCard", { id: u._id.toString() })
        }
        isLoadingEarlier={!!isFetchingMore}
        renderLoadEarlier={({ isLoadingEarlier }) =>
          isLoadingEarlier ? (
            <Loading />
          ) : (
            <MyButton onPress={() => fetchMore()}>load more</MyButton>
          )
        }
        listViewProps={{
          showsVerticalScrollIndicator: false,
        }}
        timeTextStyle={{
          left: { color: buttonSecondaryForegroundDarker },
          right: { color: buttonForegroundDarker },
        }}
        renderBubble={(props) => {
          return (
            <Bubble
              {...props}
              textStyle={{
                right: {
                  color: buttonForeground,
                },
                left: {
                  color: buttonSecondaryForeground,
                },
              }}
              wrapperStyle={{
                left: {
                  backgroundColor: buttonSecondaryBackground,
                },
                right: {
                  backgroundColor: buttonBackground,
                },
              }}
            />
          );
        }}
        renderSend={(props) => (
          <Send
            {...props}
            containerStyle={{
              justifyContent: "center",
              alignItems: "center",
              alignSelf: "center",
              marginRight: 15,
            }}
          >
            <MaterialIcons name="send" size={24} color={buttonBackground} />
          </Send>
        )}
        // @ts-ignore
        containerStyle={{
          backgroundColor: inputBackground,
        }}
        textInputStyle={{
          color: inputForeground,
          backgroundColor: inputBackground,
        }}
        // @ts-ignore
        renderTicks={() => null}
        messages={messages}
        onSend={(messages) => {
          messages.forEach((m) => {
            mutate([
              "/message",
              { recipientId: params.id, text: m.text, matchId: params.matchId },
              "POST",
            ]).then(({ message: newMessage }) => {
              cache.setQueryData<IMessageResponse[]>(qKey, (d) => {
                return produce(d!, (x) => {
                  x[0].messages = GiftedChat.append(x[0].messages, [
                    messageToGiftedMessage(newMessage, {
                      me: meData.user!,
                      them: params,
                    }),
                  ]);
                });
              });
              const d = cache.getQueryData<MatchesResponse>("/matches/0");
              if (d) {
                cache.setQueryData<MatchesResponse>("/matches/0", {
                  matches: d.matches.map((m) =>
                    m.userId === params.id
                      ? {
                          ...m,
                          message: {
                            text: newMessage.text,
                            createdAt: newMessage.createdAt,
                          },
                        }
                      : m
                  ),
                });
              }
            });
          });
        }}
        user={{
          _id: meData.user.id,
        }}
      />
    </ScreenWrapper>
  );
}
Example #14
Source File: App.tsx    From lets-fork-native with MIT License 4 votes vote down vote up
export default function App(): React.ReactElement {
  const [showApp, setShowApp] = React.useState(false)
  const [fontsLoaded] = useFonts({ VarelaRound_400Regular })
  const [loading, setLoading] = React.useState<boolean>(true)
  const [location, setLocation] = React.useState<Location.LocationObject>()
  const [party, setParty] = React.useState<Party>({} as Party)

  const linking = {
    prefixes: ['https://letsfork.app', 'letsfork://', 'exp://192.168.178.76:19000/+'],
    config: {
      screens: {
        Party: 'party/:id',
      },
    },
  }

  React.useEffect(() => {
    // Keep track of current matches
    let matches: Restaurant[] = []

    ws.onopen = (): void => {
      console.log('opened')
    }

    ws.onmessage = (msg): void => {
      console.log(msg.data)
      const data: Party = JSON.parse(msg.data)

      const newMatches = JSON.stringify(data.matches?.map((r) => r.id).sort())
      const oldMatches = JSON.stringify(matches?.map((r) => r.id).sort())

      // Alert when there are new matches
      if (data.matches?.length
        && oldMatches !== newMatches) {
        matches = data.matches
        Alert.alert(
          'You have a new match!',
          'Click the icon in the top right to view your matches',
        )
      }

      setParty(data)
    }

    ws.onclose = (msg): void => {
      console.log('closed', msg.reason)
    }

    ws.onerror = (err): void => {
      console.log('websocket error:', err)
    }
  }, [])

  const loadApplicationAsync = async (): Promise<void> => {
    const { status } = await Location.requestPermissionsAsync()
    if (status !== 'granted') {
      console.log('Permission to access location was denied')
    }

    const [loc, val] = await Promise.all([
      Location.getCurrentPositionAsync({}),
      AsyncStorage.getItem('showApp'),
    ])

    if (loc) setLocation(loc)

    // User has seen intro
    if (val) setShowApp(true)
  }

  if (loading || !fontsLoaded) {
    return (
      <AppLoading
        startAsync={loadApplicationAsync}
        onFinish={(): void => setLoading(false)}
        onError={console.warn}
      />
    )
  }

  if (!showApp) {
    return <TutorialScreen setShowApp={setShowApp} />
  }

  return (
    <NavigationContainer linking={linking} fallback={<Text>Loading...</Text>}>
      <Stack.Navigator initialRouteName="Home">
        <Stack.Screen
          name="Create"
          options={(): object => ({
            headerTitle: (): null => null,
          })}
        >
          {(props): React.ReactElement => (
            <CreateScreen
              {...props}
              ws={ws}
              location={location}
            />
          )}
        </Stack.Screen>
        <Stack.Screen
          name="Home"
          component={HomeScreen}
          options={(): object => ({
            headerShown: false,
          })}
        />
        <Stack.Screen
          name="Join"
          options={(): object => ({
            headerTitle: (): null => null,
          })}
        >
          {(props): React.ReactElement => <JoinScreen {...props} ws={ws} />}
        </Stack.Screen>
        <Stack.Screen
          name="Match"
          options={(): object => ({
            headerTitle: (): null => null,
          })}
        >
          {(props): React.ReactElement => <MatchScreen {...props} party={party} />}
        </Stack.Screen>
        <Stack.Screen
          name="Party"
          options={({ navigation }): object => ({
            gestureEnabled: false,
            headerTitle: (): null => null,
            headerLeft: (): React.ReactElement => (
              <TouchableOpacity
                style={styles.backButton}
                onPress={(): void => Alert.alert(
                  'Are you sure you want to exit?',
                  'Exiting will make you lose all data in this party',
                  [
                    { text: 'Cancel' },
                    {
                      text: 'OK',
                      onPress: (): void => {
                        ws.send(JSON.stringify({ type: 'quit' }))
                        navigation.navigate('Home')
                        setParty({} as Party)
                      },
                    },
                  ],
                  { cancelable: true },
                )}
              >
                <MaterialIcons name="close" color="black" size={24} />
              </TouchableOpacity>
            ),
            headerRight: (): React.ReactElement | null => (
              party.status === 'active'
                ? <Menu navigation={navigation} />
                : null
            ),
          })}
        >
          {(props): React.ReactElement => (
            <PartyScreen
              {...props}
              ws={ws}
              party={party}
              setParty={setParty}
            />
          )}
        </Stack.Screen>
        <Stack.Screen
          name="Restaurant"
          component={RestaurantScreen}
          options={(): object => ({
            headerTitle: (): null => null,
          })}
        />
        <Stack.Screen
          name="Share"
          options={(): object => ({
            headerTitle: (): null => null,
          })}
        >
          {(props): React.ReactElement => (
            <ShareScreen {...props} party={party} />
          )}
        </Stack.Screen>
      </Stack.Navigator>
    </NavigationContainer>
  )
}
Example #15
Source File: index.tsx    From lets-fork-native with MIT License 4 votes vote down vote up
export default function Details(props: Props): React.ReactElement {
  const headerHeight = useHeaderHeight()
  const { restaurant: defaultRestaurant, photos } = props
  const [restaurant, setRestaurant] = React.useState(defaultRestaurant)

  React.useEffect(() => {
    const fetchData = async (): Promise<void> => {
      // More details about the restaurant can be fetched from
      // the server. This can be triggered off a feature flag in the future.
      // For the time being this saves on api requests to yelp.
      if (false) { // eslint-disable-line
        try {
          const rest = await getRestaurant(defaultRestaurant.id)
          setRestaurant({
            ...rest,
            ...defaultRestaurant,
          })
        } catch (err) {
          console.log(err)
        }
      } else {
        setRestaurant(defaultRestaurant)
      }
    }

    fetchData()
  }, [defaultRestaurant])

  const imageHeight = env.ADS
    ? height - headerHeight - 50
    : height - headerHeight

  const images = [(
    <Image
      key={restaurant.image_url}
      style={{
        ...styles.image,
        height: imageHeight,
      }}
      source={{ uri: restaurant.image_url, cache: 'force-cache' }}
    />
  )]

  if (restaurant.photos?.length) {
    restaurant.photos.forEach((url) => {
      if (url !== restaurant.image_url) {
        images.push(
          <Image
            key={url}
            style={{
              ...styles.image,
              height: imageHeight,
            }}
            source={{ uri: url, cache: 'force-cache' }}
          />,
        )
      }
    })
  }

  return (
    <View
      style={{
        ...styles.container,
        minHeight: (height - headerHeight) * 0.8,
      }}
    >
      {
        photos ? (
          <ScrollView
            horizontal
            alwaysBounceHorizontal={false}
            showsHorizontalScrollIndicator
            scrollEventThrottle={10}
            pagingEnabled
            onScroll={
              Animated.event(
                [{ nativeEvent: { contentOffset: { x: new Animated.Value(0) } } }],
                { useNativeDriver: true },
              )
            }
          >
            {images}
          </ScrollView>
        ) : null
      }
      <View>
        <Text style={[styles.text, styles.name]}>{restaurant.name}</Text>
        <View style={styles.rating}>
          <Rating rating={restaurant.rating} size="sm" />
          <Text style={styles.text}>{`•   ${restaurant.review_count} reviews`}</Text>
        </View>
        <Text
          style={styles.text}
        >
          {
            restaurant.price
              ? `${restaurant.price}   •   ${restaurant?.categories?.map((c) => c.title).join(', ')}`
              : restaurant?.categories?.map((c) => c.title).join(', ')
          }
        </Text>
        { restaurant?.transactions?.length
          ? (
            <Text style={styles.text}>
              {restaurant.transactions.map((tran) => `${tran[0].toUpperCase()}${tran.replace('_', ' ').substring(1)}`).join('   •   ')}
            </Text>
          ) : null}
      </View>
      <View style={styles.section}>
        <TouchableOpacity onPress={(): void => call(restaurant.display_phone)}>
          <MaterialIcons name="phone" size={32} />
        </TouchableOpacity>
        <TouchableOpacity onPress={(): Promise<any> => Linking.openURL(restaurant.url)}>
          <FontAwesome name="yelp" size={32} color={colors.yelpRed} />
        </TouchableOpacity>
      </View>
      {
        restaurant?.coordinates?.latitude && restaurant?.coordinates?.longitude
          ? (
            <View style={styles.mapContainer}>
              <MapView
                region={{
                  latitude: restaurant.coordinates.latitude,
                  longitude: restaurant.coordinates.longitude,
                  latitudeDelta: 0.005,
                  longitudeDelta: 0.05,
                }}
                style={styles.map}
                rotateEnabled={false}
                scrollEnabled={false}
                zoomEnabled={false}
              >
                <Marker
                  coordinate={{
                    latitude: restaurant.coordinates.latitude,
                    longitude: restaurant.coordinates.longitude,
                  }}
                  title={restaurant.name}
                />
              </MapView>
            </View>
          ) : null
      }
      <TouchableOpacity
        style={styles.section}
        onPress={(): void => {
          const url = Platform.select({
            ios: `maps:0,0?q=${restaurant.coordinates.latitude},${restaurant.coordinates.longitude}`,
            android: `geo:0,0?q=${restaurant.coordinates.latitude},${restaurant.coordinates.longitude}`,
          })
          if (url) {
            Linking.openURL(url)
          }
        }}
      >
        <Text style={styles.directionText}>Get Directions</Text>
        <MaterialIcons name="directions" size={32} />
      </TouchableOpacity>
      {
        restaurant.hours
          ? <Hours hours={restaurant.hours} />
          : null
      }
    </View>
  )
}
Example #16
Source File: invite-screen.tsx    From beancount-mobile with MIT License 4 votes vote down vote up
export function InviteScreen(props: Props) {
  React.useEffect(() => {
    async function init() {
      await analytics.track("page_view_invite", {});
    }
    init();
  }, []);

  const theme = useTheme().colorTheme;
  const styles = getStyles(theme);

  const contacts = useContacts();

  const [selectedContacts, setSelectedContacts] = React.useState<RowItem[]>([]);
  const [keyword, setKeyword] = React.useState("");

  const sections = React.useMemo(() => {
    // @ts-ignore
    return Object.entries(
      groupBy(
        // Create one contact per phone number and email.
        contacts.data.reduce((res, cur) => {
          if (cur.phoneNumbers != null) {
            for (const p of cur.phoneNumbers) {
              res.push({
                id: cur.id + p.number,
                name: cur.name || "",
                phoneNumber: p.number,
              });
            }
          }
          if (cur.emails != null) {
            for (const e of cur.emails) {
              res.push({
                id: cur.id + e.email,
                name: cur.name || "",
                email: e.email,
              });
            }
          }
          return res;
        }, [] as Array<RowItem>),
        (c: RowItem) => {
          const firstChar = (c.name.charAt(0) || "#").toLowerCase();
          return firstChar.match(/[a-z]/) ? firstChar : "#";
        }
      )
    )
      .map(([key, value]: [string, RowItem[]]) => ({
        key,
        data: value.sort((a, b) =>
          (a.name || a.name || "") < (b.name || b.name || "") ? -1 : 1
        ),
      }))
      .sort((a: { key: string }, b: { key: string }) =>
        a.key < b.key ? -1 : 1
      );
  }, [contacts.data]);

  const filteredSection = React.useMemo(() => {
    if (keyword.length > 0) {
      const filteredSections = new Array<SectionItem>();
      for (const s of sections) {
        const filteredData = s.data.filter(
          (d) =>
            d.name.indexOf(keyword) >= 0 ||
            (d.email && d.email.indexOf(keyword) >= 0) ||
            (d.phoneNumber && d.phoneNumber.indexOf(keyword) >= 0)
        );
        if (filteredData.length > 0) {
          filteredSections.push({ key: s.key, data: filteredData });
        }
      }
      return filteredSections;
    }
    return sections;
  }, [sections, keyword]);

  const onInvitePress = async () => {
    const { shareLink } = props.route.params;
    let didShare = false;
    const message = `${i18n.t("recommend")} ${shareLink}`;
    const emails = selectedContacts
      .filter((c) => c.email != null)
      .map((c) => c.email) as string[];
    const phoneNumbers = selectedContacts
      .filter((c) => c.phoneNumber != null)
      .map((c) => c.phoneNumber) as string[];
    if (emails.length > 0) {
      try {
        const result = await composeAsync({
          recipients: emails,
          subject: "beancount.io",
          body: message,
          isHtml: false,
        });
        didShare = didShare || result.status === "sent";
      } catch (ex) {
        Toast.fail(ex.message);
      }
    }
    if (phoneNumbers.length > 0 && (await isAvailableAsync())) {
      try {
        const result = await sendSMSAsync(phoneNumbers, message);
        didShare = didShare || result.result === "sent";
      } catch (ex) {
        Toast.fail(ex.message);
      }
    }

    if (didShare) {
      Toast.show(i18n.t("thanksShare"));
      await analytics.track("tap_invite", { shareLink, selectedContacts });
    }
  };

  const renderBody = () => {
    if (contacts.loading) {
      return (
        <View style={styles.bodyContainer}>
          <ActivityIndicator size="large" color={theme.primary} />
          <CommonMargin />
          <Text style={styles.loadingOrErrText}>{i18n.t("loading")}</Text>
        </View>
      );
    }
    if (contacts.error != null) {
      const errMsg =
        String(contacts.error.message).indexOf("permission") >= 0
          ? i18n.t("noContactPermission")
          : String(contacts.error.message);
      return (
        <View style={styles.bodyContainer}>
          <MaterialIcons name="error" size={48} color={theme.primary} />
          <CommonMargin />
          <Text style={styles.loadingOrErrText}>{errMsg}</Text>
        </View>
      );
    }

    return (
      <View style={styles.flex1}>
        <SearchBar
          styles={{
            wrapper: {
              backgroundColor: theme.white,
            },
          }}
          style={styles.searchInput}
          placeholder={i18n.t("inputKeyword")}
          value={keyword}
          onCancel={() => {
            setKeyword("");
          }}
          onChange={(val) => {
            setKeyword(val);
          }}
        />
        <SectionList
          showsVerticalScrollIndicator={false}
          bounces={false}
          sections={filteredSection}
          renderSectionHeader={({ section }) => (
            <View style={styles.sectionHeaderContainer}>
              <Text style={styles.sectionHeaderText}>
                {section.key!.toUpperCase()}
              </Text>
            </View>
          )}
          renderItem={({ item }: { item: RowItem }) => {
            const selectedIndex = selectedContacts.findIndex(
              (i) => i.id === item.id
            );
            const onPress = () => {
              const newContacts = [...selectedContacts];
              if (selectedIndex >= 0) {
                newContacts.splice(selectedIndex, 1);
              } else {
                newContacts.push(item);
              }
              setSelectedContacts(newContacts);
            };
            return (
              <ContactRow
                name={item.name}
                emailOrNumber={(item.email || item.phoneNumber)!}
                selected={selectedIndex >= 0}
                onPress={onPress}
              />
            );
          }}
          extraData={selectedContacts}
          contentContainerStyle={styles.contentContainerStyle}
        />
        <SafeAreaView style={styles.bottomButtonContainer}>
          <Button
            style={styles.button}
            onPress={onInvitePress}
            disabled={selectedContacts.length === 0}
          >
            <Text style={styles.buttonText}>{`${i18n.t("invite")} (${
              selectedContacts.length
            })`}</Text>
          </Button>
        </SafeAreaView>
      </View>
    );
  };

  return (
    <View style={styles.container}>
      <NavigationBar
        title={i18n.t("inviteFriends")}
        showBack
        navigation={props.navigation}
      />

      {renderBody()}
    </View>
  );
}