diff --git a/src/components/BotaoVoltar.tsx b/src/components/BotaoVoltar.tsx index b61477c..22fcfef 100644 --- a/src/components/BotaoVoltar.tsx +++ b/src/components/BotaoVoltar.tsx @@ -8,11 +8,17 @@ import { useNavigation } from '@react-navigation/core' interface Props { destino?: any + testID?: string semTexto?: boolean marginLeft?: number } -const BotaoVoltar = ({ destino, semTexto = false, marginLeft }: Props) => { +const BotaoVoltar = ({ + destino, + testID, + semTexto = false, + marginLeft +}: Props) => { const { t } = i18n const navigation = useNavigation() const handlePress = () => { @@ -23,7 +29,12 @@ const BotaoVoltar = ({ destino, semTexto = false, marginLeft }: Props) => { } } return ( - + void iconeSecundario?: ReactNode + testID?: string } interface INavigationListProps { @@ -26,6 +27,8 @@ const NavigationList = ({ itens }: INavigationListProps) => { left={() => {item.icone}} right={() => {item.iconeSecundario}} onPress={item.onPress} + testID={item.testID} + accessibilityLabel={item.testID} /> ) diff --git a/src/entities/User.ts b/src/entities/User.ts index e60b8e5..2d8d860 100644 --- a/src/entities/User.ts +++ b/src/entities/User.ts @@ -13,6 +13,10 @@ interface IUserAttributes { tokens?: Array idioma?: string aceitouPolitica?: boolean + premium?: boolean + temLivroPromocode?: boolean + inicioPromocode?: Date + duracaoPromocode?: number } export type IUser = IUserAttributes @@ -31,6 +35,10 @@ export default class User implements IUser { public tokens?: Array public idioma: string public aceitouPolitica?: boolean + public premium?: boolean + public temLivroPromocode?: boolean + public inicioPromocode?: Date + public duracaoPromocode?: number constructor({ id, @@ -45,7 +53,11 @@ export default class User implements IUser { canaisDeNotificacao, tokens, idioma, - aceitouPolitica + aceitouPolitica, + premium, + temLivroPromocode, + inicioPromocode, + duracaoPromocode }: IUserAttributes) { this.id = id this.nome = nome @@ -60,6 +72,10 @@ export default class User implements IUser { this.tokens = tokens this.idioma = idioma this.aceitouPolitica = aceitouPolitica + this.premium = premium + this.temLivroPromocode = temLivroPromocode + this.inicioPromocode = inicioPromocode + this.duracaoPromocode = duracaoPromocode } novidadeDispensada(slug: string): boolean { diff --git a/src/factories/UserFactory.ts b/src/factories/UserFactory.ts index 2ee3048..e414534 100644 --- a/src/factories/UserFactory.ts +++ b/src/factories/UserFactory.ts @@ -23,11 +23,17 @@ export default class UserFactory { canaisDeNotificacao, tokens, idioma, - aceitouPolitica + aceitouPolitica, + premium, + temLivroPromocode } = dados const lastAccess = dados.lastAccess ? dados.lastAccess.toDate() : null const countAccess = dados.countAccess ? dados.countAccess : 0 + const inicioPromocode = dados.inicioPromocode + ? dados.inicioPromocode.toDate() + : null + const duracaoPromocode = dados.duracaoPromocode ? dados.duracaoPromocode : 0 return new User({ id, @@ -42,7 +48,11 @@ export default class UserFactory { canaisDeNotificacao: canaisDeNotificacao || [], tokens: tokens || [], idioma: idioma || idiomaAtual, - aceitouPolitica + aceitouPolitica, + premium, + temLivroPromocode, + inicioPromocode, + duracaoPromocode }) } } diff --git a/src/i18n/translations/en.ts b/src/i18n/translations/en.ts index e9c1c76..781f87b 100644 --- a/src/i18n/translations/en.ts +++ b/src/i18n/translations/en.ts @@ -89,7 +89,8 @@ const en = { aceitePolitica: { respeitamos: 'We respect your privacy.', declaracao: - 'By using this app, you agree to our use of local storage to improve your experience.', + 'By using this app, you agree to our use of local storage to improve ' + + 'your experience.', conheca: 'Learn about our Privacy Policy', botao: 'Ok, I get it' }, @@ -161,6 +162,12 @@ const en = { notificacoes: 'Notifications', ajuda: 'Help', textoAjuda: 'Need help? Send an email to jornadasolar@gmail.com', + ativarPremium: 'Activate Premium', + premiumAtivo: 'Congratulations! You are Premium', + tituloPremium: 'Congratulations', + textoPremium: + 'For buying the Solar Journey 2022 book, you win' + + ' one month of Premium access', sair: 'Sign Out', ok: 'Ok', notificaEventos: 'Solar Journey Events', diff --git a/src/i18n/translations/es.ts b/src/i18n/translations/es.ts index d8fc643..29b5faa 100644 --- a/src/i18n/translations/es.ts +++ b/src/i18n/translations/es.ts @@ -89,6 +89,14 @@ const es = { anotacoes: 'Escribe sobre tu día', verMais: 'Ver más' }, + aceitePolitica: { + respeitamos: 'Respetamos su privacidad.', + declaracao: + 'Al utilizar esta aplicación, aceptas que utilicemos el' + + ' almacenamiento local para mejorar tu experiencia.', + conheca: 'Conozca nuestra política de privacidad', + botao: 'OK, lo tengo' + }, signos: { capricornio: 'Capricornio', aquario: 'Acuario', @@ -158,6 +166,12 @@ const es = { ajuda: 'Ayuda', textoAjuda: '¿Necesita ayuda? Envíe un correo electrónico a jornadasolar@gmail.com', + ativarPremium: 'Activar Premium', + premiumAtivo: 'Enhorabuena. Usted es Premium', + tituloPremium: 'Enhorabuena', + textoPremium: + 'Por haber comprado el libro Jornada Solar 2022, has ganado' + + ' un mes de acceso Premium', sair: 'Salir', ok: 'Ok', notificaEventos: 'Eventos de la Jornada Solar', diff --git a/src/i18n/translations/pt.ts b/src/i18n/translations/pt.ts index bf8ce9a..2b4ff5c 100644 --- a/src/i18n/translations/pt.ts +++ b/src/i18n/translations/pt.ts @@ -95,7 +95,8 @@ const pt = { aceitePolitica: { respeitamos: 'Respeitamos sua privacidade.', declaracao: - 'Ao utilizar este app, você aceita a utilização do armazenamento local para melhorarmos a sua experiência.', + 'Ao utilizar este app, você aceita a utilização do armazenamento ' + + 'local para melhorarmos a sua experiência.', conheca: 'Conheça nossa Política de privacidade', botao: 'Ok, entendi' }, @@ -167,6 +168,12 @@ const pt = { notificacoes: 'Notificações', ajuda: 'Ajuda', textoAjuda: 'Precisa de ajuda? Envie um e-mail para jornadasolar@gmail.com', + ativarPremium: 'Ativar Premium', + premiumAtivo: 'Parabéns! Você é Premium', + tituloPremium: 'Parabéns', + textoPremium: + 'Por ter comprado o livro da Jornada Solar 2022, você ganhou' + + ' um mês de acesso Premium', sair: 'Sair', ok: 'Ok', notificaEventos: 'Eventos do Jornada Solar', diff --git a/src/routes/App.routes.tsx b/src/routes/App.routes.tsx index be68b85..bb0bffc 100644 --- a/src/routes/App.routes.tsx +++ b/src/routes/App.routes.tsx @@ -8,11 +8,14 @@ import Sentimentos from '../screens/app/Sentimentos' import Habitos from '../screens/app/Habitos' import Anotacoes from '../screens/app/Anotacoes' import Player from '../screens/app/Player' -import { BottomTabs } from './BottomTabs.routes' +import { BottomTabs, BottomTabsParams } from './BottomTabs.routes' import AlterarSenha from '../screens/app/perfil/AlterarSenha' import MeusDados from '../screens/app/perfil/MeusDados' import Notificacoes from '../screens/app/perfil/Notificacoes' import AlterarNome from '../screens/app/perfil/AlterarNome' +import Premium from '../screens/app/perfil/Premium' +import { BottomTabScreenProps } from '@react-navigation/bottom-tabs' +import { CompositeScreenProps } from '@react-navigation/core' type AppStackParams = { Abas: undefined @@ -25,11 +28,17 @@ type AppStackParams = { AlterarNome: undefined AlterarSenha: undefined Notificacoes: undefined + Premium: { origem?: string } } type AppNavigationProps = NativeStackScreenProps type DiaNavigationProps = NativeStackScreenProps type PlayerNavigationProps = NativeStackScreenProps +type RootStackScreenProps = + CompositeScreenProps< + NativeStackScreenProps, + BottomTabScreenProps + > const { Navigator, Screen } = createNativeStackNavigator() @@ -46,6 +55,7 @@ function AppRoutes() { + ) } @@ -55,5 +65,6 @@ export { AppStackParams, AppNavigationProps, DiaNavigationProps, - PlayerNavigationProps + PlayerNavigationProps, + RootStackScreenProps } diff --git a/src/routes/BottomTabs.routes.tsx b/src/routes/BottomTabs.routes.tsx index 6745de6..faf6e48 100644 --- a/src/routes/BottomTabs.routes.tsx +++ b/src/routes/BottomTabs.routes.tsx @@ -85,4 +85,4 @@ function BottomTabs() { ) } -export { BottomTabs, TabsNavigationProps } +export { BottomTabs, BottomTabsParams, TabsNavigationProps } diff --git a/src/routes/linking.config.ts b/src/routes/linking.config.ts index 8a9fca3..d8be3c5 100644 --- a/src/routes/linking.config.ts +++ b/src/routes/linking.config.ts @@ -15,7 +15,8 @@ const config = { }, Dia: 'dia', Perfil: 'perfil', - Player: 'meditacao' + Player: 'meditacao', + Premium: 'premium' } } diff --git a/src/screens/app/perfil/Perfil.tsx b/src/screens/app/perfil/Perfil.tsx index d670c1f..dfb3317 100644 --- a/src/screens/app/perfil/Perfil.tsx +++ b/src/screens/app/perfil/Perfil.tsx @@ -1,8 +1,10 @@ -import React, { useState } from 'react' +import React, { useContext, useState } from 'react' import { StyleSheet, View } from 'react-native' import { Text } from 'react-native-paper' import Emoji from '../../../components/Emoji' -import NavigationList from '../../../components/NavigationList' +import NavigationList, { + INavigationItem +} from '../../../components/NavigationList' import { AppNavigationProps } from '../../../routes/App.routes' import SignOutUser from '../../../services/user/SignOutUser' import { MaterialIcons } from '@expo/vector-icons' @@ -13,8 +15,10 @@ import { t } from 'i18n-js' import Dialogo from '../../../components/Dialogo' import Novidade from '../../../components/Novidade' import Telas from '../../../enums/Telas' +import AuthContext from '../../../context/AuthContext' const Perfil = ({ navigation }: AppNavigationProps) => { + const { user } = useContext(AuthContext) const handleSair = () => { new SignOutUser().call() } @@ -36,37 +40,63 @@ const Perfil = ({ navigation }: AppNavigationProps) => { navigation.navigate('Notificacoes') } + const handlePremium = () => { + navigation.navigate('Premium', { origem: 'Perfil' }) + } + const ChevronRight = ( ) - const menus = [ + const menus: INavigationItem[] = [ { icone: , texto: t('perfil.meusDados'), onPress: handleMeusDados, - iconeSecundario: ChevronRight + iconeSecundario: ChevronRight, + testID: 'perfilMeusDados' }, { icone: , texto: t('perfil.notificacoes'), onPress: handleNotificacoes, - iconeSecundario: ChevronRight + iconeSecundario: ChevronRight, + testID: 'perfilNotificacao' }, { icone: , texto: t('perfil.ajuda'), onPress: abrirDialogo, - iconeSecundario: ChevronRight + iconeSecundario: ChevronRight, + testID: 'perfilAjuda' }, { icone: , texto: t('perfil.sair'), onPress: handleSair, - iconeSecundario: ChevronRight + iconeSecundario: ChevronRight, + testID: 'perfilSair' } ] + const botaoPremium = user.premium + ? { + icone: , + texto: t('perfil.premiumAtivo'), + testID: 'perfilPremiumAtivo' + } + : { + icone: , + texto: t('perfil.ativarPremium'), + onPress: handlePremium, + iconeSecundario: ChevronRight, + testID: 'perfilAtivarPremium' + } + + if (user.temLivroPromocode) { + menus.unshift(botaoPremium) + } + return ( diff --git a/src/screens/app/perfil/Premium.tsx b/src/screens/app/perfil/Premium.tsx new file mode 100644 index 0000000..80a5539 --- /dev/null +++ b/src/screens/app/perfil/Premium.tsx @@ -0,0 +1,80 @@ +import { useFocusEffect } from '@react-navigation/core' +import { t } from 'i18n-js' +import React, { useContext, useState } from 'react' +import { StyleSheet, View } from 'react-native' +import { Paragraph } from 'react-native-paper' +import BotaoVoltar from '../../../components/BotaoVoltar' +import Button from '../../../components/Button' +import Container from '../../../components/Container' +import Titulo from '../../../components/Titulo' +import AuthContext from '../../../context/AuthContext' +import { RootStackScreenProps } from '../../../routes/App.routes' +import AtivarPremium from '../../../services/user/AtivarPremium' +import AtivarTemLivroPromocode from '../../../services/user/AtivarTemLivroPromocode' + +const Premium = ({ navigation, route }: RootStackScreenProps<'Premium'>) => { + const { user, refreshUser } = useContext(AuthContext) + const destino = route.params?.origem ? null : 'Abas' + const [isLoading, setIsLoading] = useState(false) + + const ativarPromoCode = () => { + if (!user.temLivroPromocode) { + new AtivarTemLivroPromocode().call(user.id) + refreshUser() + } + } + + const voltar = () => { + if (destino) { + navigation.navigate(destino) + } else { + navigation.goBack() + } + } + + useFocusEffect( + React.useCallback(() => { + if (!user.premium) { + setIsLoading(false) + ativarPromoCode() + } else { + voltar() + } + }, []) + ) + + const handleAtivar = async () => { + setIsLoading(true) + new AtivarPremium().call(user.id) + await refreshUser() + voltar() + } + + return ( + + + + {t('perfil.tituloPremium')} + {t('perfil.textoPremium')} + + + + ) +} + +export default Premium + +const styles = StyleSheet.create({ + conteudo: { + flex: 1, + justifyContent: 'space-around', + alignItems: 'center' + }, + texto: { textAlign: 'center' } +}) diff --git a/src/services/user/AtivarPremium.ts b/src/services/user/AtivarPremium.ts new file mode 100644 index 0000000..ce67d43 --- /dev/null +++ b/src/services/user/AtivarPremium.ts @@ -0,0 +1,26 @@ +import UsersRepository, { + IUsersRepository +} from '../../repositories/UsersRepository' + +interface IAtivarPremium { + call(userId: string): boolean +} + +export default class AtivarPremium implements IAtivarPremium { + private usersRepository: IUsersRepository + + constructor() { + this.usersRepository = new UsersRepository() + } + + call(userId: string): boolean { + return this.usersRepository.update({ + id: userId, + attributes: { + premium: true, + inicioPromocode: new Date(), + duracaoPromocode: 30 + } + }) + } +} diff --git a/src/services/user/AtivarTemLivroPromocode.ts b/src/services/user/AtivarTemLivroPromocode.ts new file mode 100644 index 0000000..e11ef59 --- /dev/null +++ b/src/services/user/AtivarTemLivroPromocode.ts @@ -0,0 +1,24 @@ +import UsersRepository, { + IUsersRepository +} from '../../repositories/UsersRepository' + +interface IAtivarTemLivroPromocode { + call(userId: string): boolean +} + +export default class AtivarTemLivroPromocode + implements IAtivarTemLivroPromocode +{ + private usersRepository: IUsersRepository + + constructor() { + this.usersRepository = new UsersRepository() + } + + call(userId: string): boolean { + return this.usersRepository.update({ + id: userId, + attributes: { temLivroPromocode: true } + }) + } +}