Aplicativo gerador de tokens

⚙️ Configuração inicial

Vamos criar o projeto do nosso aplicativo usando o prompt de comando ou o Terminal do VSCODE

Crie um repositório vinculado ao Github (Eu criei uma pasta no C: chamada app)

Untitled-3 Aplicativo gerador de tokens

Dentro do terminal do VSCode ou pelo prompt de comando, vamos criar o projeto usando a plataforma expo, mas antes podemos atualizar o node package manager Veja imagens e copie o código.

npm install -g npm@latest
Untitled-1-1 Aplicativo gerador de tokens
Untitled-2-1 Aplicativo gerador de tokens
npx create-expo-app geradorToken

Após a criação do projeto Expo, é interessante fazer um commit no Github para versionar essa etapa.

Abrir o Android Studio e abrir o emulador de smartphone Android.

De volta ao CMD ou ao VSCode, vamos iniciar o servidor Expo para disponibilizar o projeto através do IP da nossa conexão. Repare que devemos estar dentro da pasta do projeto Expo.

Untitled-3-1 Aplicativo gerador de tokens

Antes de iniciar o expo, podemos adicionar uma atualização que permitirá emular o app na web também:

npx expo install react-native-web react-dom @expo/metro-runtime

Vou deixar aqui também a sintaxe que atualiza o expo caso necessário:

npx expo install expo@latest

E por fim, vamos iniciar o projeto expo com o seguinte código:

npx expo start
expo_web Aplicativo gerador de tokens

A tela acima deverá surgir e então os seguintes caminhos poderão ser seguidos

pressionar a tecla “a” irá instalar e abrir o app Expo GO no smartphone virtualizado. pressionar a tecla “w” irá abrir no localhost no seu navegador.

Scannear o QRCode com o app do Expo GO instalado no seu Smartphone. Obs. Seu Smartphone e seu computador deverão estar na mesma rede.

Caso ocorra algum erro, utilize o atalho Ctrl+C para interromper o servidor do expo e tente reiniciar usando o código anterior. Ao terminar o procedimento, sua tela deverá estar parecida com a imagem abaixo.

Untitled-4-1024x517 Aplicativo gerador de tokens

Possíveis problemas:

Problema associado ao ANDROID_HOME: reiniciar o computador ou refazer a variável de ambiente para sua conta.

npx expo start não inicia: executar a atualização do npm conforme abaixo:

npm install -g npm@latest

Problema Metro waiting on 127.0.0.1: nesse caso você pode tentar usar o tunnel para hospedar provisoriamente na web:

npx expo start --tunnel
#no primeiro acesso irá solicitar a instalação de uma dependência

Surgirá algo como esse exemplo:

Untitled-5 Aplicativo gerador de tokens

📱Front-end

Vamos iniciar nossa diagramação entendendo alguns conceitos

Vamos editar o arquivo App.js com o seguinte código:

export default function App() {
  return(
    
  )
};

Todo projeto react-native é um componente exportado. Vamos agora criar áreas de visualização:

export default function App() {
  return(
    <View>
      
    </View>
  )
}

Seria como se fosse uma <div> no html. Porém precisamos importar todos os componentes usados na função, logo precisamos inserir um código acima:

import { View } from "react-native";

Se eu quiser criar um texto dentro da view, vou usar a tag Text:

<Text>
	Meu app!
</Text>

Como dito anteriormente, se vou inserir uma nova tag, precisamos importar o recurso:

import { View, Text } from "react-native";

Para poder estilizar esse conteúdo, vamos criar uma constante que receberá a função create do método StyleSheet, que também deverá ser importado:

const estilos=StyleSheet.create({
  
})

Com a importação, o código completo deverá estar assim:

import { View, Text, StyleSheet } from "react-native";

export default function App() {
  return (
    <View>
      <Text>
        Meu app!
      </Text>
    </View>
  )
}

const estilos=StyleSheet.create({
  
})

Nesse momento, podemos estilizar qualquer item criado referenciando a constante declarada. Por exemplo:

<View style={estilos.container}>

Como definimos o container pertencendo ao estilos, então deveremos considera-lo na estilização:

const estilos=StyleSheet.create({
container:{
  
}
})

