@expo/vector-icons#Feather JavaScript Examples

The following examples show how to use @expo/vector-icons#Feather. 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: TouchableRow.js    From expo-soundcloud-clone with MIT License 6 votes vote down vote up
TouchableRow = ({ icon, label }) => {
  return (
    <TouchableOpacity
      style={{
        flexDirection: "row",
        alignItems: "center",
        backgroundColor: Colors.white,
        paddingHorizontal: 13,
        paddingVertical: 20,
        justifyContent: "space-between",
        borderBottomColor: Colors.grey,
        borderBottomWidth: StyleSheet.hairlineWidth
      }}
    >
      <View>
        <Feather name={icon} size={20} />
      </View>
      <View style={{ marginLeft: 10, flex: 1 }}>
        <Text style={{ fontSize: 16 }}>{label}</Text>
      </View>
      <View>
        <Ionicons name="ios-arrow-forward" size={20} color={Colors.grey} />
      </View>
    </TouchableOpacity>
  );
}
Example #2
Source File: index.js    From atendimento-e-agilidade-medica-AAMed with MIT License 6 votes vote down vote up
Header = ({ flag, navigation, label }) => {
  return (
    <Container>
      {flag ? (
        <>
          <ButtonLeft onPress={() => navigation.toggleDrawer()}>
            <Ionicons name="md-menu" size={35} color="#fff" />
          </ButtonLeft>
          <ImgCenter source={require('../../assets/icon_.png')} />
          <ButtonRight onPress={() => navigation.navigate('Help')}>
            <Feather name="help-circle" size={35} color="#fff" />
          </ButtonRight>
        </>
      ) : (
        <>
          <ButtonLeft
            onPress={() => navigation.dispatch(CommonActions.goBack())}
          >
            <Ionicons name="md-arrow-back" size={30} color="#fff" />
          </ButtonLeft>
          <Label>{label}</Label>
          <ImgLeft source={require('../../assets/icon_.png')} />
        </>
      )}
    </Container>
  );
}
Example #3
Source File: Blocked.js    From InstagramClone with Apache License 2.0 6 votes vote down vote up
export default function Blocked() {
    useEffect(() => {
        BackHandler.addEventListener('hardwareBackPress', () => true)
        return () =>
            BackHandler.removeEventListener('hardwareBackPress', () => true)
    }, [])

    return (
        <View style={{ height: '100%' }}>

            <View style={{ justifyContent: 'center', alignItems: 'center', height: '90%' }}>

                <Feather name="stop-circle" size={150} color="red" style={{ position: 'absolute' }} />
                <Text style={[{ textAlign: 'center', paddingHorizontal: 40, fontSize: 20, marginTop: 400, width: '100%' }]}>Your account has been blocked</Text>

            </View>
        </View>
    )
}
Example #4
Source File: CustomDrawerContent.js    From atendimento-e-agilidade-medica-AAMed with MIT License 5 votes vote down vote up
export default function CustomDrawerContent(props) {
  const [, { logout }] = useAuth();
  const [user, setUser] = useState(null || '');

  // A função getUserLogged recupera os dados do usuário por meio de uma Promise
  // que é executada assim que o componente e montado
  useEffect(() => {
    function getUserLogged() {
      // O uso da Promise + setTmeout foi necessário pois leva um tempo até os dados do AsyncStorage sejam recuperados
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          // A função resolve() irá conter os dados do usuário após 2s definidos no setTimeout()
          resolve(AsyncStorage.getItem('store'));
        }, 2000);
      });
    }
    getUserLogged()
      .then(data => {
        // Para acessar os dados recuperados é usado o .then()
        // Como os dados armazenados estão em formato de string, é utilizado o JSON.parse() para tranforma-los em objeto
        const dataParse = JSON.parse(data);
        // Após esse processo teremos um objeto que terá dentro outro objeto "auth", nele está os dados do usuário além do token
        // Como só é necessário apenas o usuário, o estado é setado apenas com os dados do mesmo (id, nome, bio, etc.)
        setUser(dataParse.auth.user);
      })
      .catch(err => console.log(err)); // Por fim é usado um .catch() para tratar algum erro
  }, []);

  return (
    <DrawerContentScrollView {...props}>
      <View style={styles.topDrawer}>
        <View style={styles.viewAvatar}>
          <Avatar
            avatarStyle={styles.avatarStyle}
            containerStyle={styles.avatarContainerStyle}
            onPress={() => props.navigation.navigate('EditProfile')}
            activeOpacity={0.7}
            size="medium"
            rounded
            title={user ? JSON.stringify(user.name[0]) : 'a'}
          />
        </View>
        <View style={styles.viewDados}>
          <Text style={styles.nameUser}>{user.name}</Text>
          <Text style={styles.bioUser}>{user.bio}</Text>
        </View>
      </View>

      <DrawerItemList {...props} />
      <View style={styles.separator} />
      <DrawerItem
        onPress={logout}
        label="SAIR"
        style={styles.drawerItem}
        labelStyle={styles.drawerItemLabel}
        icon={() => <Feather name="log-out" size={20} color="#E53935" />}
      />
    </DrawerContentScrollView>
  );
}
Example #5
Source File: index.js    From semana-omnistack-11 with MIT License 5 votes vote down vote up
export default function Detail() {
  const navigation = useNavigation();
  const route = useRoute();

  const incident = route.params.incident;
  const message = `Olá ${incident.name}, estou entrando em contato pois gostaria de ajudar no caso "${incident.title}" com o valor de ${Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(incident.value)}`;

  function navigateBack() {
    navigation.goBack()
  }

  function sendMail() {
    MailComposer.composeAsync({
      subject: `Herói do caso: ${incident.title}`,
      recipients: [incident.email],
      body: message,
    })
  }

  function sendWhatsapp() {
    Linking.openURL(`whatsapp://send?phone=${incident.whatsapp}&text=${message}`);
  }

  return (
    <View style={styles.container}>
      <View style={styles.header}>
        <Image source={logoImg} />

        <TouchableOpacity onPress={navigateBack}>
          <Feather name="arrow-left" size={28} color="#E82041" />
        </TouchableOpacity>
      </View>

      <View style={styles.incident}>
        <Text style={[styles.incidentProperty, { marginTop: 0 }]}>ONG:</Text>
        <Text style={styles.incidentValue}>{incident.name} de {incident.city}/{incident.uf}</Text>

        <Text style={styles.incidentProperty}>CASO:</Text>
        <Text style={styles.incidentValue}>{incident.title}</Text>

        <Text style={styles.incidentProperty}>VALOR:</Text>
        <Text style={styles.incidentValue}>
          {Intl.NumberFormat('pt-BR', { 
            style: 'currency', 
            currency: 'BRL' 
          }).format(incident.value)}
        </Text>
      </View>

      <View style={styles.contactBox}>
        <Text style={styles.heroTitle}>Salve o dia!</Text>
        <Text style={styles.heroTitle}>Seja o herói desse caso.</Text>

        <Text style={styles.heroDescription}>Entre em contato:</Text>

        <View style={styles.actions}>
          <TouchableOpacity style={styles.action} onPress={sendWhatsapp}>
            <Text style={styles.actionText}>WhatsApp</Text>
          </TouchableOpacity>

          <TouchableOpacity style={styles.action} onPress={sendMail}>
            <Text style={styles.actionText}>E-mail</Text>
          </TouchableOpacity>
        </View>
      </View>
    </View>
  );
}
Example #6
Source File: index.js    From be-the-hero with MIT License 5 votes vote down vote up
export default function Details() {
  const navigation = useNavigation()
  const route = useRoute()

  const incident = route.params.incident
  const messageToSend = `Olá ${incident.name}, estou entrando em contato pois gostaria de ajudar no caso "${incident.title}" com o valor de R$ ${
    Intl.NumberFormat('pt-BR', {
      style: 'currency',
      currency: 'BRL' }
    ).format(incident.value)
  }` 

  function navigateBack() {
    navigation.goBack()
  }

  function sendEmail() {
    MailComposer.composeAsync({
      subject: `Herói do caso: ${incident.title}`,
      recipients: [ incident.email ],
      body: messageToSend
    })
  }

  function sendWhatsapp() {
    Linking.openURL(`whatsapp://send?phone=${incident.whatsapp}&text=${messageToSend}`)
  }

  return (
    <View style={ styles.detailsContainer }>

      <View style={ styles.headerContainer }>
        <Image source={ logoImage }/>

        <TouchableOpacity style={ styles.headerButton } onPress={ navigateBack }>
          <Feather name="arrow-left" size={ 28 } color="#E02041"/>
          <Text style={ styles.headerButtonText }>Voltar</Text>
        </TouchableOpacity>
      </View>

      <View style={ styles.incident }>
        <Text style={ styles.incidentOng }>
          { incident.name } de { incident.city }/{ incident.uf }
        </Text>

        <Text style={ styles.incidentDescription }>
          { incident.description }
        </Text>

        <Text style={ styles.incidentValue }>
        {
          Intl.NumberFormat('pt-BR', {
            style: 'currency',
            currency: 'BRL' }
          ).format(incident.value)
        }
        </Text>
      </View>

      <View style={ styles.contact }>
        <Text style={ styles.heroTitle }>Salve o dia!</Text>
        <Text style={ styles.heroTitle }>Seja o herói desse caso</Text>

        <Text style={ styles.heroDescription }> Entre em contato:</Text>
        <View style={ styles.contactButtons }>
          <TouchableOpacity onPress={ sendWhatsapp } style={ styles.buttonWhatsapp }>
            <FontAwesome name="whatsapp" size={ 32 } color="#E9FAEF"/>
            <Text style={[ styles.buttonText, styles.buttonTextWhatsapp ]}>Whatsapp</Text>
          </TouchableOpacity>

          <TouchableOpacity onPress={ sendEmail } style={ styles.buttonEmail }>
            <FontAwesome name="envelope-o" size={ 30 } color="#FBE8EC"/>
            <Text style={[ styles.buttonText, styles.buttonTextEmail ]}>E-mail</Text>
          </TouchableOpacity>
        </View>
      </View>
    </View>
  )
}
Example #7
Source File: index.js    From be-the-hero with MIT License 5 votes vote down vote up
export default function Incidents() {
  const [incidents, setIncidents] = useState([])
  const [total, setTotal] = useState(0)
  const [page, setPage] = useState(1)
  const [loading, setLoading] = useState(false)

  const navigation = useNavigation()

  function navigateToDetail(incident) {
    navigation.navigate('Detail', { incident })
  }

  async function loadIncidents() {
    if (loading) {
      return
    }

    if (total > 0 && incidents.length == total) {
      return
    }

    setLoading(true)

    const response = await api.get('incidents', {
      params: { page }
    })

    setIncidents([... incidents, ... response.data])
    setTotal(response.headers['x-total-count'])
    setPage(page + 1)
    setLoading(false)
  }

  useEffect(() => {
    loadIncidents()
  }, [])

  return (
    <View style={styles.container}>
      <View style={styles.header}>
        <Image source={logoImg} />
        <Text style={styles.headerText}>
          Total de <Text style={styles.headerTextBold}>{total} casos</Text>.
        </Text>
      </View>
      <Text style={styles.title}> Bem-vindo! </Text>
      <Text style={styles.description}> Escolha um dos casos abaixo e salve o dia. </Text>

      <FlatList data={incidents} style={styles.incidentList} keyExtractor={incident => String(incident.id)}  onEndReached={loadIncidents} onEndReachedThreshold={0.2} renderItem={({ item: incident }) => (
        <View style={styles.incident}>
          <Text style={styles.incidentProperty}> ONG: </Text>
          <Text style={styles.incidentValue}> {incident.name} </Text>

          <Text style={styles.incidentProperty}> CASO: </Text>
          <Text style={styles.incidentValue}> {incident.title} </Text>

          <Text style={styles.incidentProperty}> VALOR: </Text>
          <Text style={styles.incidentValue}> {Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(incident.value)} </Text>

          <TouchableOpacity style={styles.detailsButton} onPress={() => navigateToDetail(incident)}>
            <Text style={styles.detailsButtonText}> Ver mais detalhes </Text>
            <Feather name="arrow-right" size={16} color='#e02041' />
          </TouchableOpacity>
        </View>
      )} />
    </View>
  )
}
Example #8
Source File: index.js    From be-the-hero with MIT License 5 votes vote down vote up
export default function Detail() {
  const navigation = useNavigation()
  const route = useRoute()

  const incident = route.params.incident
  const message = `Olá ${incident.name}, estou  entrando em contato pois gostaria  de ajudar no caso "${incident.title}" com o valor de "${Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(incident.value)}" `

  function navigateBack() {
    navigation.goBack()
  }

  function sendMail() {
    MailComposer.composeAsync({
      subject: `Herói do caso: ${incident.title}`,
      recipients: [incident.email],
      body: message,
    })
  }

  function sendWhatsApp() {
    Linking.openURL(`whatsapp://send?phone=${incident.whatsapp}&text=${message}`)
  }

  return (
    <View style={styles.container}>
      <View style={styles.header}>
        <Image source={logoImg} />
        <TouchableOpacity onPress={navigateBack}>
          <Feather name="arrow-left" size={28} color="#e02041" />
        </TouchableOpacity>
      </View>

      <View style={styles.incident}>

        <Text style={[styles.incidentProperty, { marginTop: 0 }]}> ONG: </Text>
        <Text style={styles.incidentValue}> {incident.name} de {incident.city}/{incident.uf} </Text>

        <Text style={styles.incidentProperty}> CASO: </Text>
        <Text style={styles.incidentValue}> {incident.title} </Text>

        <Text style={styles.incidentProperty}> VALOR: </Text>
        <Text style={styles.incidentValue}> {Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(incident.value)} </Text>

      </View>

      <View style={styles.contactBox}>
        <Text style={styles.heroTitle}> Salve o dia! </Text>
        <Text style={styles.heroTitle}> Seja o herói desse caso. </Text>

        <Text style={styles.heroDescription}> Entre em contato: </Text>

        <View style={styles.actions}>
          <TouchableOpacity style={styles.action} onPress={sendWhatsApp}>
            <Text style={styles.actionText}> WhatsApp </Text>
          </TouchableOpacity>
          <TouchableOpacity style={styles.action} onPress={sendMail}>
            <Text style={styles.actionText}> Email </Text>
          </TouchableOpacity>
        </View>
      </View>
    </View>
  )
}
Example #9
Source File: CollectionsIndexHeader.js    From discovery-mobile-ui with MIT License 5 votes vote down vote up
AddCollectionInputButton = ({ onPress }) => (
  <TouchableOpacity onPress={onPress}>
    <Feather name="plus-square" size={24} color={Colors.headerIcon} />
  </TouchableOpacity>

)
Example #10
Source File: routes.js    From atendimento-e-agilidade-medica-AAMed with MIT License 5 votes vote down vote up
AuthDrawerScreen = () => (
  <AuthDrawer.Navigator
    drawerType="slide"
    drawerContentOptions={{
      labelStyle: { fontWeight: 'bold' },
      activeBackgroundColor: '#006bad',
      activeTintColor: '#fff',
      itemStyle: { padding: 7 },
      contentContainerStyle: { backgroundColor: '#fff' },
    }}
    drawerContent={props => <CustomDrawerContent {...props} />}
  >
    <AuthDrawer.Screen
      name="Home"
      component={Home}
      labelStyle={{ fontSize: 23 }}
      options={{
        drawerLabel: 'HOME',
        drawerIcon: ({ color }) => (
          <Feather name="home" size={20} color={color} />
        ),
      }}
    />
    <AuthDrawer.Screen
      name="Historic"
      component={History}
      options={{
        drawerLabel: 'HISTÓRICO',
        drawerIcon: ({ color }) => (
          <Feather name="calendar" size={20} color={color} />
        ),
      }}
    />
    <AuthDrawer.Screen
      name="EditProfile"
      component={EditProfile}
      options={{
        drawerLabel: 'EDITAR PERFIL',
        drawerIcon: ({ color }) => (
          <Feather name="edit-2" size={20} color={color} />
        ),
      }}
    />
    <AuthDrawer.Screen
      name="Help"
      component={HelpStackScreen}
      options={{
        drawerLabel: 'AJUDA',
        drawerIcon: ({ color }) => (
          <Feather name="help-circle" size={20} color={color} />
        ),
      }}
    />
    <AuthDrawer.Screen
      name="Settings"
      component={Setting}
      options={{
        drawerLabel: 'CONFIGURAÇÕES',
        drawerIcon: ({ color }) => (
          <Feather name="settings" size={20} color={color} />
        ),
      }}
    />
  </AuthDrawer.Navigator>
)
Example #11
Source File: CollectionsIndexHeader.js    From discovery-mobile-ui with MIT License 5 votes vote down vote up
SearchInputButton = ({ onPress }) => (
  <TouchableOpacity onPress={onPress}>
    <Feather name="search" size={24} color={Colors.headerIcon} />
  </TouchableOpacity>

)
Example #12
Source File: RenderBadgeItem.js    From discovery-mobile-ui with MIT License 5 votes vote down vote up
function RenderBadge({
  rtl,
  label,
  value,
  textStyle,
  badgeStyle,
  badgeTextStyle,
  badgeDotStyle,
  getBadgeColor,
  getBadgeDotColor,
  showBadgeDot,
  onPress,
  THEME,
}) {
  /**
     * onPress.
     */
  const __onPress = useCallback(() => onPress(value), [onPress, value]);

  /**
     * The badge style.
     * @returns {object}
     */
  const _badgeStyle = useMemo(() => ([
    RTL_DIRECTION(rtl, THEME.badgeStyle),
    ...[badgeStyle].flat(), {
      backgroundColor: getBadgeColor(value),
    },
  ]), [THEME, rtl, badgeStyle, getBadgeColor]);

  /**
     * The badge dot style.
     * @return {object}
     */
  const _badgeDotStyle = useMemo(() => ([
    RTL_STYLE(rtl, THEME.badgeDotStyle),
    ...[badgeDotStyle].flat(), {
      backgroundColor: getBadgeDotColor(value),
    },
  ]), [THEME, rtl, badgeDotStyle, getBadgeDotColor]);

  /**
     * The badge text style.
     * @returns {object}
     */
  const _badgeTextStyle = useMemo(() => ([
    ...[textStyle].flat(),
    ...[badgeTextStyle].flat(),
  ]), [textStyle, badgeTextStyle]);

  return (

    <TouchableOpacity style={[_badgeStyle, { backgroundColor: Colors.YELLOW3 }]} onPress={__onPress}>
      <Feather name="x" size={16} color={Colors.currentCollectionColor} />

      {/** showBadgeDot && <View style={_badgeDotStyle} /> */}
      <Text style={_badgeTextStyle}>{label}</Text>
    </TouchableOpacity>
  );
}
Example #13
Source File: Settings_Screen.js    From Reminder-App with MIT License 5 votes vote down vote up
function Settings() {
  const { state, toggle_darkmode } = useContext(Context);
  const { colors } = useTheme();

  return (
    <View style={styles.container}>
      <View style={styles.TitleContainer}>
        <Text style={[styles.text, { fontSize: 45, color: colors.text }]}>
          Settings
        </Text>
      </View>
      <View
        style={{
          marginHorizontal: 20,
        }}
      >
        <View
          style={{
            flexDirection: "row",
            justifyContent: "space-between",
            marginVertical: 15,
          }}
        >
          <Text style={[styles.text, { color: colors.text }]}>Theme</Text>
          <TouchableOpacity onPress={toggle_darkmode}>
            <Feather
              name={state.Theme ? "moon" : "sun"}
              size={40}
              color={colors.text}
            />
          </TouchableOpacity>
        </View>
      </View>
      <View style={styles.TitleContainer}>
        <Text style={[styles.text, { fontSize: 45, color: colors.text }]}>
          About
        </Text>
      </View>
      <Text
        style={[
          styles.text,
          { color: colors.text, fontSize: 16, marginHorizontal: 20 },
        ]}
      >
        Its a simple open source app created with react-native and expo
      </Text>
    </View>
  );
}
Example #14
Source File: index.js    From be-the-hero with MIT License 5 votes vote down vote up
export default function Detail() {
  const navigation = useNavigation();
  const route = useRoute();
  const { incident } = route.params;
  const message = `Olá ${incident.ong.name}, estou entrando em contato pois gostaria de ajudar no caso "${incident.title}" com o valor de ${incident.valueFormated}.`;

  function sendMail() {
    MailComposer.composeAsync({
      subject: `Herói do caso: ${incident.title}`,
      recipients: [`${incident.ong.email}`],
      body: message,
    });
  }

  function sendWhatsapp() {
    Linking.openURL(`whatsapp://send?phone=+55${incident.ong.whatsapp}&text=${message}`);
  }

  return (
    <Container>
      <Header>
        <Image source={logoImg} />
        <TouchableOpacity onPress={() => navigation.goBack()}>
          <Feather name="arrow-left" size={28} color={colors.redHero} />
        </TouchableOpacity>
      </Header>

      <Incident>
        <GroupRow>
          <View style={{ marginRight: 16, width: '50%' }}>
            <IncidentProperty style={{ marginTop: 0 }}>
              CASO:
            </IncidentProperty>
            <IncidentValue>{incident.title}</IncidentValue>
          </View>

          <View style={{ flex: 1 }} >
            <IncidentProperty style={{ marginTop: 0 }}>
              ONG:
            </IncidentProperty>
            <IncidentValue ellipsizeMode="tail" >
              {incident.ong.name} de {incident.ong.city}/{incident.ong.uf}
            </IncidentValue>
          </View>
        </GroupRow>

        <IncidentProperty>DESCRIÇÃO:</IncidentProperty>
        <IncidentValue>{incident.description}</IncidentValue>

        <IncidentProperty>VALOR:</IncidentProperty>
        <IncidentValue>{incident.valueFormated}</IncidentValue>
      </Incident>

      <ContactBox>
        <HeroTitle>Salve o dia!</HeroTitle>
        <HeroTitle>Seja o herói desse caso.</HeroTitle>

        <HeroDescription>Entre em contato:</HeroDescription>

        <Actions>
          <Action onPress={sendWhatsapp}>
            <ActionText>WhatsApp</ActionText>
          </Action>

          <Action onPress={sendMail}>
            <ActionText>E-mail</ActionText>
          </Action>
        </Actions>
      </ContactBox>
    </Container>
  );
}
Example #15
Source File: index.js    From SemanaOmnistack11 with MIT License 5 votes vote down vote up
export default function Detail() {
  const navigation = useNavigation();
  const route = useRoute();

  const incident = route.params.incident;
  const message = `Olá ${
    incident.name
  }, estou entrando em contato pois gostaria de ajudar no caso "${
    incident.title
  }" com o valor de ${Intl.NumberFormat("pt-BR", {
    style: "currency",
    currency: "BRL"
  }).format(incident.value)}`;

  function navigateBack() {
    navigation.goBack();
  }

  function sendMail() {
    MailComposer.composeAsync({
      subject: `Herói do caso: ${incident.title}`,
      recipients: [incident.email],
      body: message
    });
  }

  function sendWhatsapp() {
    Linking.openURL(
      `whatsapp://send?phone=${incident.whatsapp}&text=${message}`
    );
  }

  return (
    <View style={styles.container}>
      <View style={styles.header}>
        <Image source={logoImg} />

        <TouchableOpacity onPress={navigateBack}>
          <Feather name="arrow-left" size={28} color="#E82041" />
        </TouchableOpacity>
      </View>

      <View style={styles.incident}>
        <Text style={[styles.incidentProperty, { marginTop: 0 }]}>ONG:</Text>
        <Text style={styles.incidentValue}>
          {incident.name} de {incident.city}/{incident.uf}
        </Text>

        <Text style={styles.incidentProperty}>CASO:</Text>
        <Text style={styles.incidentValue}>{incident.title}</Text>

        <Text style={styles.incidentProperty}>VALOR:</Text>
        <Text style={styles.incidentValue}>
          {Intl.NumberFormat("pt-BR", {
            style: "currency",
            currency: "BRL"
          }).format(incident.value)}
        </Text>
      </View>

      <View style={styles.contactBox}>
        <Text style={styles.heroTitle}>Salve o dia!</Text>
        <Text style={styles.heroTitle}>Seja o herói desse caso.</Text>

        <Text style={styles.heroDescription}>Entre em contato:</Text>

        <View style={styles.actions}>
          <TouchableOpacity style={styles.action} onPress={sendWhatsapp}>
            <Text style={styles.actionText}>WhatsApp</Text>
          </TouchableOpacity>

          <TouchableOpacity style={styles.action} onPress={sendMail}>
            <Text style={styles.actionText}>E-mail</Text>
          </TouchableOpacity>
        </View>
      </View>
    </View>
  );
}
Example #16
Source File: NotesScreen.js    From discovery-mobile-ui with MIT License 4 votes vote down vote up
NotesScreen = ({
  resource,
  createRecordNoteAction,
  editRecordNoteAction,
  collection,
  createCollectionNoteAction,
  editCollectionNoteAction,
}) => {
  const navigation = useNavigation();
  const route = useRoute();
  const editingNote = route?.params?.editingNote;
  const [text, onChangeText] = useState('');
  const [editNoteId, setEditNoteId] = useState(null);
  const [showNoteInput, setShowNoteInput] = useState(false);
  const isResourceNotes = !!resource;

  const closeInput = () => {
    onChangeText('');
    setEditNoteId(null);
    setShowNoteInput(false);
  };

  const discardInputAlert = () => {
    Alert.alert(
      'Discard Edits',
      'Are you sure you want to discard edits to this note?',
      [
        {
          text: 'Cancel',
          onPress: () => {},
          style: 'cancel',
        },
        {
          text: 'Discard',
          onPress: () => closeInput(),
          style: 'destructive',
        },
      ],
    );
  };

  const handleCloseInput = ({ alert }) => {
    if (alert) {
      discardInputAlert();
    } else {
      closeInput();
    }
  };

  const handleSave = () => {
    if (isResourceNotes) {
      if (editNoteId) {
        editRecordNoteAction(resource.id, text, editNoteId);
      } else {
        createRecordNoteAction(resource.id, text);
      }
    } else if (editNoteId) {
      editCollectionNoteAction(editNoteId, text);
    } else {
      createCollectionNoteAction(text);
    }
    closeInput();
  };

  const handleCreateNote = () => {
    setShowNoteInput(true);
  };

  const handleEditNote = (noteId, noteText) => {
    setEditNoteId(noteId);
    onChangeText(noteText);
    setShowNoteInput(true);
  };

  useEffect(() => {
    if (editingNote) {
      handleEditNote(editingNote.id, editingNote.text);
    } else {
      handleCreateNote();
    }
  }, []);

  const hasTextValue = text.length > 0;
  const saveButtonTextStyle = hasTextValue ? styles.saveButtonText : styles.disabledSaveButtonText;
  const noteCount = isResourceNotes
    ? collection.records[resource.id]?.notes && Object.keys(collection.records[resource?.id]?.notes).length // eslint-disable-line max-len
    : Object.keys(collection.notes).length;

  return (
    <SafeAreaView style={styles.root}>
      <Header style={styles.header}>
        <Left>
          <TouchableOpacity onPress={() => navigation.goBack()}>
            <Entypo name="chevron-thin-left" size={20} color="black" />
          </TouchableOpacity>
        </Left>
        <View style={styles.headerTitleContainer}>
          {noteCount > 0 && <HeaderCountIcon count={noteCount} outline />}
          <Title style={styles.headerText}><Text>Notes</Text></Title>
        </View>
        <Right>
          {!showNoteInput && (
            <TouchableOpacity onPress={handleCreateNote} disabled={showNoteInput}>
              <Feather name="plus-square" size={24} color="black" />
            </TouchableOpacity>
          )}
        </Right>
      </Header>
      <ScrollView>
        {isResourceNotes && (
          <View style={styles.resourceCardContainer}>
            <ResourceCard
              resourceId={resource.id}
              resource={resource}
              handleEditNote={handleEditNote}
              editNoteId={editNoteId}
              fromNotesScreen
            />
          </View>
        )}
        {!isResourceNotes && (
          <>
            <View style={styles.collectionHeaderContainer}>
              <View style={styles.collectionLabelContainer}>
                <Text>{collection.label}</Text>
              </View>
            </View>
            <CollectionNotes
              editNoteId={editNoteId}
              handleEditNote={handleEditNote}
              fromNotesScreen
            />
          </>
        )}
      </ScrollView>
      {showNoteInput && (
      <KeyboardAvoidingView behavior="padding">
        <View style={styles.noteEditingActions}>
          <TouchableOpacity onPress={() => handleCloseInput({ alert: true })}>
            <Ionicons name="ios-close-outline" size={24} color="black" />
          </TouchableOpacity>
          <TouchableOpacity style={styles.saveButton} onPress={handleSave} disabled={!hasTextValue}>
            <BaseText variant="title" style={saveButtonTextStyle}>Save</BaseText>
          </TouchableOpacity>
        </View>
        <View style={styles.textInputContainer}>
          <TextInput
            style={styles.textInput}
            onChangeText={onChangeText}
            multiline
            value={text}
            autoFocus
          />
        </View>
      </KeyboardAvoidingView>
      )}
    </SafeAreaView>
  );
}
Example #17
Source File: TagInputView.js    From discovery-mobile-ui with MIT License 4 votes vote down vote up
function Picker({
  value = null,
  items = [],
  open,
  placeholder = null,
  closeAfterSelecting = true,
  labelProps = {},
  disabled = false,
  disabledStyle = {},
  placeholderStyle = {},
  containerStyle = {},
  style = {},
  textStyle = {},
  labelStyle = {},
  arrowIconStyle = {},
  tickIconStyle = {},
  closeIconStyle = {},
  badgeStyle = {},
  badgeTextStyle = {},
  badgeDotStyle = {},
  iconContainerStyle = {},
  searchContainerStyle = {},
  searchTextInputStyle = {},
  searchPlaceholderTextColor = Colors.GREY,
  dropDownContainerStyle = {},
  modalContentContainerStyle = {},
  arrowIconContainerStyle = {},
  closeIconContainerStyle = {},
  tickIconContainerStyle = {},
  listItemContainerStyle = {},
  listItemLabelStyle = {},
  listChildContainerStyle = {},
  listChildLabelStyle = {},
  listParentContainerStyle = {},
  listParentLabelStyle = {},
  selectedItemContainerStyle = {},
  selectedItemLabelStyle = {},
  disabledItemContainerStyle = {},
  disabledItemLabelStyle = {},
  customItemContainerStyle = {},
  customItemLabelStyle = {},
  listMessageContainerStyle = {},
  listMessageTextStyle = {},
  itemSeparatorStyle = {},
  badgeSeparatorStyle = {},
  listMode = 'FLATLIST',
  categorySelectable = true,
  searchable = false,
  searchPlaceholder = null,
  schema = {},
  translation = {},
  multiple = false,
  multipleText = null,
  mode = 'BADGE',
  itemKey = null,
  maxHeight = 200,
  renderBadgeItem = null,
  renderListItem = null,
  itemSeparator = false,
  bottomOffset = 0,
  badgeColors = BADGE_COLORS,
  badgeDotColors = BADGE_DOT_COLORS,
  showArrowIcon = true,
  showBadgeDot = true,
  showTickIcon = true,
  ArrowUpIconComponent = null,
  ArrowDownIconComponent = null,
  TickIconComponent = null,
  CloseIconComponent = null,
  ListEmptyComponent = null,
  ActivityIndicatorComponent = null,
  activityIndicatorSize = 30,
  activityIndicatorColor = Colors.GREY,
  props = {},
  modalProps = {},
  flatListProps = {},
  scrollViewProps = {},
  searchTextInputProps = {},
  loading = false,
  min = null,
  max = null,
  addCustomItem = false,
  setOpen = () => {},
  setItems = () => {},
  disableBorderRadius = true,
  containerProps = {},
  onLayout = (e) => {},
  onPress = (open) => {},
  onOpen = () => {},
  onClose = () => {},
  setValue = (callback) => {},
  onChangeValue = (value) => {},
  onChangeSearchText = (text) => {},
  zIndex = 5000,
  zIndexInverse = 6000,
  rtl = false,
  dropDownDirection = DROPDOWN_DIRECTION.DEFAULT,
  disableLocalSearch = false,
  theme = THEMES.DEFAULT,
}) {
  const [necessaryItems, setNecessaryItems] = useState([]);
  const [searchText, setSearchText] = useState('');
  const [pickerHeight, setPickerHeight] = useState(0);
  const [direction, setDirection] = useState(GET_DROPDOWN_DIRECTION(dropDownDirection));
  const badgeFlatListRef = useRef();
  const pickerRef = useRef(null);
  const initialization = useRef(false);
  const [disablePlus, setDisablePlus] = useState(false);
  const THEME = useMemo(() => THEMES[theme].default, [theme]);
  const ICON = useMemo(() => THEMES[theme].ICONS, [theme]);
  const AddCollectionInputButton = ({ onPress }) => (
    <TouchableOpacity onPress={onPress} disabled={disablePlus}>
      <Feather name="plus-square" size={24} color={disablePlus ? Colors.GREY : Colors.headerIcon} />
    </TouchableOpacity>

  );
  const handleNewCollectionPress = () => {
    const temp_items = items.slice();
    temp_items.push({ label: searchText, value: searchText });
    setItems(temp_items);
    let temp_values = [];
    if (value != null) {
      temp_values = value.slice();
    }
    temp_values.push(searchText);
    setValue(temp_values);
    _onChangeSearchText('');
  };
  useEffect(() => {
    setDisablePlus(false);
    if (searchText.length == 0) {
      setDisablePlus(true);
    }
    for (const [key, value] of Object.entries(items)) {
      if (searchText.toLowerCase() == value.value.toLowerCase()) {
        setDisablePlus(true);
      }
    }
  },

  [searchText]);
  /**
     * The item schema.
     * @returns {object}
     */
  const _schema = useMemo(() => ({ ...SCHEMA, ...schema }), [schema]);

  /**
     * componentDidMount.
     */
  useEffect(() => {
    // LogBox.ignoreLogs(['VirtualizedLists should never be nested']);

    // Get initial seleted items
    let initialSelectedItems = [];
    const valueNotNull = value !== null && (Array.isArray(value) && value.length !== 0);

    if (valueNotNull) {
      if (multiple) {
        initialSelectedItems = items.filter((item) => value.includes(item[_schema.value]));
      } else {
        initialSelectedItems = items.find((item) => item[_schema.value] === value);
      }
    }

    setNecessaryItems(initialSelectedItems);
  }, []);

  /**
     * Update necessary items.
     */
  useEffect(() => {
    setNecessaryItems((state) => [...state].map((item) => {
      const _item = items.find((x) => x[_schema.value] === item[_schema.value]);

      if (_item) {
        return { ...item, ..._item };
      }

      return item;
    }));
  }, [items]);

  /**
     * Sync necessary items.
     */
  useEffect(() => {
    if (multiple) {
      setNecessaryItems((state) => {
        if (value === null || (Array.isArray(value) && value.length === 0)) return [];

        const _state = [...state].filter((item) => value.includes(item[_schema.value]));

        const newItems = value.reduce((accumulator, currentValue) => {
          const index = _state.findIndex((item) => item[_schema.value] === currentValue);

          if (index === -1) {
            const item = items.find((item) => item[_schema.value] === currentValue);

            if (item) {
              return [...accumulator, item];
            }

            return accumulator;
          }

          return accumulator;
        }, []);

        return [..._state, ...newItems];
      });
    } else {
      const state = [];

      if (value !== null) {
        const item = items.find((item) => item[_schema.value] === value);

        if (item) {
          state.push(item);
        }
      }

      setNecessaryItems(state);
    }

    if (initialization.current) {
      onChangeValue(value);
    } else {
      initialization.current = true;
    }
  }, [value, items]);

  /**
     * dropDownDirection changed.
     */
  useEffect(() => {
    setDirection(GET_DROPDOWN_DIRECTION(dropDownDirection));
  }, [dropDownDirection]);

  /**
     * mode changed.
     */
  useEffect(() => {
    if (mode === MODE.SIMPLE) badgeFlatListRef.current = null;
  }, [mode]);

  /**
     * onPressClose.
     */
  const onPressClose = useCallback(() => {
    setOpen(false);
    setSearchText('');
    onClose();
  }, [setOpen, onClose]);

  /**
     * onPressClose.
     */
  const onPressOpen = useCallback(() => {
    setOpen(true);
    onOpen();
  }, [setOpen, onOpen]);

  /**
     * onPressToggle.
     */
  const onPressToggle = useCallback(() => {
    const isOpen = !open;

    setOpen(isOpen);
    setSearchText('');

    if (isOpen) onOpen();
    else onClose();

    return isOpen;
  }, [open, setOpen, onOpen, onClose]);

  /**
     * The sorted items.
     * @returns {object}
     */
  const sortedItems = useMemo(() => {
    const sortedItems = items.filter((item) => item[_schema.parent] === undefined || item[_schema.parent] === null);
    const children = items.filter((item) => item[_schema.parent] !== undefined && item[_schema.parent] !== null);

    children.forEach((child) => {
      const index = items.findIndex((item) => item[_schema.parent] === child[_schema.parent] || item[_schema.value] === child[_schema.parent]);

      if (index > -1) {
        sortedItems.splice(index + 1, 0, child);
      }
    });

    return sortedItems;
  }, [items, _schema]);

  /**
     * The items.
     * @returns {object}
     */
  const _items = useMemo(() => {
    if (searchText.length === 0) {
      return sortedItems;
    }
    if (disableLocalSearch) return sortedItems;

    const values = [];
    const results = sortedItems.filter((item) => {
      if (item[_schema.label].toLowerCase().includes(searchText.toLowerCase())) {
        values.push(item[_schema.value]);
        return true;
      }

      return false;
    });

    results.forEach((item, index) => {
      if (item[_schema.parent] === undefined || item[_schema.parent] === null || values.includes(item[_schema.parent])) return;

      const parent = sortedItems.find((x) => x[_schema.value] === item[_schema.parent]);
      values.push(item[_schema.parent]);

      results.splice(index, 0, parent);
    });

    if ((results.length === 0 || results.findIndex((item) => item[_schema.label].toLowerCase() === searchText.toLowerCase()) === -1) && addCustomItem) {
      results.push({
        [_schema.label]: searchText,
        [_schema.value]: searchText.replace(' ', '-'),
        custom: true,
      });
    }

    return results;
  }, [sortedItems, _schema, searchText, addCustomItem]);

  /**
     * The value.
     * @returns {string|object|null}}
     */
  const _value = useMemo(() => {
    if (multiple) {
      if (value === null) return [];

      return [...new Set(value)];
    }

    return value;
  }, [value, multiple]);

  /**
     * Selected items only for multiple items.
     * @returns {object}
     */
  const selectedItems = useMemo(() => {
    if (!multiple) return [];

    return necessaryItems.filter((item) => _value.includes(item[_schema.value]));
  }, [necessaryItems, _value, _schema, multiple]);

  /**
     * The placeholder.
     * @returns {string}
     */
  const _placeholder = useMemo(() => placeholder ?? ('PLACEHOLDER'), [placeholder, 'PLACEHOLDER']);

  /**
     * The multiple text.
     * @returns {string}
     */
  const _multipleText = useMemo(() => multipleText ?? ('SELECTED_ITEMS_COUNT_TEXT'), [multipleText, 'PLACEHOLDER']);

  /**
     * The mode.
     * @returns {string}
     */
  const _mode = useMemo(() => {
    try {
      return mode;
    } catch (e) {
      return MODE.SIMPLE;
    }
  }, [mode]);

  /**
     * Indicates whether the value is null.
     * @returns {boolean}
     */
  const isNull = useMemo(() => {
    if (_value === null || (Array.isArray(_value) && _value.length === 0)) return true;

    return necessaryItems.length === 0;
  }, [necessaryItems, _value]);

  /**
     * Get the selected item.
     * @returns {object}
     */
  const getSelectedItem = useCallback(() => {
    if (multiple) return _value;

    if (isNull) return null;

    try {
      return necessaryItems.find((item) => item[_schema.value] === _value);
    } catch (e) {
      return null;
    }
  }, [_value, necessaryItems, isNull, multiple]);

  /**
     * Get the label of the selected item.
     * @param {string|null} fallback
     * @returns {string}
     */
  const getLabel = useCallback((fallback = null) => {
    const item = getSelectedItem();

    if (multiple) {
      if (item.length > 0) return _multipleText.replace('{count}', item.length);
      return fallback;
    }

    try {
      return item[_schema.label];
    } catch (e) {
      return fallback;
    }
  }, [getSelectedItem, multiple, _multipleText, _schema]);

  /**
     * The label of the selected item / placeholder.
     */
  const _selectedItemLabel = useMemo(() => getLabel(_placeholder), [getLabel, _placeholder]);

  /**
     * The icon of the selected item.
     */
  const _selectedItemIcon = useCallback(() => {
    if (multiple) return null;

    const item = getSelectedItem();

    try {
      return item[_schema.icon] ?? null;
    } catch (e) {
      return null;
    }
  }, [getSelectedItem, multiple, _schema]);

  /**
     * onPress.
     */
  const __onPress = useCallback(async () => {
    const isOpen = !open;

    onPress(isOpen);

    if (isOpen && dropDownDirection === DROPDOWN_DIRECTION.AUTO) {
      const [, y] = await new Promise((resolve) => pickerRef.current.measureInWindow((...args) => resolve(args)));
      const size = y + maxHeight + pickerHeight + bottomOffset;

      setDirection(size < WINDOW_HEIGHT ? 'top' : 'bottom');
    }

    onPressToggle();
  }, [
    open,
    onPressToggle,
    onPress,
    pickerRef,
    maxHeight,
    pickerHeight,
    bottomOffset,
    dropDownDirection,
  ]);

  /**
     * onLayout.
     */
  const __onLayout = useCallback((e) => {
    e.persist();

    onLayout(e);

    setPickerHeight(e.nativeEvent.layout.height);
  }, [onLayout]);

  /**
     * Disable borderRadius for the picker.
     * @returns {object}
     */
  const pickerNoBorderRadius = useMemo(() => {
    if (listMode === LIST_MODE.MODAL) return null;

    if (disableBorderRadius && open) {
      return direction === 'top' ? {
        borderBottomLeftRadius: 0,
        borderBottomRightRadius: 0,
      } : {
        borderTopLeftRadius: 0,
        borderTopRightRadius: 0,
      };
    }

    return {};
  }, [disableBorderRadius, open, direction, listMode]);

  /**
     * Disable borderRadius for the drop down.
     * @returns {object}
     */
  const dropDownNoBorderRadius = useMemo(() => {
    if (listMode === LIST_MODE.MODAL) return null;

    if (disableBorderRadius && open) {
      return direction === 'top' ? {
        borderTopLeftRadius: 0,
        borderTopRightRadius: 0,
      } : {
        borderBottomLeftRadius: 0,
        borderBottomRightRadius: 0,
      };
    }
  }, [disableBorderRadius, open, direction, listMode]);

  /**
     * The disabled style.
     * @returns {object}
     */
  const _disabledStyle = useMemo(() => disabled && disabledStyle, [disabled, disabledStyle]);

  /**
     * The zIndex.
     * @returns {number}
     */
  const _zIndex = useMemo(() => {
    if (open) {
      return direction === 'top' ? zIndex : zIndexInverse;
    }

    return zIndex;
  }, [zIndex, zIndexInverse, direction, open]);

  /**
     * The style.
     * @returns {object}
     */
  const _style = useMemo(() => ([
    RTL_DIRECTION(rtl, THEME.style), {
      zIndex: _zIndex,
    },
    ...[style].flat(),
    pickerNoBorderRadius,
  ]), [rtl, style, pickerNoBorderRadius, _zIndex, THEME]);

  /**
     * The placeholder style.
     * @returns {object}
     */
  const _placeholderStyle = useMemo(() => isNull && placeholderStyle, [isNull, placeholderStyle]);

  /**
     * The style of the label.
     * @returns {object}
     */
  const _labelStyle = useMemo(() => ([
    THEME.label,
    ...[textStyle].flat(),
    ...[!isNull && labelStyle].flat(),
    ...[_placeholderStyle].flat(),
  ]), [textStyle, _placeholderStyle, labelStyle, isNull, THEME]);

  /**
     * The arrow icon style.
     * @returns {object}
     */
  const _arrowIconStyle = useMemo(() => ([
    THEME.arrowIcon,
    ...[arrowIconStyle].flat(),
  ]), [arrowIconStyle, THEME]);

  /**
     * The dropdown container style.
     * @returns {object}
     */
  const _dropDownContainerStyle = useMemo(() => ([
    THEME.dropDownContainer, {
      [direction]: pickerHeight - 1,
      maxHeight,
      zIndex: _zIndex,
    },
    ...[dropDownContainerStyle].flat(),
    dropDownNoBorderRadius,
  ]), [
    dropDownContainerStyle,
    pickerHeight,
    maxHeight,
    dropDownNoBorderRadius,
    direction,
    _zIndex,
    THEME,
  ]);

  /**
     * The modal content container style.
     * @returns {object}
     */
  const _modalContentContainerStyle = useMemo(() => ([
    THEME.modalContentContainer,
    ...[modalContentContainerStyle].flat(),
  ]), [modalContentContainerStyle, THEME]);

  /**
     * The zIndex of the container.
     * @returns {object}
     */
  const zIndexContainer = useMemo(() => Platform.OS !== 'android' && {
    zIndex: _zIndex,
  }, [_zIndex]);

  /**
     * The container style.
     * @returns {object}
     */
  const _containerStyle = useMemo(() => ([
    THEME.container,
    zIndexContainer,
    ...[containerStyle].flat(),
    ...[_disabledStyle].flat(),
  ]), [zIndexContainer, containerStyle, _disabledStyle, THEME]);

  /**
     * The arrow icon container style.
     * @returns {object}
     */
  const _arrowIconContainerStyle = useMemo(() => ([
    RTL_STYLE(rtl, THEME.arrowIconContainer),
    ...[arrowIconContainerStyle].flat(),
  ]), [rtl, arrowIconContainerStyle, THEME]);

  /**
     * The arrow component.
     * @returns {JSX.Element}
     */
  const _ArrowComponent = useMemo(() => {
    if (!showArrowIcon) return null;

    let Component;
    if (open && ArrowUpIconComponent !== null) Component = <ArrowUpIconComponent style={_arrowIconStyle} />;
    else if (!open && ArrowDownIconComponent !== null) Component = <ArrowDownIconComponent style={_arrowIconStyle} />;
    else Component = <Image source={open ? ICON.ARROW_UP : ICON.ARROW_DOWN} style={_arrowIconStyle} />;

    return (
      <View style={_arrowIconContainerStyle}>
        {Component}
      </View>
    );
  }, [
    showArrowIcon,
    open,
    ArrowUpIconComponent,
    ArrowDownIconComponent,
    _arrowIconStyle,
    _arrowIconContainerStyle,
    ICON,
  ]);

  /**
     * The icon container style.
     * @returns {object}
     */
  const _iconContainerStyle = useMemo(() => ([
    RTL_STYLE(rtl, THEME.iconContainer),
    ...[iconContainerStyle].flat(),
  ]), [rtl, iconContainerStyle, THEME]);

  /**
     * The selected item icon component.
     * @returns {JSX.Element|null}
     */
  const SelectedItemIconComponent = useMemo(() => {
    const Component = _selectedItemIcon();

    return Component !== null && (
    <View style={_iconContainerStyle}>
      <Component />
    </View>
    );
  }, [_selectedItemIcon, _iconContainerStyle]);

  /**
     * The simple body component.
     * @returns {JSX.Element}
     */
  const SimpleBodyComponent = useMemo(() => (
    <>
      {SelectedItemIconComponent}
      <Text style={_labelStyle} {...labelProps}>
        {_selectedItemLabel}
      </Text>
    </>
  ), [SelectedItemIconComponent, _labelStyle, labelProps, _selectedItemLabel]);

  /**
     * onPress badge.
     */
  const onPressBadge = useCallback((value) => {
    setValue((state) => {
      const _state = [...state];
      const index = _state.findIndex((item) => item === value);
      _state.splice(index, 1);

      return _state;
    });
  }, [setValue]);

  /**
     * The badge colors.
     * @returns {object}
     */
  const _badgeColors = useMemo(() => {
    if (typeof badgeColors === 'string') return [badgeColors];

    return badgeColors;
  }, [badgeColors]);

  /**
     * The badge dot colors.
     * @returns {object}
     */
  const _badgeDotColors = useMemo(() => {
    if (typeof badgeDotColors === 'string') return [badgeDotColors];

    return badgeDotColors;
  }, [badgeDotColors]);

  /**
     * Get badge color.
     * @param {string} str
     * @returns {string}
     */
  const getBadgeColor = useCallback((str) => {
    str = `${str}`;

    const index = Math.abs(ASCII_CODE(str)) % _badgeColors.length;
    return _badgeColors[index];
  }, [_badgeColors]);

  /**
     * Get badge dot color.
     * @param {string} str
     * @returns {string}
     */
  const getBadgeDotColor = useCallback((str) => {
    str = `${str}`;

    const index = Math.abs(ASCII_CODE(str)) % _badgeDotColors.length;
    return _badgeDotColors[index];
  }, [_badgeDotColors]);

  /**
     * The render badge component.
     * @returns {JSX.Element}
     */
  const RenderBadgeComponent = useMemo(() => (renderBadgeItem !== null ? renderBadgeItem : RenderBadgeItem), [renderBadgeItem]);

  /**
     * Render badge.
     * @returns {JSX.Element}
     */
  const __renderBadge = useCallback(({ item }) => (
    <RenderBadgeComponent
      rtl={rtl}
      label={item[_schema.label]}
      value={item[_schema.value]}
      IconComponent={item[_schema.icon] ?? null}
      textStyle={textStyle}
      badgeStyle={badgeStyle}
      badgeTextStyle={badgeTextStyle}
      badgeDotStyle={badgeDotStyle}
      getBadgeColor={getBadgeColor}
      getBadgeDotColor={getBadgeDotColor}
      showBadgeDot={showBadgeDot}
      onPress={onPressBadge}
      theme={theme}
      THEME={THEME}
    />
  ), [
    rtl,
    _schema,
    textStyle,
    badgeStyle,
    badgeTextStyle,
    badgeDotStyle,
    getBadgeColor,
    getBadgeDotColor,
    showBadgeDot,
    onPressBadge,
    theme,
    THEME,
  ]);

  /**
     * The badge key.
     * @returns {string}
     */
  const _itemKey = useMemo(() => {
    if (itemKey === null) return _schema.value;

    return itemKey;
  }, [itemKey, _schema]);

  /**
     * The key extractor.
     * @returns {string}
     */
  const keyExtractor = useCallback((item) => `${item[_itemKey]}`, [_itemKey]);

  /**
     * The badge separator style.
     * @returns {object}
     */
  const _badgeSeparatorStyle = useMemo(() => ([
    THEME.badgeSeparator,
    ...[badgeSeparatorStyle].flat(),
  ]), [badgeSeparatorStyle, THEME]);

  /**
     * The badge separator component.
     * @returns {JSX.Element}
     */
  const BadgeSeparatorComponent = useCallback(() => (
    <View style={_badgeSeparatorStyle} />
  ), [_badgeSeparatorStyle]);

  /**
     * The label container.
     * @returns {object}
     */
  const labelContainer = useMemo(() => ([
    THEME.labelContainer, rtl && {
      transform: [
        { scaleX: -1 },
      ],
    },
  ]), [rtl, THEME]);

  /**
     * Badge list empty component.
     * @returns {JSX.Element}
     */
  const BadgeListEmptyComponent = useCallback(() => (
    <View style={labelContainer}>
      <Text style={_labelStyle} {...labelProps}>
        {_placeholder}
      </Text>
    </View>
  ), [_labelStyle, labelContainer, labelProps, _placeholder]);

  /**
     * Set ref.
     */
  const setBadgeFlatListRef = useCallback((ref) => {
    badgeFlatListRef.current = ref;
  }, []);

  /**
     * The badge body component.
     * @returns {JSX.Element}
     */
  const BadgeBodyComponent = useMemo(() => (
    <FlatList
      ref={setBadgeFlatListRef}
      data={selectedItems}
      renderItem={__renderBadge}
      horizontal
      showsHorizontalScrollIndicator={false}
      keyExtractor={keyExtractor}
      ItemSeparatorComponent={BadgeSeparatorComponent}
      ListEmptyComponent={BadgeListEmptyComponent}
      style={THEME.listBody}
      contentContainerStyle={THEME.listBodyContainer}
      inverted={rtl}
    />
  ), [
    rtl,
    selectedItems,
    __renderBadge,
    keyExtractor,
    BadgeSeparatorComponent,
    BadgeListEmptyComponent,
    THEME,
  ]);

  /**
     * The body component.
     */
  const _BodyComponent = useMemo(() => {
    switch (_mode) {
      case MODE.SIMPLE: return SimpleBodyComponent;
      case MODE.BADGE: return multiple ? BadgeBodyComponent : SimpleBodyComponent;
      default: //
    }
  }, [_mode, SimpleBodyComponent, BadgeBodyComponent, multiple]);

  /**
     * The list item container style.
     * @returns {object}
     */
  const _listItemContainerStyle = useMemo(() => ([
    RTL_DIRECTION(rtl, THEME.listItemContainer),
    ...[listItemContainerStyle].flat(),
  ]), [rtl, listItemContainerStyle, THEME]);

  const _listItemContainerStyle2 = useMemo(() => ([
    RTL_DIRECTION(rtl, THEME.listItemContainer2),
    ...[listItemContainerStyle].flat(),
  ]), [rtl, listItemContainerStyle, THEME]);
    /**
     * The tick icon container style.
     * @returns {object}
     */
  const _tickIconContainerStyle = useMemo(() => ([
    RTL_STYLE(rtl, THEME.tickIconContainer),
    ...[tickIconContainerStyle].flat(),
  ]), [rtl, tickIconContainerStyle, THEME]);

  /**
     * The list item label style.
     * @returns {object}
     */
  const _listItemLabelStyle = useMemo(() => ([
    THEME.listItemLabel,
    ...[textStyle].flat(),
    ...[listItemLabelStyle].flat(),
  ]), [textStyle, listItemLabelStyle, THEME]);

  /**
     * The tick icon style.
     * @returns {object}
     */
  const _tickIconStyle = useMemo(() => ([
    THEME.tickIcon,
    ...[tickIconStyle].flat(),
  ]), [tickIconStyle, THEME]);

  /**
     * The search container style.
     * @returns {object}
     */
  const _searchContainerStyle = useMemo(() => ([
    RTL_DIRECTION(rtl, THEME.searchContainer),
    ...[searchContainerStyle].flat(), !searchable && listMode === LIST_MODE.MODAL && {
      flexDirection: 'row-reverse',
    },
  ]), [rtl, listMode, searchable, searchContainerStyle, THEME]);

  /**
     * The search text input style.
     * @returns {object}
     */
  const _searchTextInputStyle = useMemo(() => ([
    textStyle,
    THEME.searchTextInput,
    ...[searchTextInputStyle].flat(),
  ]), [textStyle, searchTextInputStyle, THEME]);

  /**
     * The close icon container style.
     * @returns {object}
     */
  const _closeIconContainerStyle = useMemo(() => ([
    RTL_STYLE(rtl, THEME.closeIconContainer),
    ...[closeIconContainerStyle].flat(),
  ]), [rtl, closeIconContainerStyle, THEME]);

  /**
     * The close icon style.
     * @returns {object}
     */
  const _closeIconStyle = useMemo(() => ([
    THEME.closeIcon,
    ...[closeIconStyle].flat(),
  ]), [closeIconStyle, THEME]);

  /**
     * The list message container style.
     * @returns {objects}
     */
  const _listMessageContainerStyle = useMemo(() => ([
    THEME.listMessageContainer,
    ...[listMessageContainerStyle].flat(),
  ]), [listMessageContainerStyle, THEME]);

  /**
     * The list message text style.
     * @returns {object}
     */
  const _listMessageTextStyle = useMemo(() => ([
    THEME.listMessageText,
    ...[listMessageTextStyle].flat(),
  ]), [listMessageTextStyle, THEME]);

  /**
     * onPress item.
     */
  const onPressItem = useCallback((item, customItem = false) => {
    if (customItem !== false) {
      setItems((state) => [...state, item]);
    }

    setValue((state) => {
      if (multiple) {
        const _state = state !== null ? [...state] : [];

        if (_state.includes(item[_schema.value])) {
          // Remove the value
          if (Number.isInteger(min) && min >= _state.length) {
            return state;
          }

          const index = _state.findIndex((x) => x === item[_schema.value]);
          _state.splice(index, 1);
        } else {
          // Add the value
          if (Number.isInteger(max) && max <= _state.length) {
            return state;
          }

          _state.push(item[_schema.value]);
        }

        return _state;
      }
      return item[_schema.value];
    });

    setNecessaryItems((state) => {
      if (multiple) {
        const _state = [...state];

        if (_state.findIndex((x) => x[_schema.value] === item[_schema.value]) > -1) {
          // Remove the item
          if (Number.isInteger(min) && min >= _state.length) {
            return state;
          }

          const index = _state.findIndex((x) => x[_schema.value] === item[_schema.value]);
          _state.splice(index, 1);

          return _state;
        }
        // Add the item
        if (Number.isInteger(max) && max <= _state.length) {
          return state;
        }

        _state.push(item);

        return _state;
      }
      return [item];
    });

    if (closeAfterSelecting && !multiple) onPressClose();
  }, [
    setValue,
    multiple,
    min,
    max,
    onPressClose,
    closeAfterSelecting,
    multiple,
    setItems,
    _schema,
  ]);

  /**
     * The tick icon component.
     * @returns {JSX.Element}
     */
  const _TickIconComponent = useCallback(() => {
    if (!showTickIcon) return null;

    let Component;
    if (TickIconComponent !== null) Component = <TickIconComponent style={_tickIconStyle} />;
    else Component = <Image source={ICON.TICK} style={_tickIconStyle} />;

    return (
      <View style={_tickIconContainerStyle}>
        {Component}
      </View>
    );
  }, [TickIconComponent, _tickIconStyle, _tickIconContainerStyle, showTickIcon, ICON]);

  /**
     * The renderItem component.
     * @returns {JSX.Element}
     */
  const RenderItemComponent = useMemo(() => (renderListItem !== null ? renderListItem : RenderListItem), [renderListItem]);

  /**
     * The selected item container style.
     * @returns {object}
     */
  const _selectedItemContainerStyle = useMemo(() => ([
    THEME.selectedItemContainer,
    selectedItemContainerStyle,
  ]), [THEME, selectedItemContainerStyle]);

  /**
     * The selected item label style.
     * @returns {object}
     */
  const _selectedItemLabelStyle = useMemo(() => ([
    THEME.selectedItemLabel,
    selectedItemLabelStyle,
  ]), [THEME, selectedItemLabelStyle]);

  /**
     * The disabled item container style.
     * @returns {object}
     */
  const _disabledItemContainerStyle = useMemo(() => ([
    THEME.disabledItemContainer,
    disabledItemContainerStyle,
  ]), [THEME, disabledItemContainerStyle]);

  /**
     * The disabled item label style.
     * @returns {object}
     */
  const _disabledItemLabelStyle = useMemo(() => ([
    THEME.disabledItemContainer,
    disabledItemLabelStyle,
  ]), [THEME, disabledItemLabelStyle]);

  /**
     * Render list item.
     * @returns {JSX.Element}
     */
  const __renderListItem = useCallback(({ item }) => {
    let IconComponent = item[_schema.icon] ?? null;

    if (IconComponent) {
      IconComponent = (
        <View style={_iconContainerStyle}>
          <IconComponent />
        </View>
      );
    }

    let isSelected;
    if (multiple) {
      isSelected = _value.includes(item[_schema.value]);
    } else {
      isSelected = _value === item[_schema.value];
    }

    return (
      <RenderItemComponent
        rtl={rtl}
        item={item}
        label={item[_schema.label]}
        value={item[_schema.value]}
        parent={item[_schema.parent] ?? null}
        selectable={item[_schema.selectable] ?? null}
        disabled={item[_schema.disabled] ?? false}
        custom={item.custom ?? false}
        isSelected={isSelected}
        IconComponent={IconComponent}
        TickIconComponent={_TickIconComponent}
        listItemContainerStyle={_listItemContainerStyle}
        listItemContainerStyle2={_listItemContainerStyle2}
        listItemLabelStyle={_listItemLabelStyle}
        listChildContainerStyle={listChildContainerStyle}
        listChildLabelStyle={listChildLabelStyle}
        listParentContainerStyle={listParentContainerStyle}
        listParentLabelStyle={listParentLabelStyle}
        customItemContainerStyle={customItemContainerStyle}
        customItemLabelStyle={customItemLabelStyle}
        selectedItemContainerStyle={_selectedItemContainerStyle}
        selectedItemLabelStyle={_selectedItemLabelStyle}
        disabledItemContainerStyle={_disabledItemContainerStyle}
        disabledItemLabelStyle={_disabledItemLabelStyle}
        categorySelectable={categorySelectable}
        onPress={onPressItem}
        theme={theme}
        THEME={THEME}
      />
    );
  }, [
    rtl,
    RenderItemComponent,
    _listItemLabelStyle,
    _iconContainerStyle,
    listChildContainerStyle,
    listChildLabelStyle,
    listParentContainerStyle,
    listParentLabelStyle,
    _listItemContainerStyle,
    _listItemLabelStyle,
    customItemContainerStyle,
    customItemLabelStyle,
    _selectedItemContainerStyle,
    _selectedItemLabelStyle,
    _disabledItemContainerStyle,
    _disabledItemLabelStyle,
    _TickIconComponent,
    _schema,
    _value,
    multiple,
    categorySelectable,
    onPressItem,
    theme,
    THEME,
  ]);

  /**
     * The item separator.
     * @returns {JSX.Element|null}
     */
  const ItemSeparatorComponent = useCallback(() => {
    if (!itemSeparator) return null;

    return (
      <View style={[
        THEME.itemSeparator,
        ...[itemSeparatorStyle].flat(),
      ]}
      />
    );
  }, [itemSeparator, itemSeparatorStyle, THEME]);

  /**
     * The search placeholder.
     * @returns {string}
     */
  const _searchPlaceholder = useMemo(() => {
    if (searchPlaceholder !== null) return searchPlaceholder;

    return ('search for or create new tags');
  }, [searchPlaceholder, 'add tags']);

  /**
     * onChangeSearchText.
     * @param {string} text
     */
  const _onChangeSearchText = useCallback((text) => {
    setSearchText(text);
    onChangeSearchText(text);
  }, [onChangeSearchText]);

  /**
     * The close icon component.
     * @returns {JSX.Element}
     */
  const _CloseIconComponent = useMemo(() => {
    if (listMode !== LIST_MODE.MODAL) return null;

    let Component;

    if (CloseIconComponent !== null) Component = <CloseIconComponent style={_closeIconStyle} />;
    else Component = <Image source={ICON.CLOSE} style={_closeIconStyle} />;

    return (
      <TouchableOpacity style={_closeIconContainerStyle} onPress={onPressClose}>
        {Component}
      </TouchableOpacity>
    );
  }, [listMode, CloseIconComponent, _closeIconStyle, _closeIconContainerStyle, onPressClose, ICON]);

  /**
     * Indicates if the search component is visible.
     * @returns {boolean}
     */
  const isSearchComponentVisible = useMemo(() => {
    if (listMode === LIST_MODE.MODAL) return true;

    return searchable;
  }, [listMode, searchable]);

  /**
     * The search component.
     * @returns {JSX.Element}
     */
  const SearchComponent = useMemo(() => isSearchComponentVisible && (
    <View style={_searchContainerStyle}>
      {
                searchable && (
                <TextInput
                  value={searchText}
                  onChangeText={_onChangeSearchText}
                  style={_searchTextInputStyle}
                  placeholder={_searchPlaceholder}
                  placeholderTextColor={searchPlaceholderTextColor}
                  {...searchTextInputProps}
                />
                )
            }
      {_CloseIconComponent}
      <AddCollectionInputButton onPress={handleNewCollectionPress} />

    </View>
  ), [
    searchable,
    isSearchComponentVisible,
    _onChangeSearchText,
    _searchContainerStyle,
    _searchTextInputStyle,
    _searchPlaceholder,
    searchPlaceholderTextColor,
    searchText,
    searchTextInputProps,
  ]);

  /**
     * The dropdown component wrapper.
     * @returns {JSX.Element}
     */
  const DropDownComponentWrapper = useCallback((Component) => (
    <View style={_dropDownContainerStyle}>
      {SearchComponent}
      {Component}
    </View>
  ), [_dropDownContainerStyle, SearchComponent]);

  /**
     * The ActivityIndicatorComponent.
     * @returns {JSX.Element}
     */
  const _ActivityIndicatorComponent = useCallback(() => {
    let Component;

    if (ActivityIndicatorComponent !== null) Component = ActivityIndicatorComponent;
    else Component = ActivityIndicator;

    return <Component size={activityIndicatorSize} color={activityIndicatorColor} />;
  }, [ActivityIndicatorComponent, activityIndicatorSize, activityIndicatorColor]);

  /**
     * The ListEmptyComponent.
     * @returns {JSX.Element}
     */
  const _ListEmptyComponent = useCallback(() => {
    let Component;
    const message = ('');

    if (ListEmptyComponent !== null) Component = ListEmptyComponent;
    else Component = ListEmpty;

    return (
      <Component
        listMessageContainerStyle={_listMessageContainerStyle}
        listMessageTextStyle={_listMessageTextStyle}
        ActivityIndicatorComponent={_ActivityIndicatorComponent}
        loading={loading}
        message={message}
      />
    );
  }, [
    'message',
    _listMessageContainerStyle,
    _listMessageTextStyle,
    ListEmptyComponent,
    _ActivityIndicatorComponent,
    loading,
  ]);

  /**
     * The dropdown flatlist component.
     * @returns {JSX.Element}
     */
  const DropDownFlatListComponent = useMemo(() => (
    <FlatList
      style={styles.flex}
      contentContainerStyle={THEME.flatListContentContainer}
      ListEmptyComponent={_ListEmptyComponent}
      data={_items}
      renderItem={__renderListItem}
      keyExtractor={keyExtractor}
      extraData={_value}
      ItemSeparatorComponent={ItemSeparatorComponent}
      {...flatListProps}
    />
  ), [
    _items,
    _value,
    __renderListItem,
    keyExtractor,
    ItemSeparatorComponent,
    flatListProps,
    _ListEmptyComponent,
    THEME,
  ]);

  /**
     * The dropdown scrollview component.
     * @returns {JSX.Element}
     */
  const DropDownScrollViewComponent = useMemo(() => (
    <ScrollView nestedScrollEnabled {...scrollViewProps}>
      {_items.map((item, index) => (
        <Fragment key={item[_itemKey]}>
          {index > 0 && ItemSeparatorComponent()}
          {__renderListItem({ item })}
        </Fragment>
      ))}
      {_items.length === 0 && _ListEmptyComponent()}
    </ScrollView>
  ), [__renderListItem, _itemKey, scrollViewProps, _ListEmptyComponent]);

  /**
     * The dropdown modal component.
     * @returns {JSX.Element}
     */
  const DropDownModalComponent = useMemo(() => (
    <Modal visible={open} presentationStyle="fullScreen" {...modalProps}>
      <SafeAreaView style={[_modalContentContainerStyle]}>
        {SearchComponent}
        {DropDownFlatListComponent}
      </SafeAreaView>
    </Modal>
  ), [open, SearchComponent, _modalContentContainerStyle, modalProps]);

  /**
     * The dropdown component.
     * @returns {JSX.Element}
     */
  const DropDownComponent = useMemo(() => {
    switch (listMode) {
      case LIST_MODE.FLATLIST: return DropDownComponentWrapper(DropDownFlatListComponent);
      case LIST_MODE.SCROLLVIEW: return DropDownComponentWrapper(DropDownScrollViewComponent);
      case LIST_MODE.MODAL: return DropDownModalComponent;
      default: //
    }
  }, [
    listMode,
    DropDownFlatListComponent,
    DropDownScrollViewComponent,
    DropDownModalComponent,
    DropDownComponentWrapper,
  ]);

  /**
     * The body of the dropdown component.
     * @returns {JSX.Element}
     */
  const DropDownBodyComponent = useMemo(() => {
    if (open || listMode === LIST_MODE.MODAL) return DropDownComponent;
    return null;
  }, [open, listMode, DropDownComponent]);

  /**
     * onRef.
     */
  const onRef = useCallback((ref) => {
    pickerRef.current = ref;
  }, []);

  /**
     * Pointer events.
     * @returns {string}
     */
  const pointerEvents = useMemo(() => (disabled ? 'none' : 'auto'), [disabled]);

  return (
    <View style={_containerStyle} {...containerProps}>
      <TouchableOpacity style={_style} onPress={__onPress} onLayout={__onLayout} {...props} ref={onRef} pointerEvents={pointerEvents} disabled={disabled}>
        {_BodyComponent}
        {_ArrowComponent}
      </TouchableOpacity>
      {DropDownBodyComponent}
    </View>
  );
}
Example #18
Source File: TagSearchView.js    From discovery-mobile-ui with MIT License 4 votes vote down vote up
function Picker({
  value = null,
  items = [],
  open,
  placeholder = null,
  closeAfterSelecting = true,
  labelProps = {},
  disabled = false,
  disabledStyle = {},
  placeholderStyle = {},
  containerStyle = {},
  style = {},
  textStyle = {},
  labelStyle = {},
  arrowIconStyle = {},
  tickIconStyle = {},
  closeIconStyle = {},
  badgeStyle = {},
  badgeTextStyle = {},
  badgeDotStyle = {},
  iconContainerStyle = {},
  searchContainerStyle = {},
  searchTextInputStyle = {},
  searchPlaceholderTextColor = Colors.GREY,
  dropDownContainerStyle = {},
  modalContentContainerStyle = {},
  arrowIconContainerStyle = {},
  closeIconContainerStyle = {},
  tickIconContainerStyle = {},
  listItemContainerStyle = {},
  listItemLabelStyle = {},
  listChildContainerStyle = {},
  listChildLabelStyle = {},
  listParentContainerStyle = {},
  listParentLabelStyle = {},
  selectedItemContainerStyle = {},
  selectedItemLabelStyle = {},
  disabledItemContainerStyle = {},
  disabledItemLabelStyle = {},
  customItemContainerStyle = {},
  customItemLabelStyle = {},
  listMessageContainerStyle = {},
  listMessageTextStyle = {},
  itemSeparatorStyle = {},
  badgeSeparatorStyle = {},
  listMode = 'FLATLIST',
  categorySelectable = true,
  searchable = false,
  searchPlaceholder = null,
  translation = {},
  multiple = false,
  multipleText = null,
  mode = 'BADGE',
  maxHeight = 200,
  renderBadgeItem = null,
  renderListItem = null,
  itemSeparator = false,
  bottomOffset = 0,
  badgeColors = BADGE_COLORS,
  badgeDotColors = BADGE_DOT_COLORS,
  showArrowIcon = true,
  showBadgeDot = true,
  showTickIcon = true,
  ArrowUpIconComponent = null,
  ArrowDownIconComponent = null,
  TickIconComponent = null,
  CloseIconComponent = null,
  ListEmptyComponent = null,
  ActivityIndicatorComponent = null,
  activityIndicatorSize = 30,
  activityIndicatorColor = Colors.GREY,
  props = {},
  modalProps = {},
  flatListProps = {},
  scrollViewProps = {},
  searchTextInputProps = {},
  loading = false,
  min = null,
  max = null,
  addCustomItem = false,
  setOpen = () => {},
  setItems = () => {},
  disableBorderRadius = true,
  containerProps = {},
  onLayout = (e) => {},
  onPress = (open) => {},
  onOpen = () => {},
  onClose = () => {},
  setValue = (callback) => {},
  onChangeValue = (value) => {},
  onChangeSearchText = (text) => {},
  zIndex = 5000,
  zIndexInverse = 6000,
  rtl = false,
  dropDownDirection = DROPDOWN_DIRECTION.DEFAULT,
  disableLocalSearch = false,
  theme = THEMES.DEFAULT,
}) {
  const [necessaryItems, setNecessaryItems] = useState([]);
  const [searchText, setSearchText] = useState('');
  const [pickerHeight, setPickerHeight] = useState(0);
  const [direction, setDirection] = useState(GET_DROPDOWN_DIRECTION(dropDownDirection));
  const badgeFlatListRef = useRef();
  const pickerRef = useRef(null);
  const initialization = useRef(false);

  const THEME = useMemo(() => THEMES[theme].default, [theme]);
  const ICON = useMemo(() => THEMES[theme].ICONS, [theme]);
  const AddCollectionInputButton = ({ onPress }) => (
    <TouchableOpacity onPress={onPress}>
      <Feather name="plus-square" size={24} color={Colors.headerIcon} />
    </TouchableOpacity>

  );
  const handleNewCollectionPress = () => {
    const temp_items = items.slice();
    temp_items.push({ label: searchText, value: searchText });
    setItems(temp_items);
    let temp_values = [];
    if (value != null) {
      temp_values = value.slice();
    }
    temp_values.push(searchText);
    setValue(temp_values);
  };

  useEffect(() => {
  }, [items]);
  /**
     * The item schema.
     * @returns {object}
     */
  const schema = useMemo(() => ({ ...SCHEMA, ...{} }), [{}]);

  /**
     * componentDidMount.
     */
  useEffect(() => {
    // LogBox.ignoreLogs(['VirtualizedLists should never be nested']);

    // Get initial seleted items
    let initialSelectedItems = [];
    const valueNotNull = value !== null && (Array.isArray(value) && value.length !== 0);

    if (valueNotNull) {
      if (multiple) {
        initialSelectedItems = items.filter((item) => value.includes(item[schema.value]));
      } else {
        initialSelectedItems = items.find((item) => item[schema.value] === value);
      }
    }

    setNecessaryItems(initialSelectedItems);
  }, []);

  /**
     * Update necessary items.
     */
  useEffect(() => {
    setNecessaryItems((state) => [...state].map((item) => {
      const _item = items.find((x) => x[schema.value] === item[schema.value]);

      if (_item) {
        return { ...item, ..._item };
      }

      return item;
    }));
  }, [items]);

  /**
     * Sync necessary items.
     */
  useEffect(() => {
    if (multiple) {
      setNecessaryItems((state) => {
        if (value === null || (Array.isArray(value) && value.length === 0)) return [];

        const _state = [...state].filter((item) => value.includes(item[schema.value]));

        const newItems = value.reduce((accumulator, currentValue) => {
          const index = _state.findIndex((item) => item[schema.value] === currentValue);

          if (index === -1) {
            const item = items.find((item) => item[schema.value] === currentValue);

            if (item) {
              return [...accumulator, item];
            }

            return accumulator;
          }

          return accumulator;
        }, []);

        return [..._state, ...newItems];
      });
    } else {
      const state = [];

      if (value !== null) {
        const item = items.find((item) => item[schema.value] === value);

        if (item) {
          state.push(item);
        }
      }

      setNecessaryItems(state);
    }

    if (initialization.current) {
      onChangeValue(value);
    } else {
      initialization.current = true;
    }
  }, [value, items]);

  /**
     * dropDownDirection changed.
     */
  useEffect(() => {
    setDirection(GET_DROPDOWN_DIRECTION(dropDownDirection));
  }, [dropDownDirection]);

  /**
     * mode changed.
     */
  useEffect(() => {
    if (mode === MODE.SIMPLE) badgeFlatListRef.current = null;
  }, [mode]);

  /**
     * onPressClose.
     */
  const onPressClose = useCallback(() => {
    setOpen(false);
    setSearchText('');
    onClose();
  }, [setOpen, onClose]);

  /**
     * onPressClose.
     */
  const onPressOpen = useCallback(() => {
    setOpen(true);
    onOpen();
  }, [setOpen, onOpen]);

  /**
     * onPressToggle.
     */
  const onPressToggle = useCallback(() => {
    const isOpen = !open;

    setOpen(isOpen);
    setSearchText('');

    if (isOpen) onOpen();
    else onClose();

    return isOpen;
  }, [open, setOpen, onOpen, onClose]);

  /**
     * The sorted items.
     * @returns {object}
     */
  const sortedItems = useMemo(() => {
    const sortedItems = items.filter((item) => item[schema.parent] === undefined || item[schema.parent] === null);
    const children = items.filter((item) => item[schema.parent] !== undefined && item[schema.parent] !== null);

    children.forEach((child) => {
      const index = items.findIndex((item) => item[schema.parent] === child[schema.parent] || item[schema.value] === child[schema.parent]);

      if (index > -1) {
        sortedItems.splice(index + 1, 0, child);
      }
    });

    return sortedItems;
  }, [items, schema]);

  /**
     * The items.
     * @returns {object}
     */
  const _items = useMemo(() => {
    if (searchText.length === 0) {
      return sortedItems;
    }
    if (disableLocalSearch) return sortedItems;

    const values = [];
    const results = sortedItems.filter((item) => {
      if (item[schema.label].toLowerCase().includes(searchText.toLowerCase())) {
        values.push(item[schema.value]);
        return true;
      }

      return false;
    });

    results.forEach((item, index) => {
      if (item[schema.parent] === undefined || item[schema.parent] === null || values.includes(item[schema.parent])) return;

      const parent = sortedItems.find((x) => x[schema.value] === item[schema.parent]);
      values.push(item[schema.parent]);

      results.splice(index, 0, parent);
    });

    if ((results.length === 0 || results.findIndex((item) => item[schema.label].toLowerCase() === searchText.toLowerCase()) === -1) && addCustomItem) {
      results.push({
        [schema.label]: searchText,
        [schema.value]: searchText.replace(' ', '-'),
        custom: true,
      });
    }

    return results;
  }, [sortedItems, schema, searchText, addCustomItem]);

  /**
     * The value.
     * @returns {string|object|null}}
     */
  const _value = useMemo(() => {
    if (multiple) {
      if (value === null) return [];

      return [...new Set(value)];
    }

    return value;
  }, [value, multiple]);

  /**
     * Selected items only for multiple items.
     * @returns {object}
     */
  const selectedItems = useMemo(() => {
    if (!multiple) return [];

    return necessaryItems.filter((item) => _value.includes(item[schema.value]));
  }, [necessaryItems, _value, schema, multiple]);

  /**
     * The placeholder.
     * @returns {string}
     */
  const _placeholder = useMemo(() => placeholder ?? ('PLACEHOLDER'), [placeholder, 'PLACEHOLDER']);

  /**
     * The multiple text.
     * @returns {string}
     */
  const _multipleText = useMemo(() => multipleText ?? ('SELECTED_ITEMS_COUNT_TEXT'), [multipleText, 'PLACEHOLDER']);

  /**
     * The mode.
     * @returns {string}
     */
  const _mode = useMemo(() => {
    try {
      return mode;
    } catch (e) {
      return MODE.SIMPLE;
    }
  }, [mode]);

  /**
     * Indicates whether the value is null.
     * @returns {boolean}
     */
  const isNull = useMemo(() => {
    if (_value === null || (Array.isArray(_value) && _value.length === 0)) return true;

    return necessaryItems.length === 0;
  }, [necessaryItems, _value]);

  /**
     * Get the selected item.
     * @returns {object}
     */
  const getSelectedItem = useCallback(() => {
    if (multiple) return _value;

    if (isNull) return null;

    try {
      return necessaryItems.find((item) => item[schema.value] === _value);
    } catch (e) {
      return null;
    }
  }, [_value, necessaryItems, isNull, multiple]);

  /**
     * Get the label of the selected item.
     * @param {string|null} fallback
     * @returns {string}
     */
  const getLabel = useCallback((fallback = null) => {
    const item = getSelectedItem();

    if (multiple) {
      if (item.length > 0) return _multipleText.replace('{count}', item.length);
      return fallback;
    }

    try {
      return item[schema.label];
    } catch (e) {
      return fallback;
    }
  }, [getSelectedItem, multiple, _multipleText, schema]);

  /**
     * The label of the selected item / placeholder.
     */
  const _selectedItemLabel = useMemo(() => getLabel(_placeholder), [getLabel, _placeholder]);

  /**
     * The icon of the selected item.
     */
  const _selectedItemIcon = useCallback(() => {
    if (multiple) return null;

    const item = getSelectedItem();

    try {
      return item[schema.icon] ?? null;
    } catch (e) {
      return null;
    }
  }, [getSelectedItem, multiple, schema]);

  /**
     * onPress.
     */
  const __onPress = useCallback(async () => {
    const isOpen = !open;

    onPress(isOpen);

    if (isOpen && dropDownDirection === DROPDOWN_DIRECTION.AUTO) {
      const [, y] = await new Promise((resolve) => pickerRef.current.measureInWindow((...args) => resolve(args)));
      const size = y + maxHeight + pickerHeight + bottomOffset;

      setDirection(size < WINDOW_HEIGHT ? 'top' : 'bottom');
    }

    onPressToggle();
  }, [
    open,
    onPressToggle,
    onPress,
    pickerRef,
    maxHeight,
    pickerHeight,
    bottomOffset,
    dropDownDirection,
  ]);

  /**
     * onLayout.
     */
  const __onLayout = useCallback((e) => {
    e.persist();

    onLayout(e);

    setPickerHeight(e.nativeEvent.layout.height);
  }, [onLayout]);

  /**
     * Disable borderRadius for the picker.
     * @returns {object}
     */
  const pickerNoBorderRadius = useMemo(() => {
    if (listMode === LIST_MODE.MODAL) return null;

    if (disableBorderRadius && open) {
      return direction === 'top' ? {
        borderBottomLeftRadius: 0,
        borderBottomRightRadius: 0,
      } : {
        borderTopLeftRadius: 0,
        borderTopRightRadius: 0,
      };
    }

    return {};
  }, [disableBorderRadius, open, direction, listMode]);

  /**
     * Disable borderRadius for the drop down.
     * @returns {object}
     */
  const dropDownNoBorderRadius = useMemo(() => {
    if (listMode === LIST_MODE.MODAL) return null;

    if (disableBorderRadius && open) {
      return direction === 'top' ? {
        borderTopLeftRadius: 0,
        borderTopRightRadius: 0,
      } : {
        borderBottomLeftRadius: 0,
        borderBottomRightRadius: 0,
      };
    }
  }, [disableBorderRadius, open, direction, listMode]);

  /**
     * The disabled style.
     * @returns {object}
     */
  const _disabledStyle = useMemo(() => disabled && disabledStyle, [disabled, disabledStyle]);

  /**
     * The zIndex.
     * @returns {number}
     */
  const _zIndex = useMemo(() => {
    if (open) {
      return direction === 'top' ? zIndex : zIndexInverse;
    }

    return zIndex;
  }, [zIndex, zIndexInverse, direction, open]);

  /**
     * The style.
     * @returns {object}
     */
  const _style = useMemo(() => ([
    RTL_DIRECTION(rtl, THEME.style), {
      zIndex: _zIndex,
    },
    ...[style].flat(),
    pickerNoBorderRadius,
  ]), [rtl, style, pickerNoBorderRadius, _zIndex, THEME]);

  /**
     * The placeholder style.
     * @returns {object}
     */
  const _placeholderStyle = useMemo(() => isNull && placeholderStyle, [isNull, placeholderStyle]);

  /**
     * The style of the label.
     * @returns {object}
     */
  const _labelStyle = useMemo(() => ([
    THEME.label,
    ...[textStyle].flat(),
    ...[!isNull && labelStyle].flat(),
    ...[_placeholderStyle].flat(),
  ]), [textStyle, _placeholderStyle, labelStyle, isNull, THEME]);

  /**
     * The arrow icon style.
     * @returns {object}
     */
  const _arrowIconStyle = useMemo(() => ([
    THEME.arrowIcon,
    ...[arrowIconStyle].flat(),
  ]), [arrowIconStyle, THEME]);

  /**
     * The dropdown container style.
     * @returns {object}
     */
  const _dropDownContainerStyle = useMemo(() => ([
    THEME.dropDownContainer, {
      [direction]: pickerHeight - 1,
      maxHeight,
      zIndex: _zIndex,
    },
    ...[dropDownContainerStyle].flat(),
    dropDownNoBorderRadius,
  ]), [
    dropDownContainerStyle,
    pickerHeight,
    maxHeight,
    dropDownNoBorderRadius,
    direction,
    _zIndex,
    THEME,
  ]);

  /**
     * The modal content container style.
     * @returns {object}
     */
  const _modalContentContainerStyle = useMemo(() => ([
    THEME.modalContentContainer,
    ...[modalContentContainerStyle].flat(),
  ]), [modalContentContainerStyle, THEME]);

  /**
     * The zIndex of the container.
     * @returns {object}
     */
  const zIndexContainer = useMemo(() => Platform.OS !== 'android' && {
    zIndex: _zIndex,
  }, [_zIndex]);

  /**
     * The container style.
     * @returns {object}
     */
  const _containerStyle = useMemo(() => ([
    THEME.container,
    zIndexContainer,
    ...[containerStyle].flat(),
    ...[_disabledStyle].flat(),
  ]), [zIndexContainer, containerStyle, _disabledStyle, THEME]);

  /**
     * The arrow icon container style.
     * @returns {object}
     */
  const _arrowIconContainerStyle = useMemo(() => ([
    RTL_STYLE(rtl, THEME.arrowIconContainer),
    ...[arrowIconContainerStyle].flat(),
  ]), [rtl, arrowIconContainerStyle, THEME]);

  /**
     * The arrow component.
     * @returns {JSX.Element}
     */
  const _ArrowComponent = useMemo(() => {
    if (!showArrowIcon) return null;

    let Component;
    if (open && ArrowUpIconComponent !== null) Component = <ArrowUpIconComponent style={_arrowIconStyle} />;
    else if (!open && ArrowDownIconComponent !== null) Component = <ArrowDownIconComponent style={_arrowIconStyle} />;
    else Component = <Image source={open ? ICON.ARROW_UP : ICON.ARROW_DOWN} style={_arrowIconStyle} />;

    return (
      <View style={_arrowIconContainerStyle}>
        {Component}
      </View>
    );
  }, [
    showArrowIcon,
    open,
    ArrowUpIconComponent,
    ArrowDownIconComponent,
    _arrowIconStyle,
    _arrowIconContainerStyle,
    ICON,
  ]);

  /**
     * The icon container style.
     * @returns {object}
     */
  const _iconContainerStyle = useMemo(() => ([
    RTL_STYLE(rtl, THEME.iconContainer),
    ...[iconContainerStyle].flat(),
  ]), [rtl, iconContainerStyle, THEME]);

  /**
     * The selected item icon component.
     * @returns {JSX.Element|null}
     */
  const SelectedItemIconComponent = useMemo(() => {
    const Component = _selectedItemIcon();

    return Component !== null && (
    <View style={_iconContainerStyle}>
      <Component />
    </View>
    );
  }, [_selectedItemIcon, _iconContainerStyle]);

  /**
     * The simple body component.
     * @returns {JSX.Element}
     */
  const SimpleBodyComponent = useMemo(() => (
    <>
      {SelectedItemIconComponent}
      <Text style={_labelStyle} {...labelProps}>
        {_selectedItemLabel}
      </Text>
    </>
  ), [SelectedItemIconComponent, _labelStyle, labelProps, _selectedItemLabel]);

  /**
     * onPress badge.
     */
  const onPressBadge = useCallback((value) => {
    setValue((state) => {
      const _state = [...state];
      const index = _state.findIndex((item) => item === value);
      _state.splice(index, 1);

      return _state;
    });
  }, [setValue]);

  /**
     * The badge colors.
     * @returns {object}
     */
  const _badgeColors = useMemo(() => {
    if (typeof badgeColors === 'string') return [badgeColors];

    return badgeColors;
  }, [badgeColors]);

  /**
     * The badge dot colors.
     * @returns {object}
     */
  const _badgeDotColors = useMemo(() => {
    if (typeof badgeDotColors === 'string') return [badgeDotColors];

    return badgeDotColors;
  }, [badgeDotColors]);

  /**
     * Get badge color.
     * @param {string} str
     * @returns {string}
     */
  const getBadgeColor = useCallback((str) => {
    str = `${str}`;

    const index = Math.abs(ASCII_CODE(str)) % _badgeColors.length;
    return _badgeColors[index];
  }, [_badgeColors]);

  /**
     * Get badge dot color.
     * @param {string} str
     * @returns {string}
     */
  const getBadgeDotColor = useCallback((str) => {
    str = `${str}`;

    const index = Math.abs(ASCII_CODE(str)) % _badgeDotColors.length;
    return _badgeDotColors[index];
  }, [_badgeDotColors]);

  /**
     * The render badge component.
     * @returns {JSX.Element}
     */
  const RenderBadgeComponent = useMemo(() => (renderBadgeItem !== null ? renderBadgeItem : RenderBadgeItem), [renderBadgeItem]);

  /**
     * Render badge.
     * @returns {JSX.Element}
     */
  const __renderBadge = useCallback(({ item }) => (
    <RenderBadgeComponent
      rtl={rtl}
      label={item[schema.label]}
      value={item[schema.value]}
      IconComponent={item[schema.icon] ?? null}
      textStyle={textStyle}
      badgeStyle={badgeStyle}
      badgeTextStyle={badgeTextStyle}
      badgeDotStyle={badgeDotStyle}
      getBadgeColor={getBadgeColor}
      getBadgeDotColor={getBadgeDotColor}
      showBadgeDot={showBadgeDot}
      onPress={onPressBadge}
      theme={theme}
      THEME={THEME}
    />
  ), [
    rtl,
    schema,
    textStyle,
    badgeStyle,
    badgeTextStyle,
    badgeDotStyle,
    getBadgeColor,
    getBadgeDotColor,
    showBadgeDot,
    onPressBadge,
    theme,
    THEME,
  ]);

  /**
     * The badge key.
     * @returns {string}
     */
  const itemKey = useMemo(() => schema.value, [null, schema]);

  /**
     * The key extractor.
     * @returns {string}
     */
  const keyExtractor = useCallback((item) => `${item[itemKey]}`, [itemKey]);

  /**
     * The badge separator style.
     * @returns {object}
     */
  const _badgeSeparatorStyle = useMemo(() => ([
    THEME.badgeSeparator,
    ...[badgeSeparatorStyle].flat(),
  ]), [badgeSeparatorStyle, THEME]);

  /**
     * The badge separator component.
     * @returns {JSX.Element}
     */
  const BadgeSeparatorComponent = useCallback(() => (
    <View style={_badgeSeparatorStyle} />
  ), [_badgeSeparatorStyle]);

  /**
     * The label container.
     * @returns {object}
     */
  const labelContainer = useMemo(() => ([
    THEME.labelContainer, rtl && {
      transform: [
        { scaleX: -1 },
      ],
    },
  ]), [rtl, THEME]);

  /**
     * Badge list empty component.
     * @returns {JSX.Element}
     */
  const BadgeListEmptyComponent = useCallback(() => (
    <View style={labelContainer}>
      <Text style={_labelStyle} {...labelProps}>
        {_placeholder}
      </Text>
    </View>
  ), [_labelStyle, labelContainer, labelProps, _placeholder]);

  /**
     * Set ref.
     */
  const setBadgeFlatListRef = useCallback((ref) => {
    badgeFlatListRef.current = ref;
  }, []);

  /**
     * The badge body component.
     * @returns {JSX.Element}
     */
  const BadgeBodyComponent = useMemo(() => (
    <FlatList

      ref={setBadgeFlatListRef}
      data={selectedItems}
      renderItem={__renderBadge}
      horizontal
      showsHorizontalScrollIndicator={false}
      keyExtractor={keyExtractor}
      ItemSeparatorComponent={BadgeSeparatorComponent}
      ListEmptyComponent={BadgeListEmptyComponent}
      style={THEME.listBody}
      contentContainerStyle={THEME.listBodyContainer}

      inverted={rtl}
    />
  ), [
    rtl,
    selectedItems,
    __renderBadge,
    keyExtractor,
    BadgeSeparatorComponent,
    BadgeListEmptyComponent,
    THEME,
  ]);

  /**
     * The body component.
     */
  const _BodyComponent = useMemo(() => {
    switch (_mode) {
      case MODE.SIMPLE: return SimpleBodyComponent;
      case MODE.BADGE: return multiple ? BadgeBodyComponent : SimpleBodyComponent;
      default: //
    }
  }, [_mode, SimpleBodyComponent, BadgeBodyComponent, multiple]);

  /**
     * The list item container style.
     * @returns {object}
     */
  const _listItemContainerStyle = useMemo(() => ([
    RTL_DIRECTION(rtl, THEME.listItemContainer),
    ...[listItemContainerStyle].flat(),
  ]), [rtl, listItemContainerStyle, THEME]);

  const _listItemContainerStyle2 = useMemo(() => ([
    RTL_DIRECTION(rtl, THEME.listItemContainer2),
    ...[listItemContainerStyle].flat(),
  ]), [rtl, listItemContainerStyle, THEME]);
    /**
     * The tick icon container style.
     * @returns {object}
     */
  const _tickIconContainerStyle = useMemo(() => ([
    RTL_STYLE(rtl, THEME.tickIconContainer),
    ...[tickIconContainerStyle].flat(),
  ]), [rtl, tickIconContainerStyle, THEME]);

  /**
     * The list item label style.
     * @returns {object}
     */
  const _listItemLabelStyle = useMemo(() => ([
    THEME.listItemLabel,
    ...[textStyle].flat(),
    ...[listItemLabelStyle].flat(),
  ]), [textStyle, listItemLabelStyle, THEME]);

  /**
     * The tick icon style.
     * @returns {object}
     */
  const _tickIconStyle = useMemo(() => ([
    THEME.tickIcon,
    ...[tickIconStyle].flat(),
  ]), [tickIconStyle, THEME]);

  /**
     * The search container style.
     * @returns {object}
     */
  const _searchContainerStyle = useMemo(() => ([
    RTL_DIRECTION(rtl, THEME.searchContainer),
    ...[searchContainerStyle].flat(), !searchable && listMode === LIST_MODE.MODAL && {
      flexDirection: 'row-reverse',
    },
  ]), [rtl, listMode, searchable, searchContainerStyle, THEME]);

  /**
     * The search text input style.
     * @returns {object}
     */
  const _searchTextInputStyle = useMemo(() => ([
    textStyle,
    THEME.searchTextInput,
    ...[searchTextInputStyle].flat(),
  ]), [textStyle, searchTextInputStyle, THEME]);

  /**
     * The close icon container style.
     * @returns {object}
     */
  const _closeIconContainerStyle = useMemo(() => ([
    RTL_STYLE(rtl, THEME.closeIconContainer),
    ...[closeIconContainerStyle].flat(),
  ]), [rtl, closeIconContainerStyle, THEME]);

  /**
     * The close icon style.
     * @returns {object}
     */
  const _closeIconStyle = useMemo(() => ([
    THEME.closeIcon,
    ...[closeIconStyle].flat(),
  ]), [closeIconStyle, THEME]);

  /**
     * The list message container style.
     * @returns {objects}
     */
  const _listMessageContainerStyle = useMemo(() => ([
    THEME.listMessageContainer,
    ...[listMessageContainerStyle].flat(),
  ]), [listMessageContainerStyle, THEME]);

  /**
     * The list message text style.
     * @returns {object}
     */
  const _listMessageTextStyle = useMemo(() => ([
    THEME.listMessageText,
    ...[listMessageTextStyle].flat(),
  ]), [listMessageTextStyle, THEME]);

  /**
     * onPress item.
     */
  const onPressItem = useCallback((item, customItem = false) => {
    if (customItem !== false) {
      setItems((state) => [...state, item]);
    }

    setValue((state) => {
      if (multiple) {
        const _state = state !== null ? [...state] : [];

        if (_state.includes(item[schema.value])) {
          // Remove the value
          if (Number.isInteger(min) && min >= _state.length) {
            return state;
          }

          const index = _state.findIndex((x) => x === item[schema.value]);
          _state.splice(index, 1);
        } else {
          // Add the value
          if (Number.isInteger(max) && max <= _state.length) {
            return state;
          }

          _state.push(item[schema.value]);
        }

        return _state;
      }
      return item[schema.value];
    });

    setNecessaryItems((state) => {
      if (multiple) {
        const _state = [...state];

        if (_state.findIndex((x) => x[schema.value] === item[schema.value]) > -1) {
          // Remove the item
          if (Number.isInteger(min) && min >= _state.length) {
            return state;
          }

          const index = _state.findIndex((x) => x[schema.value] === item[schema.value]);
          _state.splice(index, 1);

          return _state;
        }
        // Add the item
        if (Number.isInteger(max) && max <= _state.length) {
          return state;
        }

        _state.push(item);

        return _state;
      }
      return [item];
    });

    if (closeAfterSelecting && !multiple) onPressClose();
  }, [
    setValue,
    multiple,
    min,
    max,
    onPressClose,
    closeAfterSelecting,
    multiple,
    setItems,
    schema,
  ]);

  /**
     * The tick icon component.
     * @returns {JSX.Element}
     */
  const _TickIconComponent = useCallback(() => {
    if (!showTickIcon) return null;

    let Component;
    if (TickIconComponent !== null) Component = <TickIconComponent style={_tickIconStyle} />;
    else Component = <Image source={ICON.TICK} style={_tickIconStyle} />;

    return (
      <View style={_tickIconContainerStyle}>
        {Component}
      </View>
    );
  }, [TickIconComponent, _tickIconStyle, _tickIconContainerStyle, showTickIcon, ICON]);

  /**
     * The renderItem component.
     * @returns {JSX.Element}
     */
  const RenderItemComponent = useMemo(() => (renderListItem !== null ? renderListItem : RenderListItem), [renderListItem]);

  /**
     * The selected item container style.
     * @returns {object}
     */
  const _selectedItemContainerStyle = useMemo(() => ([
    THEME.selectedItemContainer,
    selectedItemContainerStyle,
  ]), [THEME, selectedItemContainerStyle]);

  /**
     * The selected item label style.
     * @returns {object}
     */
  const _selectedItemLabelStyle = useMemo(() => ([
    THEME.selectedItemLabel,
    selectedItemLabelStyle,
  ]), [THEME, selectedItemLabelStyle]);

  /**
     * The disabled item container style.
     * @returns {object}
     */
  const _disabledItemContainerStyle = useMemo(() => ([
    THEME.disabledItemContainer,
    disabledItemContainerStyle,
  ]), [THEME, disabledItemContainerStyle]);

  /**
     * The disabled item label style.
     * @returns {object}
     */
  const _disabledItemLabelStyle = useMemo(() => ([
    THEME.disabledItemContainer,
    disabledItemLabelStyle,
  ]), [THEME, disabledItemLabelStyle]);

  /**
     * Render list item.
     * @returns {JSX.Element}
     */
  const __renderListItem = useCallback(({ item }) => {
    let IconComponent = item[schema.icon] ?? null;

    if (IconComponent) {
      IconComponent = (
        <View style={_iconContainerStyle}>
          <IconComponent />
        </View>
      );
    }

    let isSelected;
    if (multiple) {
      isSelected = _value.includes(item[schema.value]);
    } else {
      isSelected = _value === item[schema.value];
    }

    return (
      <RenderItemComponent
        rtl={rtl}
        item={item}
        label={item[schema.label]}
        value={item[schema.value]}
        parent={item[schema.parent] ?? null}
        selectable={item[schema.selectable] ?? null}
        disabled={item[schema.disabled] ?? false}
        custom={item.custom ?? false}
        isSelected={isSelected}
        IconComponent={IconComponent}
        TickIconComponent={_TickIconComponent}
        listItemContainerStyle={_listItemContainerStyle}
        listItemContainerStyle2={_listItemContainerStyle2}

        listItemLabelStyle={_listItemLabelStyle}
        listChildContainerStyle={listChildContainerStyle}
        listChildLabelStyle={listChildLabelStyle}
        listParentContainerStyle={listParentContainerStyle}
        listParentLabelStyle={listParentLabelStyle}
        customItemContainerStyle={customItemContainerStyle}
        customItemLabelStyle={customItemLabelStyle}
        selectedItemContainerStyle={_selectedItemContainerStyle}
        selectedItemLabelStyle={_selectedItemLabelStyle}
        disabledItemContainerStyle={_disabledItemContainerStyle}
        disabledItemLabelStyle={_disabledItemLabelStyle}
        categorySelectable={categorySelectable}
        onPress={onPressItem}
        theme={theme}
        THEME={THEME}
      />
    );
  }, [
    rtl,
    RenderItemComponent,
    _listItemLabelStyle,
    _iconContainerStyle,
    listChildContainerStyle,
    listChildLabelStyle,
    listParentContainerStyle,
    listParentLabelStyle,
    _listItemContainerStyle,
    _listItemLabelStyle,
    customItemContainerStyle,
    customItemLabelStyle,
    _selectedItemContainerStyle,
    _selectedItemLabelStyle,
    _disabledItemContainerStyle,
    _disabledItemLabelStyle,
    _TickIconComponent,
    schema,
    _value,
    multiple,
    categorySelectable,
    onPressItem,
    theme,
    THEME,
  ]);

  /**
     * The item separator.
     * @returns {JSX.Element|null}
     */
  const ItemSeparatorComponent = useCallback(() => {
    if (!itemSeparator) return null;

    return (
      <View style={[
        THEME.itemSeparator,
        ...[itemSeparatorStyle].flat(),
      ]}
      />
    );
  }, [itemSeparator, itemSeparatorStyle, THEME]);

  /**
     * The search placeholder.
     * @returns {string}
     */
  const _searchPlaceholder = useMemo(() => {
    if (searchPlaceholder !== null) return searchPlaceholder;

    return ('search for tags');
  }, [searchPlaceholder, 'search for tags']);

  /**
     * onChangeSearchText.
     * @param {string} text
     */
  const _onChangeSearchText = useCallback((text) => {
    setSearchText(text);
    onChangeSearchText(text);
  }, [onChangeSearchText]);

  /**
     * The close icon component.
     * @returns {JSX.Element}
     */
  const _CloseIconComponent = useMemo(() => {
    if (listMode !== LIST_MODE.MODAL) return null;

    let Component;

    if (CloseIconComponent !== null) Component = <CloseIconComponent style={_closeIconStyle} />;
    else Component = <Image source={ICON.CLOSE} style={_closeIconStyle} />;

    return (
      <TouchableOpacity style={_closeIconContainerStyle} onPress={onPressClose}>
        {Component}
      </TouchableOpacity>
    );
  }, [listMode, CloseIconComponent, _closeIconStyle, _closeIconContainerStyle, onPressClose, ICON]);

  /**
     * Indicates if the search component is visible.
     * @returns {boolean}
     */
  const isSearchComponentVisible = useMemo(() => {
    if (listMode === LIST_MODE.MODAL) return true;

    return searchable;
  }, [listMode, searchable]);

  /**
     * The search component.
     * @returns {JSX.Element}
     */
  const SearchComponent = useMemo(() => isSearchComponentVisible && (
    <View style={_searchContainerStyle}>
      {
                searchable && (
                <TextInput
                  value={searchText}
                  onChangeText={_onChangeSearchText}
                  style={_searchTextInputStyle}
                  placeholder={_searchPlaceholder}
                  placeholderTextColor={searchPlaceholderTextColor}
                  {...searchTextInputProps}
                />
                )
            }
      {_CloseIconComponent}

    </View>
  ), [
    searchable,
    isSearchComponentVisible,
    _onChangeSearchText,
    _searchContainerStyle,
    _searchTextInputStyle,
    _searchPlaceholder,
    searchPlaceholderTextColor,
    searchText,
    searchTextInputProps,
  ]);

  /**
     * The dropdown component wrapper.
     * @returns {JSX.Element}
     */
  const DropDownComponentWrapper = useCallback((Component) => (
    <View style={_dropDownContainerStyle}>
      {SearchComponent}
      {Component}
    </View>
  ), [_dropDownContainerStyle, SearchComponent]);

  /**
     * The ActivityIndicatorComponent.
     * @returns {JSX.Element}
     */
  const _ActivityIndicatorComponent = useCallback(() => {
    let Component;

    if (ActivityIndicatorComponent !== null) Component = ActivityIndicatorComponent;
    else Component = ActivityIndicator;

    return <Component size={activityIndicatorSize} color={activityIndicatorColor} />;
  }, [ActivityIndicatorComponent, activityIndicatorSize, activityIndicatorColor]);

  /**
     * The ListEmptyComponent.
     * @returns {JSX.Element}
     */
  const _ListEmptyComponent = useCallback(() => {
    let Component;
    const message = ('');

    if (ListEmptyComponent !== null) Component = ListEmptyComponent;
    else Component = ListEmpty;

    return (
      <Component
        listMessageContainerStyle={_listMessageContainerStyle}
        listMessageTextStyle={_listMessageTextStyle}
        ActivityIndicatorComponent={_ActivityIndicatorComponent}
        loading={loading}
        message={message}
      />
    );
  }, [
    'message',
    _listMessageContainerStyle,
    _listMessageTextStyle,
    ListEmptyComponent,
    _ActivityIndicatorComponent,
    loading,
  ]);

  /**
     * The dropdown flatlist component.
     * @returns {JSX.Element}
     */
  const DropDownFlatListComponent = useMemo(() => (
    <FlatList
      style={styles.flex}
      contentContainerStyle={THEME.flatListContentContainer}
      ListEmptyComponent={_ListEmptyComponent}
      data={_items}
      renderItem={__renderListItem}
      keyExtractor={keyExtractor}
      extraData={_value}
      ItemSeparatorComponent={ItemSeparatorComponent}
      {...flatListProps}
    />
  ), [
    _items,
    _value,
    __renderListItem,
    keyExtractor,
    ItemSeparatorComponent,
    flatListProps,
    _ListEmptyComponent,
    THEME,
  ]);

  /**
     * The dropdown scrollview component.
     * @returns {JSX.Element}
     */
  const DropDownScrollViewComponent = useMemo(() => (
    <ScrollView nestedScrollEnabled {...scrollViewProps}>
      {_items.map((item, index) => (
        <Fragment key={item[itemKey]}>
          {index > 0 && ItemSeparatorComponent()}
          {__renderListItem({ item })}
        </Fragment>
      ))}
      {_items.length === 0 && _ListEmptyComponent()}
    </ScrollView>
  ), [__renderListItem, itemKey, scrollViewProps, _ListEmptyComponent]);

  /**
     * The dropdown modal component.
     * @returns {JSX.Element}
     */
  const DropDownModalComponent = useMemo(() => (
    <Modal visible={open} presentationStyle="fullScreen" {...modalProps}>
      <SafeAreaView style={_modalContentContainerStyle}>
        {SearchComponent}
        {DropDownFlatListComponent}
      </SafeAreaView>
    </Modal>
  ), [open, SearchComponent, _modalContentContainerStyle, modalProps]);

  /**
     * The dropdown component.
     * @returns {JSX.Element}
     */
  const DropDownComponent = useMemo(() => {
    switch (listMode) {
      case LIST_MODE.FLATLIST: return DropDownComponentWrapper(DropDownFlatListComponent);
      case LIST_MODE.SCROLLVIEW: return DropDownComponentWrapper(DropDownScrollViewComponent);
      case LIST_MODE.MODAL: return DropDownModalComponent;
      default: //
    }
  }, [
    listMode,
    DropDownFlatListComponent,
    DropDownScrollViewComponent,
    DropDownModalComponent,
    DropDownComponentWrapper,
  ]);

  /**
     * The body of the dropdown component.
     * @returns {JSX.Element}
     */
  const DropDownBodyComponent = useMemo(() => {
    if (open || listMode === LIST_MODE.MODAL) return DropDownComponent;
    return null;
  }, [open, listMode, DropDownComponent]);

  /**
     * onRef.
     */
  const onRef = useCallback((ref) => {
    pickerRef.current = ref;
  }, []);

  /**
     * Pointer events.
     * @returns {string}
     */
  const pointerEvents = useMemo(() => (disabled ? 'none' : 'auto'), [disabled]);

  return (
    <View style={_containerStyle} {...containerProps}>
      <TouchableOpacity style={_style} onPress={__onPress} onLayout={__onLayout} {...props} ref={onRef} pointerEvents={pointerEvents} disabled={disabled}>
        {_BodyComponent}
        {_ArrowComponent}
      </TouchableOpacity>
      {DropDownBodyComponent}
    </View>
  );
}
Example #19
Source File: CollectionInputScreen.js    From discovery-mobile-ui with MIT License 4 votes vote down vote up
CollectionInputScreen = ({
  collection,
  createCollectionAction,
  selectCollectionAction,
  editCollectionDetailsAction,
  creatingCollection,
  collectionsLabels,
  collections,
  renameCollectionAction,
}) => {
  const navigation = useNavigation();
  const [title, onChangeTitle] = useState(creatingCollection ? DEFAULT_COLLECTION_NAME : collection.label); // eslint-disable-line max-len
  const [purpose, onChangePurpose] = useState(creatingCollection ? '' : collection?.purpose);
  const [current, currentSelection] = useState(creatingCollection ? false : collection?.current);
  const [urgent, urgentSelection] = useState(creatingCollection ? false : collection?.urgent);
  const [newCollectionID, setCollectionID] = useState('');
  const [isAddingCollection, setIsAddingCollection] = useState(false);
  const [collectionsDialogText, setCollectionsDialogText] = useState(null);
  const [open, setOpen] = useState(false);
  const [hasTextValue, setHasTextValue] = useState(false);
  const [sameName, setSameName] = useState(false);
  const [moveToCatalog, setMoveToCatalog] = useState(false);

  const itemsList = [

  ];
  const itemNames = [];
  const collectionNames = [];
  if (Object.keys(collections).length > 0) {
    Object.keys(collections).forEach((key) => {
      if (collections[key] != null) {
        collectionNames.push(collections[key].label);
        for (let j = 0; j < collections[key].tags.length; j += 1) {
          if (!itemNames.includes(collections[key].tags[j])) {
            itemNames.push(collections[key].tags[j]);
          }
        }
      }
    });
  }
  for (let i = 0; i < itemNames.length; i += 1) {
    itemsList.push({ label: itemNames[i], value: itemNames[i] });
  }
  const [items, setItems] = useState(itemsList);
  const [value, setValue] = useState(creatingCollection ? [] : collection.tags);

  const discardInputAlert = () => {
    Alert.alert(
      'Discard Edits',
      'Are you sure you want to discard edits to this collection?',
      [
        {
          text: 'Cancel',
          onPress: () => {},
          style: 'cancel',
        },
        {
          text: 'Discard',
          onPress: () => handleCloseInput(),
          style: 'destructive',
        },
      ],
    );
  };

  const handleCloseInput = ({ alert }) => {
    if (alert) {
      discardInputAlert();
    }
  };

  const handleSave = () => {
    if (creatingCollection) {
      if (!collectionNames.includes(title)) {
        if (hasTextValue) {
          if (hasInputErrors({ text: title, isRename: false, label: title })) {
            return;
          }
          createCollectionAction(title);
          setIsAddingCollection(true);
        }
      }
    } else {
      if (hasInputErrors({ text: title, isRename: true, label: title })) {
        return;
      }
      renameCollectionAction(newCollectionID, title);

      editCollectionDetailsAction(purpose, value, (current || urgent), urgent);
    }
  };
  const saveCollection = () => {
    handleSave();
    navigation.navigate('CollectionsList');
  };
  const saveAndContinue = () => {
    if (creatingCollection) {
      if (!collectionNames.includes(title)) {
        if (hasTextValue) {
          if (hasInputErrors({ text: title, isRename: false, label: title })) {
            return;
          }
          createCollectionAction(title);
          setIsAddingCollection(true);
        }
      }
    } else {
      if (hasInputErrors({ text: title, isRename: true, label: title })) {
        return;
      }
      renameCollectionAction(newCollectionID, title);

      editCollectionDetailsAction(purpose, value, (current || urgent), urgent);
    }
    setMoveToCatalog(true);
    //
  };
  const discardChanges = () => {
    setCollectionsDialogText(CollectionsDialogText[COLLECTIONS_DIALOG_ACTIONS.DISCARD]);
  };

  const discardChangesCreate = () => {
    setCollectionsDialogText(CollectionsDialogText[COLLECTIONS_DIALOG_ACTIONS.DISCARD_CREATE]);
  };
    // selectCollectionAction(title);

  // console.log(collection)
  // collection.label = title
  // collection.tags = tags

  useEffect(() => {
    if (Object.keys(collections).length > 0) {
      setCollectionID(Object.keys(collections)[Object.keys(collections).length - 1]);

      if (isAddingCollection) {
        selectCollectionAction(Object.keys(collections)[Object.keys(collections).length - 1]);
        editCollectionDetailsAction(purpose, value, (current || urgent), urgent);
        setIsAddingCollection(false);
      }
    }
    if (moveToCatalog) {
      navigation.navigate('Catalog');
    }

    // if (useState(collections )!== collections) {
    // }
  }, [collections, isAddingCollection, moveToCatalog]);

  useEffect(() => {
    setSameName(false);
    if (title.length > 0) {
      setHasTextValue(true);
    }
    if (creatingCollection) {
      for (let i = 0; i < collectionNames.length; i += 1) {
        if (collectionNames[i].toLowerCase() === title.toLowerCase()) {
          setHasTextValue(false);
          setSameName(true);
        }
      }
    }
  }, [title]);

  const saveButtonTextStyle = hasTextValue ? styles.saveButtonText : styles.disabledSaveButtonText;

  // PLACEHOLDERS
  const placeholderTitle = creatingCollection ? '' : collection.label;

  const isUniqueName = ({ text, isRename, label }) => {
    // if action is rename, new label can be same as old label
    if (isRename && (text.toLowerCase() === label.toLowerCase())) {
      return true;
    }
    return !((collectionsLabels).includes(text.toLowerCase()));
  };
  const hasMinLength = (text) => text.length > 0;

  const hasInputErrors = ({ text, isRename, label }) => {
    if (!hasMinLength(text)) {
      return true;
    }
    if (!isUniqueName({ text, isRename, label })) {
      return true;
    }
    return false;
  };
  const reduceInputs = () => {
    Keyboard.dismiss();
    setOpen(false);
  };

  return (

    <SafeAreaView style={styles.root}>

      <Header style={styles.header}>
        <Left>
          <TouchableOpacity onPress={() => navigation.goBack()}>
            <Entypo name="chevron-thin-left" size={20} color="black" />
          </TouchableOpacity>
        </Left>
        <TouchableWithoutFeedback onPress={reduceInputs}>
          <View style={styles.headerTitleContainer}>
            <Title style={styles.headerText}><Text>{title}</Text></Title>
          </View>
        </TouchableWithoutFeedback>
        <Right>
          <TouchableWithoutFeedback style={styles.empty_toucable} onPress={reduceInputs}>
            <View style={styles.headerTitleContainer}>

              <Text style={styles.header_empty_text}> </Text>

            </View>

          </TouchableWithoutFeedback>
        </Right>
      </Header>

      <View style={styles.inputField}>

        <KeyboardAvoidingView behavior="padding">
          <TouchableWithoutFeedback onPress={reduceInputs}>

            <View style={styles.textInputDiv}>
              <Text variant="title" style={styles.formHeader}>Title</Text>
            </View>
          </TouchableWithoutFeedback>

          <View style={styles.titleTextInputContainer}>
            <TextInput
              style={styles.textInput}
              onChangeText={onChangeTitle}
              placeholder={placeholderTitle}
              value={title}
              onTouchStart={() => setOpen(false)}
              multiline={false}
              autoFocus
            />
          </View>

          <View style={styles.titleFooter}>
            {sameName
            && (
            <View style={styles.sameNameAlertContainer}>
              <Text style={{ color: Colors.destructive }}>Collection name must be unique</Text>
            </View>
            )}
          </View>

        </KeyboardAvoidingView>

        <KeyboardAvoidingView behavior="padding">

          <TouchableWithoutFeedback onPress={reduceInputs}>

            <View style={styles.textInputDiv}>

              <TouchableOpacity style={styles.textInputHeader} disabled>
                <Text variant="title" style={styles.formHeader}>Purpose</Text>
              </TouchableOpacity>
            </View>
          </TouchableWithoutFeedback>

          <View style={styles.purposeTextInputContainer}>
            <TextInput
              style={styles.textInput}
              onChangeText={onChangePurpose}
              placeholder="add purpose"
              onSubmitEditing={Keyboard.dismiss}
              value={purpose}
              onTouchStart={() => setOpen(false)}
              multiline={false}
              autoFocus
            />
          </View>
        </KeyboardAvoidingView>

        <View style={styles.tagTextHeader}>
          <TouchableWithoutFeedback disabled onPress={reduceInputs}>
            <Text variant="title" style={styles.formHeader}>Collection Tags</Text>
          </TouchableWithoutFeedback>
        </View>

        <View style={{ zIndex: 100, backgroundColor: '#fff' }}>
          <Picker
            multiple
            min={0}
            max={5}
            open={open}
            value={value}
            setOpen={setOpen}
            setValue={setValue}
            items={items}
            setItems={setItems}
            searchable
            placeholder="add new or existing tags "
          />
        </View>
        <View style={styles.switchTextHeader}>
          <TouchableWithoutFeedback disabled onPress={reduceInputs}>

            <Text variant="title" style={styles.formHeader}>Priority</Text>
          </TouchableWithoutFeedback>

        </View>

        <View style={styles.switchRow}>
          <View style={styles.currentTextField}>

            <Feather name="watch" size={18} color={Colors.currentCollectionColor} />

            <Text style={styles.switchText}>Current</Text>
          </View>

          <Switch
            trackColor={{
              false: Colors.mediumgrey,
              true: Platform.OS === 'ios' ? Colors.primary : Colors.primaryLight,
            }}
            thumbColor={(Platform.OS === 'ios') ? 'white' : Colors[(current ? 'primary' : 'primaryLight')]}
            onValueChange={() => currentSelection(!current)}
            value={current || urgent}
            disabled={urgent}
          />
          <View style={styles.leftRightPadding}>
            <Feather name="alert-triangle" size={18} color={Colors.destructive} />

            <Text variant="title" style={styles.switchText}>Urgent</Text>
          </View>

          <Switch
            trackColor={{
              false: Colors.mediumgrey,
              true: Platform.OS === 'ios' ? Colors.primary : Colors.primaryLight,
            }}
            thumbColor={(Platform.OS === 'ios') ? 'white' : Colors[(urgent ? 'primary' : 'primaryLight')]}
            onValueChange={() => urgentSelection(!urgent)}
            value={urgent}
          />
        </View>
      </View>

      <KeyboardAvoidingView style={styles.textRow}>
        <TouchableOpacity
          style={styles.saveButton}
          onPress={() => {
            if (creatingCollection) {
              discardChangesCreate();
            } else {
              discardChanges();
            }
          }}
        >
          <BaseText variant="title" style={styles.discardButtonText}>Discard</BaseText>
        </TouchableOpacity>

        {collectionsDialogText && (
        <CollectionsDialog
          collectionsDialogText={collectionsDialogText}
          setCollectionsDialogText={setCollectionsDialogText}
        />
        )}
        <View style={styles.saveCol}>
          <TouchableOpacity
            style={styles.saveButton}
            onPress={saveCollection}
            disabled={!hasTextValue}
          >
            <BaseText variant="title" style={saveButtonTextStyle}>Save</BaseText>
          </TouchableOpacity>

          <TouchableOpacity
            style={styles.saveButton}
            onPress={saveAndContinue}
            disabled={!hasTextValue}
          >
            <BaseText variant="title" style={saveButtonTextStyle}>Save and Continue</BaseText>
          </TouchableOpacity>
        </View>
      </KeyboardAvoidingView>

    </SafeAreaView>
  );
}
Example #20
Source File: CollectionRow.js    From discovery-mobile-ui with MIT License 4 votes vote down vote up
CollectionRow = ({
  collection,
  collectionId,
  label,
  navigation,
  selectCollectionAction,
  updateIsAddingNewCollectionAction,
}) => {
  const [showDetails, setShowDetails] = useState(false);
  const handlePress = () => {
    selectCollectionAction(collectionId);
    updateIsAddingNewCollectionAction(false);

    navigation.navigate('Catalog');
  };
  const createdDate = formatDateShort(collection.created);
  const modifiedDate = formatDateShort(collection.lastUpdated);
  const collectionNotesCount = Object.keys(collection.notes).length;
  const collectionRecords = Object.values(collection.records);
  const recordNotesCount = collectionRecords.reduce((acc, { notes }) => (
    notes ? acc.concat(Object.keys(notes)) : acc), []).length;
  const savedRecordsCount = collectionRecords.filter((record) => record.saved === true).length;
  const showPurpose = (collection?.purpose.length) > 0;
  return (
    <View style={styles.collectionRowContainer}>
      <View style={styles.dateInfoRow}>
        <View style={styles.dateInfoMargin}>
          <DateInfo date={modifiedDate} />
        </View>
        <DateInfo date={createdDate} color={Colors.darkgrey} />
      </View>
      <TouchableOpacity style={styles.collectionRow} onPress={handlePress}>
        <View style={styles.collectionRowCountIconsContainer}>
          <CountInfo count={savedRecordsCount} color={Colors.collectionYellow} />
          <CountInfo count={collectionNotesCount + recordNotesCount} color={Colors.mediumgrey} />
          <Text style={styles.labelText}>{label}</Text>
        </View>
        <View style={styles.iconContainer}>

          {collection?.current
            && (
            <View style={styles.iconPadding}>
              <Feather name="watch" size={20} color={Colors.currentCollectionColor} />
            </View>
            )}
          {collection?.urgent
            && (
            <View style={styles.iconPadding}>
              <Feather name="alert-triangle" size={20} color={Colors.destructive} />
            </View>
            )}

          <TouchableOpacity style={styles.infoIcon} onPress={() => setShowDetails(!showDetails)}>
            <Ionicons name="information-circle-outline" size={24} color="black" />
          </TouchableOpacity>
          <CollectionRowActionIcon collectionId={collectionId} collectionLabel={label} />
        </View>
      </TouchableOpacity>
      {showDetails && (
        <View style={styles.detailsContainer}>
          {collection.preBuilt && (
            <View style={styles.descriptionContainer}>
              <Text>
                <PreBuiltDescriptionText collectionId={collectionId} />
              </Text>
            </View>
          )}
          <View>

            {showPurpose
            && (
            <View>
              <Text style={styles.purposeText}>
                {collection?.purpose}
              </Text>
            </View>
            )}

            {collection?.current
            && (
            <View style={styles.currentTextField}>

              <Feather name="watch" size={18} color={Colors.currentCollectionColor} />

              <Text style={styles.switchText}>Current Collection</Text>
            </View>
            )}
            {collection?.urgent
            && (
            <View style={styles.currentTextField}>
              <Feather name="alert-triangle" size={18} color={Colors.destructive} />

              <Text variant="title" style={styles.switchText}>Urgent Collection</Text>
            </View>
            )}
            <View style={styles.badgeRow}>
              {Object.entries(collection.tags).map((item, index) => (
                <TouchableOpacity style={styles.badgeStyle}>
                  <Text>{collection.tags[index]}</Text>

                </TouchableOpacity>
              ))}
            </View>

            <CountInfo count={savedRecordsCount} label="Records In Collection" color={Colors.collectionYellow} />
            <CountInfo count={collectionNotesCount} label="Collection Notes" color={Colors.mediumgrey} />
            <CountInfo count={recordNotesCount} label="Record Notes" color={Colors.mediumgrey} />
            <View style={styles.dateInfoContainer}>

              <DateInfo date={modifiedDate} label="Last Modified" />

              <View style={styles.dateInfoContainer}>
                <DateInfo date={createdDate} label="Created" color={Colors.darkgrey2} />

              </View>
            </View>

          </View>
        </View>
      )}
    </View>
  );
}
Example #21
Source File: index.js    From semana-omnistack-11 with MIT License 4 votes vote down vote up
export default function Incidents() {
  const [incidents, setIncidents] = useState([]);
  const [total, setTotal] = useState(0);

  const [page, setPage] = useState(1);
  const [loading, setLoading] = useState(false);

  const navigation = useNavigation();

  function navigateToDetail(incident) {
    navigation.navigate('Detail', { incident });
  }

  async function loadIncidents() {
    if (loading) {
      return;
    }

    if (total > 0 && incidents.length === total) {
      return;
    }

    setLoading(true);

    const response = await api.get('incidents', {
      params: { page }
    });

    setIncidents([...incidents, ...response.data]);
    setTotal(response.headers['x-total-count']);
    setPage(page + 1);
    setLoading(false);
  }

  useEffect(() => {
    loadIncidents();
  }, []);
 
  return (
    <View style={styles.container}>
      <View style={styles.header}>
        <Image source={logoImg} />
        <Text style={styles.headerText}>
          Total de <Text style={styles.headerTextBold}>{total} casos</Text>.
        </Text>
      </View>

      <Text style={styles.title}>Bem-vindo!</Text>
      <Text style={styles.description}>Escolha um dos casos abaixo e salve o dia.</Text>

      <FlatList
        data={incidents}
        style={styles.incidentList}
        keyExtractor={incident => String(incident.id)}
        // showsVerticalScrollIndicator={false}
        onEndReached={loadIncidents}
        onEndReachedThreshold={0.2}
        renderItem={({ item: incident }) => (
          <View style={styles.incident}>
            <Text style={styles.incidentProperty}>ONG:</Text>
            <Text style={styles.incidentValue}>{incident.name}</Text>

            <Text style={styles.incidentProperty}>CASO:</Text>
            <Text style={styles.incidentValue}>{incident.title}</Text>

            <Text style={styles.incidentProperty}>VALOR:</Text>
            <Text style={styles.incidentValue}>
              {Intl.NumberFormat('pt-BR', { 
                style: 'currency', 
                currency: 'BRL' 
              }).format(incident.value)}
            </Text>

            <TouchableOpacity 
              style={styles.detailsButton} 
              onPress={() => navigateToDetail(incident)}
            >
              <Text style={styles.detailsButtonText}>Ver mais detalhes</Text>
              <Feather name="arrow-right" size={16} color="#E02041" />            
            </TouchableOpacity>
          </View>
        )}
      />
    </View>
  );
}
Example #22
Source File: ReviewTopBar.js    From juken with MIT License 4 votes vote down vote up
ReviewTopBar = ({
  submissionQueue,
  submissionErrors,
  ignoreSubmissionErrors,
  retrySubmission,
  isQueueClear,
  setMenuOpen,
}) => {
  
  const isInternetReachable = useNetworkListener();
  const { showActionSheetWithOptions } = useActionSheet();

  const uploadSuccess = submissionQueue.length === 0;
  const uploadFail = submissionErrors.length > 0;
  const uploadQueue = submissionQueue.length;
  const uploadErrors = submissionErrors.length;

  let badgeColor = uploadFail
    ? theme.palette.red
    : 'rgba(0, 0, 0, .1)';
  
  let badgeIcon = null;
  if (uploadSuccess) badgeIcon = <AntDesign name="check" size={10} color="white" />;
  if (uploadQueue > 0) badgeIcon = <AntDesign name="arrowup" size={10} color="white" />;

  let badgeText = null;
  if (uploadQueue > 0) badgeText = uploadQueue;
  if (uploadFail) badgeText = uploadErrors;

  return (
    <>

      {/** top bar */}
      <TopBar
        style={styles.wrapper}
        centerText={isQueueClear ? '' : 'Reviews'}
        left={<Entypo name="menu" size={20} color="white" />}
        leftOnPress={() => setMenuOpen(true)}
        right={
          <>
            {!isInternetReachable && (
              <Badge
                style={{ marginRight: 6, backgroundColor: theme.palette.red }}
                icon={<Feather name="wifi-off" size={10} color="white" />}
              />
            )}
            <Badge
              style={{ backgroundColor: badgeColor }}
              text={badgeText}
              icon={badgeIcon}
            />
          </>
        }
        rightOnPress={!uploadFail ? null : () => {
          showActionSheetWithOptions({
              options: [
                'Cancel',
                'Retry',
                'Ignore',
              ],
              destructiveButtonIndex: 2,
              title: `Failed to submit ${uploadErrors} review${uploadErrors === 1 ? '' : 's'}`,
              message: (
                'You can retry submission after making sure your device ' +
                'has an active internet connection. If you submitted the reviews ' +
                'from another application, please use the Ignore button to dismiss ' +
                'the errors.'
              ),
            }, buttonIndex => {
              if (buttonIndex === 1) {
                retrySubmission();
              } else if (buttonIndex === 2) {
                dialog({
                  webTitle: 'Unless you submitted your reviews elsewhere, your unsubmitted reviews will be lost. Are you sure ?',
                  mobileTitle: 'Are you sure ?',
                  mobileMessage: 'Unless you submitted your reviews elsewhere, your unsubmitted reviews will be lost.',
                  onConfirm: ignoreSubmissionErrors
                });
              }
            })
        }}
      />
    </>
  )
}
Example #23
Source File: index.js    From be-the-hero with MIT License 4 votes vote down vote up
export default function Incidents() {
  const [ incidents, setIncidents ] = useState([])
  const [ totalIncidents, setTotalIncidents ] = useState(0)
  const [ currentPage, setCurrentPage ] = useState(1)
  const [ loading, setLoading ] = useState(false)

  const navigation = useNavigation()

  function navigateToDetails(incident) {
    navigation.navigate('Details', { incident })
  }

  async function loadIncidents() {
    if (loading) {
      return
    }

    if (totalIncidents > 0 && incidents.length === totalIncidents) {
      return
    }

    setLoading(true)

    const response = await api.get('/incidents', {
      params: {
        page: currentPage
      }
    })

    setIncidents([ ...incidents, ...response.data ])
    setTotalIncidents(response.headers['X-Total-Count'])

    setCurrentPage(currentPage + 1)
    setLoading(false)
  }

  useEffect(() => {
    loadIncidents()
  }, [])

  return (
    <View style={ styles.incidentsContainer }>

      <View style={ styles.headerContainer }>
        <Image source={ logoImage }/>
        <Text style={ styles.headerText }>
          Total de <Text style={ styles.headerTextBold }>{ totalIncidents }</Text> casos
        </Text>
      </View>
      <Text style={ styles.mainTitle }>Bem-vindo</Text>
      <Text style={ styles.mainDescription }>Escolha um dos casos abaixo e salve o dia!</Text>
      
      <FlatList style={ styles.incidentsList } data={ incidents }
        keyExtractor={ incident => String(incident.id) }
        showsVerticalScrollIndicator={ false }
        onEndReached={ loadIncidents }
        onEndReachedThreshold={ 0.2 }
        renderItem={({ item: incident }) => (

        <View style={ styles.incident }>

          <Text style={ styles.incidentOng }>
            { incident.name } de { incident.city }/{ incident.uf }
          </Text>

          <Text style={ styles.incidentDescription }>
            { incident.description }
          </Text> 

          <Text style={ styles.incidentValue }>
          {
            Intl.NumberFormat('pt-BR', {
              style: 'currency',
              currency: 'BRL'
            }).format(incident.value)
          }
          </Text>

          <TouchableOpacity onPress={ () => navigateToDetails(incident) }
            style={ styles.incidentButton }>
            <Text style={ styles.incidentButtonText }>Ver mais detalhes</Text>
            <Feather name="arrow-right" size={ 16 } color="#E02041"/>
          </TouchableOpacity>
        </View>
      )}/>

    </View>
  )
}
Example #24
Source File: index.js    From be-the-hero with MIT License 4 votes vote down vote up
export default function Incidents(){
    const [incidents, setIncidents] = useState([]);
    const [total, setTotal] = useState(0);

    const [page, setPage] = useState(1);
    const [loading, setLoading] = useState(false);

    const navigation = useNavigation();

    function navigateToDetail(incident){
        navigation.navigate('Detail', { incident });
    }

    async function loadIcidents(){
        if (loading) {
            return;
        }

        if (total > 0 && incidents.length === total ) {
            return;
        }

        setLoading(true);

        const response = await api.get('incidents', { 
            params: { page }
        });

        setIncidents([...incidents, ...response.data]);
        setTotal(response.headers['x-total-count']);
        setPage(page + 1);
        setLoading(false);
    }

    useEffect(() => {
        loadIcidents();
    }, []);

    return(
        <View style={styles.container}>
            <View style={styles.header}>
                <Image source={logoImg} />
                <Text style={styles.headerText}>
                    Total de <Text style={styles.headerTextBold}>{total} casos</Text>.
                </Text>
            </View>

            <Text style={styles.title}>Bem-vindo!</Text>
            <Text style={styles.description}>Escolha um dos casos abaixo e salve o dia.</Text>


            <FlatList  
                data={incidents}
                style={styles.incidentList}
                keyExtractor={incident => String(incident.id)}
                showsVerticalScrollIndicator={false}
                onEndReached={loadIcidents}
                onEndReachedThreshold={0.2}
                renderItem={({ item: incident }) => (
                    <View style={styles.incident}>
                    <Text style={styles.incidentProperty}>ONG:</Text>
                    <Text style={styles.incidentValue}>{incident.name}</Text>

                    <Text style={styles.incidentProperty}>CASO:</Text>
                    <Text style={styles.incidentValue}>{incident.title}</Text>

                    <Text style={styles.incidentProperty}>VALOR:</Text>
                    <Text style={styles.incidentValue}>
                        {Intl.NumberFormat('pt-BR', { 
                            style: 'currency', 
                            currency: 'BRL' }).format(incident.value)}</Text>

                    <TouchableOpacity style={styles.detailsButton} onPress={() => navigateToDetail(incident)}>
                        <Text style={styles.detailsButtonText}>Ver mais detalhes</Text>
                        <Feather name="arrow-right" size={16} color="#e02041" />
                    </TouchableOpacity>
                </View>

                )}
            />
        </View>
    );
}
Example #25
Source File: index.js    From be-the-hero with MIT License 4 votes vote down vote up
export default function Detail() {
  const navigation = useNavigation();
  const route = useRoute();

  const incident = route.params.incident;
  const messege = `Olá ${
    incident.name
  }, estou entrando em contato pois gostaria de ajudar no caso "${
    incident.title
  }" com o valor de ${Intl.NumberFormat('pt-BR', {
    style: 'currency',
    currency: 'BRL',
  }).format(incident.value)}`;

  function navigateBack() {
    navigation.goBack();
  }

  function sendMail() {
    MailComposer.composeAsync({
      subject: `Herói do caso: ${incident.title}`,
      recipients: [incident.email],
      body: messege,
    });
  }

  function sendWhatsapp() {
    Linking.openURL(
      `whatsapp://send?phone=+55${incident.whatsapp}&text=${messege}`
    );
  }

  return (
    <View style={styles.container}>
      <View style={styles.header}>
        <Image source={logoImg} />

        <TouchableOpacity onPress={navigateBack}>
          <Feather name="arrow-left" size={20} color="#E02041" />
        </TouchableOpacity>
      </View>

      <View style={styles.incident}>
        <Text style={[styles.incidentProperty, { marginTop: 0 }]}>ONG:</Text>
        <Text style={styles.incidentValue}>
          {incident.name} de {incident.city}/{incident.uf}
        </Text>

        <Text style={styles.incidentProperty}>CASO:</Text>
        <Text style={styles.incidentValue}>{incident.description}</Text>

        <Text style={styles.incidentProperty}>VALOR:</Text>
        <Text style={styles.incidentValue}>
          {Intl.NumberFormat('pt-BR', {
            style: 'currency',
            currency: 'BRL',
          }).format(incident.value)}
        </Text>
      </View>

      <View style={styles.contactBox}>
        <Text style={styles.heroTitle}>Salve o dia!</Text>
        <Text style={styles.heroTitle}>Seja o herói desse caso.</Text>

        <Text style={styles.heroDescription}>Entre em contato:</Text>

        <View style={styles.actions}>
          <TouchableOpacity style={styles.action} onPress={sendWhatsapp}>
            <FontAwesome name="whatsapp" size={20} color="#fff" />
            <Text style={styles.actionText}>Whatsapp</Text>
          </TouchableOpacity>

          <TouchableOpacity style={styles.action} onPress={sendMail}>
            <Icon name="mail" size={20} color="#fff" />
            <Text style={styles.actionText}>Email</Text>
          </TouchableOpacity>
        </View>
      </View>
    </View>
  );
}
Example #26
Source File: ViewProfile.js    From salyd with GNU General Public License v3.0 4 votes vote down vote up
ViewProfile = ({ navigation }) => {

    const { user, token } = useContext(GlobalContext);
    const { _id, name, email, phone, image } = user;

    const logout = async () => {
        const token = await AsyncStorage.removeItem("token")
        const user = await AsyncStorage.removeItem("user")
        if (!token) {
            navigation.replace("Login");
        }
    }
    console.log(image)

    return (
        <React.Fragment>
            <Header navigation={navigation} isBack> View Profile </Header>
            <View style={styles.root}>
                <View style={{ alignItems: "center" }}>
                    <Image
                        style={{ width: 140, height: 140, borderRadius: 140 / 2, marginTop: 50 }}
                        source={{ uri: (image ? image : "https://sanjaymotels.com/wp-content/uploads/2019/01/testimony.png") }}
                    />

                </View>
                <View style={{ alignItems: "center", margin: 15 }}>
                    <Text style={styles.name}> {name} </Text>
                </View>

                <TouchableOpacity onPress={() => {
                    navigation.navigate('EditProfile', {
                        name, email, phone
                    })
                }}>
                    <View style={styles.cardContainer}>
                        <View style={styles.mycard}>
                            <View style={styles.cardContent}>
                                <FontAwesome name="user-circle" style={styles.icon} />
                                <Text style={styles.mytext}>Account Details</Text>
                                <Ionicons name="ios-arrow-forward" style={styles.arrow} />
                            </View>
                        </View>
                    </View>
                </TouchableOpacity>

                <TouchableOpacity onPress={() => {
                    navigation.navigate('RecentOrders')
                }}>
                    <View style={styles.mycard}>
                        <View style={styles.cardContent}>
                            <MaterialCommunityIcons name="food-variant" style={styles.icon} />
                            <Text style={styles.mytext}>Order History</Text>
                            <Ionicons name="ios-arrow-forward" style={styles.arrow} />
                        </View>
                    </View>
                </TouchableOpacity>


                <TouchableOpacity onPress={() => {
                    navigation.navigate('Contact')
                }}>

                    <View style={styles.mycard} onPress={() => {
                        navigation.navigate('Contact')
                    }}>
                        <View style={styles.cardContent}>
                            <Feather name="phone-call" style={styles.icon} />
                            <Text style={styles.mytext}>Contact Us</Text>
                            <Ionicons name="ios-arrow-forward" style={styles.arrow} />
                        </View>
                    </View>
                </TouchableOpacity>

                <View style={{
                    justifyContent: "center",
                    alignItems: "center"
                }}>
                    <Button onPressFunction={() => logout()}>Logout</Button>
                </View>

            </View>
        </React.Fragment>
    )
}
Example #27
Source File: Edit.js    From InstagramClone with Apache License 2.0 4 votes vote down vote up
function Edit(props) {
    const [name, setName] = useState(props.currentUser.name);
    const [description, setDescription] = useState("");
    const [image, setImage] = useState(props.currentUser.image);
    const [imageChanged, setImageChanged] = useState(false);
    const [hasGalleryPermission, setHasGalleryPermission] = useState(null);

    const onLogout = async () => {
        firebase.auth().signOut();
        Updates.reloadAsync()
    }


    useEffect(() => {
        (async () => {
            if (props.currentUser.description !== undefined) {
                setDescription(props.currentUser.description)
            }

        })();
    }, []);

    useLayoutEffect(() => {
        props.navigation.setOptions({
            headerRight: () => (

                <Feather style={navbar.image} name="check" size={24} color="green" onPress={() => { console.log({ name, description }); Save() }} />
            ),
        });
    }, [props.navigation, name, description, image, imageChanged]);


    const pickImage = async () => {
        if (true) {
            let result = await ImagePicker.launchImageLibraryAsync({
                mediaTypes: ImagePicker.MediaTypeOptions.Images,
                allowsEditing: true,
                aspect: [1, 1],
                quality: 1,
            });

            if (!result.cancelled) {
                setImage(result.uri);
                setImageChanged(true);
            }
        }
    };


    const Save = async () => {
        if (imageChanged) {
            const uri = image;
            const childPath = `profile/${firebase.auth().currentUser.uid}`;

            const response = await fetch(uri);
            const blob = await response.blob();

            const task = firebase
                .storage()
                .ref()
                .child(childPath)
                .put(blob);

            const taskProgress = snapshot => {
                console.log(`transferred: ${snapshot.bytesTransferred}`)
            }

            const taskCompleted = () => {
                task.snapshot.ref.getDownloadURL().then((snapshot) => {

                    firebase.firestore().collection("users")
                        .doc(firebase.auth().currentUser.uid)
                        .update({
                            name,
                            description,
                            image: snapshot,
                        }).then(() => {
                            props.updateUserFeedPosts();
                            props.navigation.goBack()

                        })
                })
            }

            const taskError = snapshot => {
                console.log(snapshot)
            }

            task.on("state_changed", taskProgress, taskError, taskCompleted);
        } else {
            saveData({
                name,
                description,
            })
        }
    }

    const saveData = (data) => {
        firebase.firestore().collection("users")
            .doc(firebase.auth().currentUser.uid)
            .update(data).then(() => {
                props.updateUserFeedPosts();

                props.navigation.goBack()
            })
    }

    return (
        <View style={container.form}>

            <TouchableOpacity style={[utils.centerHorizontal, utils.marginBottom]} onPress={() => pickImage()} >
                {image == 'default' ?
                    (
                        <FontAwesome5
                            style={[utils.profileImageBig, utils.marginBottomSmall]}
                            name="user-circle" size={80} color="black" />
                    )
                    :
                    (
                        <Image
                            style={[utils.profileImageBig, utils.marginBottomSmall]}
                            source={{
                                uri: image
                            }}
                        />
                    )
                }
                <Text style={text.changePhoto}>Change Profile Photo</Text>
            </TouchableOpacity>

            <TextInput
                value={name}
                style={form.textInput}
                placeholder="Name"
                onChangeText={(name) => setName(name)}
            />
            <TextInput
                value={description}
                style={[form.textInput]}
                placeholderTextColor={"#e8e8e8"}
                placeholder="Description"
                onChangeText={(description) => { setDescription(description); }}
            />
            <Button
                title="Logout"
                onPress={() => onLogout()} />
        </View>

    )
}
Example #28
Source File: Post.js    From InstagramClone with Apache License 2.0 4 votes vote down vote up
function Post(props) {
    const [item, setItem] = useState(props.route.params.item)
    const [user, setUser] = useState(props.route.params.user)
    const [currentUserLike, setCurrentUserLike] = useState(false)
    const [unmutted, setUnmutted] = useState(true)
    const [videoref, setvideoref] = useState(null)
    const [sheetRef, setSheetRef] = useState(useRef(null))
    const [modalShow, setModalShow] = useState({ visible: false, item: null })
    const [isValid, setIsValid] = useState(true);
    const [exists, setExists] = useState(false);
    const [loaded, setLoaded] = useState(false);

    const isFocused = useIsFocused();
    useEffect(() => {

        if (props.route.params.notification != undefined) {

            firebase.firestore()
                .collection("users")
                .doc(props.route.params.user)
                .get()
                .then((snapshot) => {
                    if (snapshot.exists) {
                        let user = snapshot.data();
                        user.uid = snapshot.id;

                        setUser(user)
                    }
                })

            firebase.firestore()
                .collection("posts")
                .doc(props.route.params.user)
                .collection("userPosts")
                .doc(props.route.params.item)
                .get()
                .then((snapshot) => {
                    if (snapshot.exists) {
                        let post = snapshot.data();
                        post.id = snapshot.id;

                        setItem(post)
                        setLoaded(true)
                        setExists(true)
                    }
                })
            firebase.firestore()
                .collection("posts")
                .doc(props.route.params.user)
                .collection("userPosts")
                .doc(props.route.params.item)
                .collection("likes")
                .doc(firebase.auth().currentUser.uid)
                .onSnapshot((snapshot) => {
                    let currentUserLike = false;
                    if (snapshot.exists) {
                        currentUserLike = true;
                    }
                    setCurrentUserLike(currentUserLike)

                })

        }
        else {
            firebase.firestore()
                .collection("posts")
                .doc(props.route.params.user.uid)
                .collection("userPosts")
                .doc(props.route.params.item.id)
                .collection("likes")
                .doc(firebase.auth().currentUser.uid)
                .onSnapshot((snapshot) => {
                    let currentUserLike = false;
                    if (snapshot.exists) {
                        currentUserLike = true;
                    }
                    setCurrentUserLike(currentUserLike)

                })

            setItem(props.route.params.item)
            setUser(props.route.params.user)
            setLoaded(true)
            setExists(true)
        }

    }, [props.route.params.notification, props.route.params.item])

    useEffect(() => {
        if (videoref !== null) {
            videoref.setIsMutedAsync(props.route.params.unmutted)
        }
        setUnmutted(props.route.params.unmutted)
    }, [props.route.params.unmutted])

    useEffect(() => {
        if (videoref !== null) {
            if (isFocused) {
                videoref.playAsync()
            } else {
                videoref.stopAsync()

            }
        }

    }, [props.route.params.index, props.route.params.inViewPort])

    const onUsernamePress = (username, matchIndex) => {
        props.navigation.navigate("ProfileOther", { username, uid: undefined })
    }

    const onLikePress = (userId, postId, item) => {
        item.likesCount += 1;
        setCurrentUserLike(true)
        firebase.firestore()
            .collection("posts")
            .doc(userId)
            .collection("userPosts")
            .doc(postId)
            .collection("likes")
            .doc(firebase.auth().currentUser.uid)
            .set({})
            .then()
        props.sendNotification(user.notificationToken, "New Like", `${props.currentUser.name} liked your post`, { type: 0, postId, user: firebase.auth().currentUser.uid })

    }
    const onDislikePress = (userId, postId, item) => {
        item.likesCount -= 1;

        setCurrentUserLike(false)
        firebase.firestore()
            .collection("posts")
            .doc(userId)
            .collection("userPosts")
            .doc(postId)
            .collection("likes")
            .doc(firebase.auth().currentUser.uid)
            .delete()
    }
    if (!exists && loaded) {
        return (
            <View style={{ height: '100%', justifyContent: 'center', margin: 'auto' }}>
                <FontAwesome5 style={{ alignSelf: 'center', marginBottom: 20 }} name="dizzy" size={40} color="black" />
                <Text style={[text.notAvailable]}>Post does not exist</Text>
            </View>
        )
    }
    if (!loaded) {
        return (<View></View>)

    }
    if (user == undefined) {
        return (<View></View>)
    }
    if (item == null) {
        return (<View />)
    }

    const _handleVideoRef = (component) => {
        setvideoref(component);

        if (component !== null) {
            component.setIsMutedAsync(props.route.params.unmutted)
        }
    }

    if (videoref !== null) {
        videoref.setIsMutedAsync(unmutted)
        if (isFocused && props.route.params.index == props.route.params.inViewPort) {
            videoref.playAsync()
        } else {
            videoref.stopAsync()

        }
    }


    if (sheetRef.current !== null && !props.route.params.feed) {
        if (modalShow.visible) {
            sheetRef.snapTo(0)
        } else {
            sheetRef.snapTo(1)
        }
    }

    return (
        <View style={[container.container, utils.backgroundWhite]}>

            <View>
                <View style={[container.horizontal, { alignItems: 'center', padding: 10 }]}>
                    <TouchableOpacity
                        style={[container.horizontal, { alignItems: 'center' }]}
                        onPress={() => props.navigation.navigate("ProfileOther", { uid: user.uid, username: undefined })}>

                        {user.image == 'default' ?
                            (
                                <FontAwesome5
                                    style={[utils.profileImageSmall]}
                                    name="user-circle" size={35} color="black" />

                            )
                            :
                            (
                                <Image
                                    style={[utils.profileImageSmall]}
                                    source={{
                                        uri: user.image
                                    }}
                                />
                            )
                        }
                        <View style={{ alignSelf: 'center' }}>
                            <Text style={[text.bold, text.medium, { marginBottom: 0 }]} >{user.name}</Text>
                        </View>

                    </TouchableOpacity>

                    <TouchableOpacity
                        style={[{ marginLeft: 'auto' }]}

                        onPress={() => {
                            if (props.route.params.feed) {
                                props.route.params.setModalShow({ visible: true, item })
                            } else {
                                setModalShow({ visible: true, item })
                            }
                        }}>
                        <Feather
                            name="more-vertical" size={20} color="black" />
                    </TouchableOpacity>
                </View>
                {item.type == 0 ?
                    <View>
                        {props.route.params.index == props.route.params.inViewPort && isFocused ?
                            <View>
                                <VideoPlayer
                                    videoProps={{
                                        isLooping: true,
                                        shouldPlay: true,
                                        resizeMode: Video.RESIZE_MODE_COVER,
                                        source: {
                                            uri: item.downloadURL,
                                        },
                                        videoRef: _handleVideoRef,
                                    }}
                                    inFullscreen={false}
                                    showControlsOnLoad={true}
                                    showFullscreenButton={false}
                                    height={WINDOW_WIDTH}
                                    width={WINDOW_WIDTH}
                                    shouldPlay={true}
                                    isLooping={true}
                                    style={{
                                        aspectRatio: 1 / 1, height: WINDOW_WIDTH,
                                        width: WINDOW_WIDTH, backgroundColor: 'black'
                                    }}
                                />

                                <TouchableOpacity
                                    style={{ position: 'absolute', borderRadius: 500, backgroundColor: 'black', width: 40, height: 40, alignItems: 'center', justifyContent: 'center', margin: 10, right: 0 }}
                                    activeOpacity={1}
                                    onPress={() => {
                                        if (videoref == null) {
                                            return;
                                        }
                                        if (unmutted) {
                                            if (props.route.params.setUnmuttedMain == undefined) {
                                                setUnmutted(false)
                                            } else {
                                                props.route.params.setUnmuttedMain(false)

                                            }

                                        } else {
                                            if (props.route.params.setUnmuttedMain == undefined) {
                                                setUnmutted(true)
                                            } else {
                                                props.route.params.setUnmuttedMain(true)

                                            }

                                        }

                                    }}>
                                    {!unmutted ?

                                        <Feather name="volume-2" size={20} color="white" />
                                        :
                                        <Feather name="volume-x" size={20} color="white" />
                                    }
                                </TouchableOpacity>

                            </View>

                            :
                            <View style={{ marginTop: 4 }}>

                                <CachedImage
                                    cacheKey={item.id}
                                    style={[container.image]}
                                    source={{ uri: item.downloadURLStill }}
                                />
                            </View>
                        }

                    </View>

                    :

                    <CachedImage
                        cacheKey={item.id}
                        style={container.image}
                        source={{ uri: item.downloadURL }}
                    />
                }

                <View style={[utils.padding10, container.horizontal]}>
                    {currentUserLike ?
                        (
                            <Entypo name="heart" size={30} color="red" onPress={() => onDislikePress(user.uid, item.id, item)} />
                        )
                        :
                        (
                            <Feather name="heart" size={30} color="black" onPress={() => onLikePress(user.uid, item.id, item)} />

                        )
                    }
                    <Feather style={utils.margin15Left} name="message-square" size={30} color="black" onPress={() => props.navigation.navigate('Comment', { postId: item.id, uid: user.uid, user })} />
                    <Feather style={utils.margin15Left} name="share" size={26} color="black" onPress={() => props.navigation.navigate('ChatList', { postId: item.id, post: { ...item, user: user }, share: true })} />


                </View>
                <View style={[container.container, utils.padding10Sides]}>
                    <Text style={[text.bold, text.medium]}>
                        {item.likesCount} likes
                    </Text>
                    <Text style={[utils.margin15Right, utils.margin5Bottom]}>
                        <Text style={[text.bold]}
                            onPress={() => props.navigation.navigate("ProfileOther", { uid: user.uid, username: undefined })}>
                            {user.name}
                        </Text>

                        <Text>    </Text>
                        <ParsedText
                            parse={
                                [
                                    { pattern: /@(\w+)/, style: { color: 'green', fontWeight: 'bold' }, onPress: onUsernamePress },
                                ]
                            }
                        >{item.caption}</ParsedText>

                    </Text>
                    <Text
                        style={[text.grey, utils.margin5Bottom]} onPress={() => props.navigation.navigate('Comment', { postId: item.id, uid: user.uid, user })}>
                        View all {item.commentsCount} Comments
                    </Text>
                    <Text
                        style={[text.grey, text.small, utils.margin5Bottom]}>
                        {timeDifference(new Date(), item.creation.toDate())}
                    </Text>
                </View>
            </View>

            <BottomSheet
                bottomSheerColor="#FFFFFF"
                ref={setSheetRef}
                initialPosition={0} //200, 300
                snapPoints={[300, 0]}
                isBackDrop={true}
                isBackDropDismissByPress={true}
                isRoundBorderWithTipHeader={true}
                backDropColor="black"
                isModal
                containerStyle={{ backgroundColor: "white" }}
                tipStyle={{ backgroundColor: "white" }}
                headerStyle={{ backgroundColor: "white", flex: 1 }}
                bodyStyle={{ backgroundColor: "white", flex: 1, borderRadius: 20 }}
                body={

                    <View>

                        {modalShow.item != null ?
                            <View>
                                <TouchableOpacity style={{ padding: 20 }}
                                    onPress={() => {
                                        props.navigation.navigate("ProfileOther", { uid: modalShow.item.user.uid, username: undefined });
                                        setModalShow({ visible: false, item: null });
                                    }}>
                                    <Text >Profile</Text>
                                </TouchableOpacity>
                                <Divider />
                                {props.route.params.user.uid == firebase.auth().currentUser.uid ?
                                    <TouchableOpacity style={{ padding: 20 }}
                                        onPress={() => {
                                            props.deletePost(modalShow.item).then(() => {
                                                props.fetchUserPosts()
                                                props.navigation.popToTop()
                                            })
                                            setModalShow({ visible: false, item: null });
                                        }}>
                                        <Text >Delete</Text>
                                    </TouchableOpacity>
                                    : null}

                                <Divider />
                                <TouchableOpacity style={{ padding: 20 }} onPress={() => setModalShow({ visible: false, item: null })}>
                                    <Text >Cancel</Text>
                                </TouchableOpacity>
                            </View>
                            : null}

                    </View>
                }
            />
            <Snackbar
                visible={isValid.boolSnack}
                duration={2000}
                onDismiss={() => { setIsValid({ boolSnack: false }) }}>
                {isValid.message}
            </Snackbar>
        </View>
    )
}
Example #29
Source File: Save.js    From InstagramClone with Apache License 2.0 4 votes vote down vote up
function Save(props) {
    const [caption, setCaption] = useState("")
    const [uploading, setUploading] = useState(false)
    const [error, setError] = useState(false)
    const [data, setData] = useState("")
    const [keyword, setKeyword] = useState("")


    useLayoutEffect(() => {
        props.navigation.setOptions({
            headerRight: () => (
                <Feather style={navbar.image} name="check" size={24} color="green" onPress={() => { uploadImage() }} />
            ),
        });
    }, [caption]);

    const uploadImage = async () => {
        if (uploading) {
            return;
        }
        setUploading(true)
        let downloadURLStill = null
        let downloadURL = await SaveStorage(props.route.params.source, `post/${firebase.auth().currentUser.uid}/${Math.random().toString(36)}`)

        if (props.route.params.imageSource != null) {
            downloadURLStill = await SaveStorage(props.route.params.imageSource, `post/${firebase.auth().currentUser.uid}/${Math.random().toString(36)}`)
        }

        savePostData(downloadURL, downloadURLStill);

    }

    const SaveStorage = async (image, path) => {
        if (image == 'default') {
            return '';
        }

        const fileRef = firebase.storage().ref()
            .child(path);

        const response = await fetch(image);
        const blob = await response.blob();

        const task = await fileRef.put(blob);

        const downloadURL = await task.ref.getDownloadURL();

        return downloadURL;
    }
    const savePostData = (downloadURL, downloadURLStill) => {
        let object = {
            downloadURL,
            caption,
            likesCount: 0,
            commentsCount: 0,
            type: props.route.params.type,
            creation: firebase.firestore.FieldValue.serverTimestamp()
        }
        if (downloadURLStill != null) {
            object.downloadURLStill = downloadURLStill
        }

        firebase.firestore()
            .collection('posts')
            .doc(firebase.auth().currentUser.uid)
            .collection("userPosts")
            .add(object).then((result) => {
                props.fetchUserPosts()
                props.navigation.popToTop()
            }).catch((error) => {
                setUploading(false)
                setError(true)
            })

        var pattern = /\B@[a-z0-9_-]+/gi;
        let array = caption.match(pattern);

        if (array !== null) {

            for (let i = 0; i < array.length; i++) {
                firebase.firestore()
                    .collection("users")
                    .where("username", "==", array[i].substring(1))
                    .get()
                    .then((snapshot) => {

                        snapshot.forEach((doc) => {
                            props.sendNotification(doc.data().notificationToken, "New tag", `${props.currentUser.name} Tagged you in a post`, { type: 0, user: firebase.auth().currentUser.uid })

                        });
                    })
            }
        }


    }

    const renderSuggestionsRow = ({ item }, hidePanel) => {
        return (
            <TouchableOpacity onPress={() => onSuggestionTap(item.username, hidePanel)}>
                <View style={styles.suggestionsRowContainer}>
                    <View style={styles.userIconBox}>
                        <Image
                            style={{ aspectRatio: 1 / 1, height: 45 }}
                            source={{
                                uri: item.image
                            }}
                        />
                    </View>
                    <View style={styles.userDetailsBox}>
                        <Text style={styles.displayNameText}>{item.name}</Text>
                        <Text style={styles.usernameText}>@{item.username}</Text>
                    </View>
                </View>
            </TouchableOpacity>
        )
    }

    const onSuggestionTap = (username, hidePanel) => {
        hidePanel();
        const comment = caption.slice(0, - keyword.length)
        setCaption(comment + '@' + username + " ");
    }


    const callback = (keyword) => {
        setKeyword(keyword)
        firebase.firestore()
            .collection("users")
            .where("username", ">=", keyword.substring(1))
            .limit(10)
            .get()
            .then((snapshot) => {
                let result = snapshot.docs.map(doc => {

                    const data = doc.data();
                    const id = doc.id;
                    return { id, ...data }
                });
                setData(result)

            })
    }
    return (
        <View style={[container.container, utils.backgroundWhite]}>
            {uploading ? (

                <View style={[container.container, utils.justifyCenter, utils.alignItemsCenter]}>
                    <ActivityIndicator style={utils.marginBottom} size="large" />
                    <Text style={[text.bold, text.large]}>Upload in progress...</Text>
                </View>
            ) : (
                <View style={[container.container]}>
                    <View style={[container.container, utils.backgroundWhite, utils.padding15]}>

                        <View style={[{ marginBottom: 20, width: '100%' }]}>


                            <MentionsTextInput

                                textInputStyle={{ borderColor: '#ebebeb', borderWidth: 1, padding: 5, fontSize: 15, width: '100%' }}
                                suggestionsPanelStyle={{ backgroundColor: 'rgba(100,100,100,0.1)' }}
                                loadingComponent={() => <View style={{ flex: 1, width: 200, justifyContent: 'center', alignItems: 'center' }}><ActivityIndicator /></View>}
                                textInputMinHeight={30}
                                textInputMaxHeight={80}
                                trigger={'@'}
                                triggerLocation={'new-word-only'} // 'new-word-only', 'anywhere'
                                value={caption}
                                onChangeText={setCaption}
                                triggerCallback={callback.bind(this)}
                                renderSuggestionsRow={renderSuggestionsRow.bind(this)}
                                suggestionsData={data}
                                keyExtractor={(item, index) => item.username}
                                suggestionRowHeight={45}
                                horizontal={true}
                                MaxVisibleRowCount={3}
                            />
                        </View>
                        <View>
                            {props.route.params.type ?

                                <Image
                                    style={container.image}
                                    source={{ uri: props.route.params.source }}
                                    style={{ aspectRatio: 1 / 1, backgroundColor: 'black' }}
                                />

                                :

                                <Video
                                    source={{ uri: props.route.params.source }}
                                    shouldPlay={true}
                                    isLooping={true}
                                    resizeMode="cover"

                                    style={{ aspectRatio: 1 / 1, backgroundColor: 'black' }}
                                />
                            }
                        </View>

                    </View>
                    <Snackbar
                        visible={error}
                        duration={2000}
                        onDismiss={() => setError(false)}>
                        Something Went Wrong!
                    </Snackbar>
                </View>
            )}

        </View>

    )
}