E assim podemos inserir elementos que estilizam nosso conteúdo e no final teremos essa concepção de código:

const estilos= StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#f3f3ff",
    justifyContent: 'center',
    alignItems: 'center'
  }
})

Para inserir uma imagem, vamos fazer download da figura png abaixo e salva-la dentro da pasta assets

Untitled-6 Aplicativo gerador de tokens
logo Aplicativo gerador de tokens

Em seguida, vamos inserir o seguinte código dentro da View:

<Image source={require("./assets/logo.png")} style={estilos.logo}/>

Naturalmente, se estamos usando uma nova tag, precisamos importa-la também:

import { View, Text, StyleSheet, Image } from "react-native";

Vamos inserir o estilo do logo também em nossa tabela de estilos:

const estilos= StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#f3f3ff",
    justifyContent: 'center',
    alignItems: 'center'
  },
  logo: {
    marginBottom: 60
  }
})

Repare que é necessário separar as referências por vírgula.

Bom, agora podemos criar outra tela e aprender a fazer o roteamento.

Vamos criar uma nova pasta na nossa estrutura e vamos criar dois arquivos.

Untitled-7 Aplicativo gerador de tokens

O index.js acabará sendo nossa página principal e o arquivo paginasenhas.js representará nossa segunda página.

Vamos trabalhar um pouco na nossa páginasenhas.js com o seguinte código:

// paginaSenhas.js

export function PaginaSenhas() {
    return (
        <SafeAreaView style={{ flex: 1 }}>
            <View>
                <Text>
                    Minhas senhas
                </Text>
            </View>
        </SafeAreaView>
    )
}

const estilos= StyleSheet.create({
    
})

Repare que uma nova tag surgiu, a SafeAreaView, que inclusive está recebendo uma estilização em linha.

Então deveremos resolver os imports:

import { View, StyleSheet, Text} from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";

Agora inserindo os estilos, ficamos com o código assim:

import { View, StyleSheet, Text} from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";

export function PaginaSenhas() {
    return (
        <SafeAreaView style={{ flex: 1 }}>
            <View style={estilos.header}>
                <Text style={estilos.title}>
                    Minhas senhas
                </Text>
            </View>
        </SafeAreaView>
    )
}

const estilos = StyleSheet.create({
    header: {
        padding: 14,
        paddingTop: 58,
        backgroundColor: "#392de9"
    },
    title: {
        fontSize: 18,
        fontWeight: "bold",
        color: "#FFF"
    }

})

Como o Expo somente reproduz aquilo que está no App.js exportado como default function, então não é possível ver como está ficando essa tela antes de fazer o roteamento.

Para podermos acompanhar as mudanças pelo servidor Expo no Android, vamos acertar as páginas index.js e App.js

Precisaremos copiar todo conteúdo do arquivo App.js para index.js alterando a linha export default function App() conforme abaixo:

export function Home() {

Para entender melhor o sistema de roteamento, vamos iniciar acessando o site abaixo:

https://reactnavigation.org/docs/getting-started

Precisaremos pegar o código de instalação da dependência, para assim poder fazer os imports.

npm install @react-navigation/native
npx expo install react-native-safe-area-context
npm install @react-navigation/bottom-tabs

Após instalação, vamos reiniciar o servidor Expo e criar o arquivo routes.js conforme imagem:

Untitled-8 Aplicativo gerador de tokens

No arquivo routes.js, iremos iniciar a base do roteamento com o seguinte código:

import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";

const Tab = createBottomTabNavigator();

export function Routes() {
    return (
        <Tab.Navigator>
            <Tab.Screen />
            <Tab.Screen />
        </Tab.Navigator>
    )
}

Em seguida, vamos informar o roteamento importando nossos componentes criados, ou seja, nossas páginas.

import { Home } from './pages/index'
import { PaginaSenhas } from './pages/paginaSenhas'

Vamos informar as páginas que cada botão deverá abrir:

<Tab.Screen name="home" component={Home}/>
<Tab.Screen name="paginaSenhas" component={PaginaSenhas}/>

O código do arquivo routes.js ficará assim:

import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import {Home} from './Pages/index';
import {PaginaSenhas} from './Pages/paginaSenhas';

const Tab = createBottomTabNavigator();

export function Routes() {
    return (
        <Tab.Navigator>
            <Tab.Screen name="home" component={Home}/>
            <Tab.Screen name="paginaSenhas" component={PaginaSenhas}/>
        </Tab.Navigator>
    )
}

Em seguida, vamos abrir o arquivo App.js, apagar o código e escrever como deve ser feito o direcionamento do Expo para o roteamento:

import { NavigationContainer } from '@react-navigation/native'
import { Routes } from './routes'

export default function App() {

  return (
    <NavigationContainer>
      <Routes />
    </NavigationContainer>
  )
}

Repare que agora temos duas possibilidades de navegação, porém ainda sem ícones.

Captura-de-tela-2024-04-24-215434 Aplicativo gerador de tokens

Vamos aprimorar a visibilidade do app usando a plataforma ionic:

https://ionic.io/ionicons

Captura-de-tela-2024-04-24-215651-1024x502 Aplicativo gerador de tokens

Para usar os ícones escolhidos, vamos importar dentro do arquivo routes.js:

import Ionicons from '@expo/vector-icons/Ionicons';

Em seguida, vamos adicionar as opções da Screen:

<Tab.Screen
name="home"
component={Home}
options={{
tabBarShowLabel: false,
headerShown: false,
tabBarIcon: () => {
return(<Ionicons name="home"/>)
}
}}
/>
<Tab.Screen
	name="paginaSenhas"
	component={PaginaSenhas}
	options={{
	tabBarShowLabel:false,
	headerShown: false,
	tabBarIcon: () => {
		return (<Ionicons name="lock-closed" />)
	 }
}}
/>

Os ícones aparecem porém sem estilização. Vamos aplicar algumas mudanças.

tabBarIcon: () => {
return(<Ionicons size={25} color={"#000"} name="home"/>)

O código acima permitirá estilizar o tamanho e a cor do ícone. Agora podemos aplicar um desvio condicional para dar um efeito na troca de telas:

tabBarIcon: ({focused}) => {
if (focused) {
return (<Ionicons size={25} color={"#000"} name="home" />)
}
return (<Ionicons size={20} color={"#000"} name="home-outline" />)
}

Em resumo, o código aplicado em ambas navegações ficou dessa forma:

<Tab.Screen
name="home"
component={Home}
options={{
tabBarShowLabel: false,
headerShown: false,
tabBarIcon: ({focused}) => {
if (focused) {
return (<Ionicons size={25} color={"#000"} name="home" />)
 }
return (<Ionicons size={20} color={"#000"} name="home-outline" />)
}
}}
/>
<Tab.Screen
name="paginaSenhas"
 component={PaginaSenhas}
options={{
tabBarShowLabel: false,
headerShown: false,
tabBarIcon: ({focused}) => {
if (focused) {
return (<Ionicons size={25} color={"#000"} name="lock-closed" />)
}
return (<Ionicons size={20} color={"#000"} name="lock-closed-outline" />)
}
}}
/>

Agora que o roteamento está funcionando com os ícones estabelecidos, vamos finalizar nossa tela principal, então vamos abrir o arquivo index.js.

Para adicionar o Slider, precisamos instalar uma nova dependência que se encontra nesse link:

https://docs.expo.dev/versions/latest/sdk/slider

npx expo install @react-native-community/slider

Após a instalação, vamos inserir uma nova View com o Slider interno logo após a tag Text, em nosso arquivo index.js:

<View>
  <Slider/>        
</View>

Como estamos usando uma nova tag, devemos importar seu componente também:

import Slider from "@react-native-community/slider";

Vamos então inserir uma customização ao slider, estilizando a View:

style={estilos.area}

Posteriormente, vamos adicionar os padrões de estilo. Não se esqueça de separar os elementos por vírgula.

area: {
    marginBottom: 14,
    marginTop: 14,
    width: "80%",
    backgroundColor: "#FFF",
    borderRadius: 8,
    padding: 8
  }

Defina uma altura para o Slider:

<Slider style={{ height: 50 }} />

Vamos adicionar mais parâmetros ao Slider para definir os valores limites:

minimumValue={6}
maximumValue={20}

Vamos também trabalhar em algumas cores atribuindo nos parâmetros:

minimumTrackTintColor="#ff0000"
maximumTrackTintColor="#000"
thumbTintColor="#392de9"

Agora vamos entender como funciona botões no react native inserindo o seguinte código após a View:

<TouchableOpacity style={estilos.button}>
        <Text style={estilos.buttonText}>
          Gerar Senha
        </Text>
      </TouchableOpacity>

Note que agora temos uma nova tag, e também temos novos estilos, sendo assim vamos continuar acrescentando a tag ao import:

import { View, StyleSheet, Text, Image, TouchableOpacity } from "react-native";

E definindo os estilos:

button: {
    backgroundColor: "#392de9",
    width: "80%",
    height: 50,
    justifyContent: 'center',
    alignItems: 'center',
    borderRadius: 8,
  },
  buttonText: {
    color: "#FFF"
  },

Agora podemos definir mais algumas propriedades do nosso slider. Primeiramente vamos entender o conceito do useState:

import { useState } from "react";

O useState é um Hook (função) em React que permite adicionar funcionalidade de estado em componentes funcionais. Ele retorna um par de valores: o estado atual e uma função que atualiza esse estado. Quando useState é chamado, aceita o estado inicial como argumento e retorna um array contendo o estado atual e uma função para atualizá-lo.

Vamos definir onde o texto será trocado inserindo o código abaixo no lugar da frase Meu app!:

{qtde} Caracteres

Vamos usar o useState para definir o valor padrão e seu controlador:

const [qtde, defineQtde] = useState(6)

E atribuir o parâmetro dentro do Slider:

value={qtde}

Agora vamos a parte mais importante, informar que a cada mudança no slider queremos que o valor da nossa variável se altere, inserindo o parâmetro a seguir:

onValueChange={(value) => defineQtde(value)}

Para manter apenas os números inteiros, vamos inserir a função toFixed à variável:

onValueChange={(value) => defineQtde(value.toFixed(0))}

Vamos estilizar o texto:

style={estilos.caracteres}
caracteres:{
    fontSize:30,
    fontWeight:"bold"
  }

Agora vamos construir a tela modal, criando uma pasta components em nosso projeto conforme imagem:

Untitled-9 Aplicativo gerador de tokens

Na tela modal.js, vamos inserir a seguinte estrutura:

import { View, StyleSheet} from "react-native";

export function ModalTokens() {
    return (
        <View style={estilos.container}>
            <View style={estilos.content}>

            </View>
        </View>
    )
}

Em seguida podemos estilizar esse conteúdo:

const estilos= StyleSheet.create({
    container: {
        backgroundColor: "rgba(25,25,25,0.6)",
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center'
    },
    content: {
        backgroundColor: "#FFF",
        width: "85%",
        paddingTop: 25,
        paddingBottom: 25,
        justifyContent: 'center',
        alignItems: 'center',
        borderRadius: 8,
    }

})

Para poder chamar essa tela modal no index.js, vamos realizar alguns procedimentos.

Primeiro, Vamos inserir o modal após no final do componente index.js usando o código abaixo:

<Modal>
</Modal>

Nosso objetivo é trazer o componente já criado para dentro do modal:

<Modal >
<ModalTokens/>
</Modal>

E adicionar as importações:

import { View, StyleSheet, Text, Image, TouchableOpacity, Modal } from "react-native";
import { ModalTokens } from '../components/modal';

Agora vamos criar o algoritmo que associa o botão Gerar senha a tela modal, adicionando um método useState que vai controlar a visibilidade:

const [telaModal, configTelaModal]=useState(false)

Vamos informar ao Modal que sua visibilidade será determinada pelo valor do useState e aproveitar para adicionar outros parâmetros:

 <Modal visible={telaModal} animationType="fade" transparent={true}>

E criar uma função que altera o valor do useState:

function gerarToken() {
configTelaModal(true);
}

Em seguida vamos associar a função ao botão:

<TouchableOpacity style={estilos.button} onPress={gerarToken}>

Note que agora podemos usar o botão para chamar o Modal. Porém não podemos voltar, pois não criamos a estrutura para isso. Vamos trabalhar mais no arquivo modal.js

<View style={estilos.container}>
            <View style={estilos.content}>
                <Text style={estilos.title}>
                    Senha Gerada
                </Text>
                <Pressable style={estilos.innerToken} >
                    <Text style={estilos.text}>
                        senha
                    </Text>
                </Pressable>
                <View style={estilos.buttonArea}>
                    <TouchableOpacity style={estilos.button} >
                        <Text style={estilos.buttonText}>
                            Voltar
                        </Text>
                    </TouchableOpacity>
                    <TouchableOpacity style={[estilos.button, estilos.buttonSave]} >
                        <Text style={estilos.buttonSaveText}>
                            Salvar Senha
                        </Text>
                    </TouchableOpacity>
                </View>
            </View>

        </View>

Vamos acertar os imports:

import { Text, View, StyleSheet, TouchableOpacity, Pressable } from "react-native";

E vamos usar o código abaixo para estilizar:

const estilos= StyleSheet.create({
    container: {
        backgroundColor: "rgba(25,25,25,0.6)",
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center'
    },
    content: {
        backgroundColor: "#FFF",
        width: "85%",
        paddingTop: 25,
        paddingBottom: 25,
        justifyContent: 'center',
        alignItems: 'center',
        borderRadius: 8,
    },
    title: {
        fontSize: 20,
        fontWeight: "bold",
        color: "#000",
        marginBottom: 25,
    },
    innerToken: {
        backgroundColor: "#0e0e0e",
        width: "85%",
        padding: 14,
        borderRadius: 8
    },
    text: {
        color: "#FFF",
        textAlign: "center"
    },
    buttonArea: {
        flexDirection: "row",
        width: "90%",
        marginTop: 8,
        alignItems: "center",
        justifyContent: "space-between"
    },
    button: {
        flex: 1,
        alignItems: "center",
        marginBottom: 14,
        marginTop: 14,
        margin:9,
        padding: 8,
        backgroundColor: "#EEEEEE",
        borderRadius: 8,
    },
    buttonSave: {
    
        backgroundColor: "#392DE9"
    },
    buttonSaveText: {
        color: "#FFF",
        fontWeight: "bold"
    }

})

Agora temos a chamada do overlay, mas não temos ainda a função voltar estabelecida:

Para fechar a tela modal, vamos passar o parâmetro que controla o modal no index.js

<ModalTokens fechar={()=> configTelaModal(false)} />

Em seguida, vamos associar ao botão Voltar no arquivo modal.js:

<TouchableOpacity style={estilos.button} onPress={fechar}>

E passar essa informação ao export function:

export function ModalTokens({fechar}) {

🔗Back-end

Vamos iniciar o Back-end do projeto criando um algoritmo para gerar os tokens

No arquivo index.js vamos declarar a seguinte variável para receber nossa lista de caracteres.

let caracteres = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

Agora vou aproveitar a função de abrir a tela modal para também gerar o token. A função ficará assim:

function gerarToken() {
        let token = ""
        for (let i = 0, n = caracteres.length; i < qtde; i++) {
            token += caracteres.charAt(Math.floor(Math.random() * n))
        }
        configTelaModal(true);
    }

A variável token receberá um caractere aleatório cada vez que passar pelo looping até que a quantidade definida no slider seja atingida.

Agora vamos usar o useState() para aplicar as senhas geradas:

const [tokenValue, configTokenValue] = useState("")

E vamos adicionar a instrução na função abrirModal para atualizar o useState:

configTokenValue(token);

Conferindo, a função abrirModal ficará assim:

function gerarToken() {
        let token = ""
        for (let i = 0, n = caracteres.length; i < qtde; i++) {
            token += caracteres.charAt(Math.floor(Math.random() * n))
        }
        configTokenValue(token);
        configTelaModal(true);
    }

Vamos atribuir ao modal o valor do token, deixando a sintaxe dessa forma:

<ModalTokens token={tokenValue} fechar={() => configTelaModal(false)} />

Agora que já informamos ao index que ele receberá um modal atribuído de uma função de fechar e um valor de token, vamos ao arquivo modal.js para consumir o valor do atributo token.

<Text style={estilos.text}>
    {token}
</Text>

E também não podemos esquecer de passar essa informação para o componente modalTokens:

export function ModalTokens({token, fechar}) {

Vamos agora permitir que usuários copiem o token gerado, adicionando um atributo ao texto dos tokens:

selectable={true}

Porém podemos utilizar um recurso mais avançado, que permite copiar para área de transferência.

Para isso vamos instalar um recurso do link abaixo:

https://docs.expo.dev/versions/latest/sdk/clipboard

npx expo install expo-clipboard

Com a dependência instalada, vamos editar o arquivo modal.js.

Primeiro vamos já realizar o import da dependência:

import * as Clipboard from 'expo-clipboard';

Agora vamos inserir a função assíncrona no componente:

async function copiarToken() {
        await Clipboard.setStringAsync(token)
        alert("Token copiado para área de transferência.")
        fechar();
    }

A função acima salva o token na área de transferência, gera um alert e depois fecha o modal.

Para finalizar, vamos instruir a área Pressable a chamada da função:

 <Pressable style={estilos.innerToken} onLongPress={copiarToken} >

Para que tudo funcione corretamente, vamos alterar o atributo anterior do texto:

selectable={false}

Feito isso, precisamos salvar as senhas. Vamos criar um Hook para determinar as regras de uso de armazenamento no app.

Primeiramente, vamos acessar a documentação expo:

https://docs.expo.dev/versions/latest/sdk/async-storage

Nele é possível encontrar a sintaxe de instalação:

npx expo install @react-native-async-storage/async-storage

Após a instalação, vamos montar a pasta hooks com o arquivo bancoTokens.js

Untitled-10 Aplicativo gerador de tokens

No arquivo bancoTokens.js, iremos iniciar importando a dependência:

import AsyncStorage from '@react-native-async-storage/async-storage';

Em seguida, vamos definir a estrutura padrão do componente:

export default function Armazenamento(){
    
return{

	}
}

A função Armazenamento terá 3 tarefas e o código ficará assim:

import AsyncStorage from '@react-native-async-storage/async-storage'

export default function Armazenamento() {

    return {
        obterItem,
        salvarItem,
        removerItem
    }
}

Agora, vamos precisar construir cada uma dessas tarefas. Todas serão funções assíncronas:

 async function obterItem() {
        try { }
        catch { }
    }

O código parcial ficará dessa forma:

import AsyncStorage from '@react-native-async-storage/async-storage'

export default function Armazenamento() {

    async function obterItem() {
        try { }
        catch { }
    }

    async function salvarItem() {
        try { }
        catch { }
    }

    async function removerItem() {
        try { }
        catch { }
    }

    return {
        obterItem,
        salvarItem,
        removerItem
    }
}

Vamos agora detalhar o funcionamento de cada algoritmo. Vamos considerar que a função irá trazer os itens ou retornará uma array vazia.

 async function obterItem(chave) {
        try {
            const tokens = await AsyncStorage.getItem(chave);
            return JSON.parse(tokens) || [];
         }

Para completar a função de obter Item, vamos elaborar o catch. A função completa ficará assim:

   async function obterItem(chave) {
        try {
            const tokens = await AsyncStorage.getItem(chave);
            return JSON.parse(tokens) || [];
        }

        catch (erro) {
            alert("Erro ao obter itens", erro)
            return [];
        }
    }

Agora vamos elaborar a função de salvar itens:

 async function salvarItem(chave, valor) {
        try {
            let tokens = await obterItem(chave);
            tokens.push(valor);
            await AsyncStorage.setItem(chave, JSON.stringify(tokens))

        } catch (erro) {
            alert("Erro ao salvar item", erro)
        }
    }

Para criar a função de remover o item, faremos uma construção diferente. Vamos atualizar nossos tokens filtrando aquele que queremos remover:

async function removerItem(chave, item) {
        try {
            let tokens = await obterItem(chave);
            let tokensAtualizados = tokens.filter((tokens) => {
                return (tokens !== item)
            })
                        
        } catch (erro) {
            alert("Erro ao remover item", erro)
        }
    }

A variável tokensAtualizados receberá todos os tokens salvos exceto aquele que escolhemos remover.

Para completar, precisamos atualizar nosso Armazenamento inserindo a seguinte instrução na função:

await AsyncStorage.setItem(chave, JSON.stringify(tokensAtualizados))
return tokensAtualizados;

Então, a função completa para remover um item fica assim:

async function removerItem(chave, item) {
        try {
            let tokens = await obterItem(chave);
            let tokensAtualizados = tokens.filter((tokens) => {
                return (tokens !== item)
            })
            await AsyncStorage.setItem(chave, JSON.stringify(tokensAtualizados))
            return tokensAtualizados;

        } catch (erro) {
            alert("Erro ao remover item", erro)
        }
    }

E assim terminamos o nosso hook para Armazenamento. Segue abaixo o código completo:

import AsyncStorage from '@react-native-async-storage/async-storage'

export default function Armazenamento() {

    async function obterItem(chave) {
        try {
            const tokens = await AsyncStorage.getItem(chave);
            return JSON.parse(tokens) || [];
        }

        catch (erro) {
            alert("Erro ao obter itens", erro)
            return [];
        }
    }

    async function salvarItem(chave, valor) {
        try {
            let tokens = await obterItem(chave);
            tokens.push(valor);
            await AsyncStorage.setItem(chave, JSON.stringify(tokens))

        } catch (erro) {
            alert("Erro ao salvar item", erro)
        }
    }

    async function removerItem(chave, item) {
        try {
            let tokens = await obterItem(chave);
            let tokensAtualizados = tokens.filter((token) => {
                return (token !== item)
            })
            await AsyncStorage.setItem(chave, JSON.stringify(tokensAtualizados))
            return tokensAtualizados;

        } catch (erro) {
            alert("Erro ao remover item", erro)
        }
    }

    return {
        obterItem,
        salvarItem,
        removerItem
    }
}

Vamos agora atribuir a função de salvar senha ao modal.js.

Primeiro vamos adicionar o import para consumir o Armazenamento:

import Armazenamento from '../hooks/bancoTokens'

Em seguida vamos informar na área de funções o uso da função salvarItem que vem lá do armazenamento:

const { salvarItem } = Armazenamento();

Em seguida, vamos criar a função assíncrona que salva, informa e fecha o modal:

async function salvarToken() {        
        await salvarItem("@token", token)
        alert(`Token ${token} salvo com sucesso`)
        fechar();
    }

Por fim, vamos atribuir a função ao botão de Salvar:

onPress={salvarToken}

📱 Pausa para um front-end ☕

Precisamos fazer o token salvo apareça na tela Minhas senhas, então vamos criar um novo componente que será usado para receber o token:

Untitled-11 Aplicativo gerador de tokens

No arquivo tokenView.js vamos montar a seguinte estrutura:

import React from "react";
import { Text, StyleSheet, Pressable } from "react-native";

export function CaixaToken() {
    return (
        <Pressable style={estilos.caixa}>
            <Text style={estilos.text}>
                Token salvo
            </Text>
        </Pressable>
    )
}

const estilos= StyleSheet.create({
    caixa:{
        backgroundColor:"#0e0e0e",
        padding: 14,
        width: "100%",
        marginBottom: 14,
        borderRadius:8,
        flexDirection:"row",
        alignItems:"center",
        justifyContent:"space-between"
    },
    text:{
        color:"#fff"
    }
})

🪄 De volta ao Back-end 🎩

Vamos agora trabalhar no arquivo paginaSenhas.js

Como queremos consumir o armazenamento, vamos iniciar realizando o import:

import Armazenamento from '../hooks/bancoTokens';

Também queremos trazer o componente CaixaToken que vai receber o texto.

import { CaixaToken } from '../components/tokenView';

Para poder manipular a lista de tokens com o useState, vamos inserir o import:

Considerando que iremos usar as funções obterItem e removerItem, vamos inicializa-las em nossa estrutura:

const { obterItem, removerItem } = Armazenamento();

/* entenda:
    const obterItem = Armazenamento().obterItem;
    const removerItem = Armazenamento().removerItem; 
    Não confunda o código acima com useState, pois trata-se de uma variável desestruturada
*/
import { useState } from 'react';

Assim podemos inserir a seguinte instrução:

const [listaTokens, defListaTokens] = useState([]);

Vamos fazer com que o carregamento dos tokens ocorra somente quando acessarmos a tela das minhas senhas. Para isso usaremos o useEffect, que irá solicitar o procedimento.

Para usar o useEffect, vamos adiciona-lo ao import:

import { useState, useEffect } from 'react'

Para conseguir determinar se a tela das minhas senhas está ativa ou não, vamos importar uma nova dependência:

import { useIsFocused } from '@react-navigation/native';

E então vamos inicializa-la também:

   const telaAtiva = useIsFocused();

Até o momento estamos com esse resultado no arquivo paginaSenhas:

import { View, StyleSheet, Text } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import Armazenamento from '../hooks/bancoTokens';
import { useState, useEffect } from 'react';
import { CaixaToken } from '../components/tokenView';
import { useIsFocused } from '@react-navigation/native';

export function PaginaSenhas() {
    const { obterItem, removerItem } = Armazenamento();
    const [listaTokens, defListaTokens] = useState([]);
    const telaAtiva = useIsFocused();

    return (
        <SafeAreaView style={{ flex: 1 }}>
            <View style={estilos.header}>
                <Text style={estilos.title}>
                    Minhas senhas
                </Text>
            </View>
        </SafeAreaView>
    )
}

const estilos= StyleSheet.create({
    header: {
        padding: 14,
        paddingTop: 58,
        backgroundColor: "#392de9"
    },
    title: {
        fontSize: 18,
        fontWeight: "bold",
        color: "#FFF"
    }

})

Para trabalhar com o useEffect, estudaremos sua estrutura. Basicamente ele recebe 2 condições; um arrow function que executa as ações, e um array de gatilho:

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

Primeiramente vamos passar o gatilho da tela ativa:

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

Agora vamos trabalhar nas funções, iniciando com a obtenção dos tokens:

async function carregaTokens() {
            const tokens = await obterItem("@token");
            defListaTokens(tokens);
        }

Assim que os tokens forem obtidos do armazenamento, executaremos o carregaTokens. Nosso useEffect completo ficará assim:

useEffect(() => {
        async function carregaTokens() {
            const tokens = await obterItem("@token");
            defListaTokens(tokens);
        }
        carregaTokens()
    }, [telaAtiva]);

Para gerar a função que deleta um item da lista, vamos usar o seguinte código:

 async function deletarToken(item) {
        const tokens  = await removerItem("@token", item)
        defListaTokens(tokens)
    };

Para podermos exibir a lista de Tokens, vamos fazer uso do FlatList:

import { View, StyleSheet, Text, FlatList } from "react-native";

E inserir a visualização da lista. Repare que a exibição do FlatList carrega a CaixaToken com dois parâmetros; exibir o token e removerToken

<View style={estilos.content}>
                <FlatList
                    style={{ flex: 1, paddingTop: 14, }}
                    data={listaTokens}
                    keyExtractor={(item) => String(item)}
                    renderItem={({ item }) => <CaixaToken
                        token={item}
                        removerToken={() => deletarToken(item)}
                    />}
                />
            </View>

E estilizar a nova View, adicionando o seguinte código:

 content: {
        flex: 1,
        paddingLeft: 14,
        paddingRight: 14,
    }

Agora iremos passar os tokens da FlatList para dentro do componente CaixaToken. Então no arquivo tokenView.js vamos modificar o código, acrescentando as variáveis:

export function CaixaToken({token, removerToken}) {
    return (
        <Pressable style={estilos.caixa} onLongPress={removerToken}>
            <Text style={estilos.text}>
                {token}
            </Text>
        </Pressable>
    )
}

Note que agora passamos as variáveis token e removerToken para serem consumidas no FlatList.

🎉Parabéns, você chegou ao final!!

⚔️Super Desafio!

Use os conhecimentos adquiridos para alterar os códigos e chegar na aparência abaixo:

Untitled-12 Aplicativo gerador de tokens

🥉O ícone usado se chama “trash” e faz parte da biblioteca Ionicons;

🥉O ícone da lixeira deverá ter a função de remover o Token.

🥈O parâmetro onLongpress deverá ser usado para copiar o token para área de transferência.

🥇Configure um setTimeout com delay de 300ms para animar o efeito fade do Touchable durante a exclusão do token

Share this content:

Profissional engajado com as últimas tendências tecnológicas e de gestão, buscando continuamente aprimorar suas competências e compartilhar seu conhecimento.

Publicar comentário