Skip to content

Análise de Código — canta-igreja

Gerado pelo Archaeologist (Reversa) em 2026-05-11 doc_level: detalhado | Organização: híbrida (módulo + caso de uso)


Módulo: musicas

Propósito: Busca, exibição, favoritar e compartilhamento de músicas. Núcleo do produto.

Arquivos principais:

  • src/model/Musica.ts — interfaces de domínio
  • src/model/IndiceMusica.ts — índice de música em livros
  • src/model/rotulo/CategoriaRotulos.ts — dicionário de rótulos litúrgicos
  • src/model/rotulo/RotuloMusica.ts — modelo de rótulo
  • src/repository/musicas/MusicasRepository.ts — acesso ao banco SQLite
  • src/repository/musicas/MusicaConfiguration.ts — configuração adaptativa (tablet/phone)
  • src/service/MusicaService.ts — lógica de negócio (favoritar, visualização, relatar erro)
  • src/store/Musicas/Musicas.action.ts — thunks Redux
  • src/store/Musicas/Musicas.reducer.ts — reducer (EntityAdapter)

Entidades

Entidade Campos Confiança
ReferenciaMusica id_musica: number, titulo: string 🟢
InformacoesBasicasMusica id_musica, titulo, letra, contribuidores, midias, rotulos 🟢
InformacoesDetalhadasMusica extends InformacoesBasicasMusica + indices: IndiceMusica[] 🟢
IndiceMusica id_musica, id_edicao_livro, indice, titulo_livro, edicao, editora 🟢
Contribuidores compositores?: string[], letristas?: string[], musicistas?: string[] 🟢
Midias YouTube?: string (deprecated), YouTubes?: string[], CifraClub?: string, Partitura?: string 🟢

Algoritmo de busca (🟢 CONFIRMADO)

O MusicasRepository.findAllBy() aplica 3 estratégias de busca com padrão Strategy:

Condição Estratégia Mecanismo
Sem termo de busca semTermo SELECT ... ORDER BY titulo
Termo é número inteiro porIndice WHERE indice = ? em cantico_livro
Termo é texto porTermo FTS5 MATCH com ranking bm25(musica_busca, 50, 5)

Normalização do termo de busca (FTS5):

término → NFKD normalize → remove não-alfanumérico → insere "* " entre palavras → termina com "*"
Exemplo: "ave maria" → "ave* maria*"

Paginação: cursor por OFFSET/LIMIT, hasNextPage calculado por size+1 de retorno.

Configuração adaptativa:

  • TOTAL_CARACTERES: 100 (phone) / 250 (tablet) — trecho da letra na listagem
  • TOTAL_TOKENS: 12 (phone) / 24 (tablet) — tokens do snippet de destaque FTS5

Regras de negócio

  • 🟢 A busca FTS5 usa tabela virtual musica_busca com colunas titulo (peso 50) e trecho da letra (peso 5)
  • 🟢 Campo midias.YouTube está deprecated — usar YouTubes para múltiplos vídeos
  • 🟢 Favoritar uma música não altera a entidade em memória (store), apenas persiste no SQLite via MusicasFavoritasRepository
  • 🟢 Ao visualizar uma música, marcarAsVisualizada é disparado como side effect (não bloqueia carregamento)
  • 🟢 Relatar erro abre Airtable form com id_musica, titulo e início da letra pré-preenchidos

Módulo: repertorios

Propósito: Criação, edição, reordenação, compartilhamento, importação e duplicação de repertórios musicais.

Arquivos principais:

  • src/model/repertorio/Repertorio.ts — interfaces de domínio
  • src/repository/repertorios/RepertoriosRepository.ts — CRUD SQLite
  • src/store/Repertorios/ — estado Redux
  • src/store/ItensRepertorio/ — itens em edição ativa
  • src/service/RepertorioService.ts — geração de mensagem, compartilhamento, importação
  • src/service/CompartilharService.ts — fluxo de compartilhamento
  • src/service/FolhetoService.ts — geração de folheto PDF online

Entidades

Entidade Campos-chave Confiança
Repertorio id_repertorio, slug?, titulo, resumo, ordem_musicas: number[], metadata, data.edicao 🟢
ItemRepertorio id_item_repertorio, id_repertorio, musica?, termo?, momento.valor, momento.sugestoes, tonalidade.valor, tonalidade.sugestoes 🟢
RepertorioSlug referencia: {conta, repertorio}, corrente: {conta, repertorio} 🟢
RepertorioMetadata data?: DateString, dia_liturgico?: {id_romcal, ano} 🟢

Algoritmos

Geração de mensagem de compartilhamento (🟢):

Repertório → título + metadata formatada (data + dia litúrgico romcal) + URL
  → para cada item: tonalidade + momento + título/termo + índices dos livros
  → Markdown WhatsApp (negrito, código inline)

Fluxo de compartilhamento via API (🟢):

  1. Autenticar Google → obter Bearer token
  2. POST/PUT /api/v1/repertorios/{slug} com itens serializados
  3. Receber conta.slug + slug do servidor
  4. Atualizar slug.corrente localmente
  5. Abrir dialog nativo de compartilhamento com mensagem Markdown

Folheto (🟢):

  • GerarFolhetoComSlugStrategy: autentica, envia para API, abre livreto.canta.app/?conta:slug
  • GerarFolhetoBase64Strategy: serializa para Base64 + abre livreto.canta.app/?repertorio=BASE64
  • GerarFolhetoNoopStrategy: no-op (fallback)

Regras de negócio

  • 🟢 Título do repertório: máximo 50 caracteres (CHECK SQL)
  • 🟢 item_repertorio.termo: máximo 300 caracteres
  • 🟢 Deleção é soft-delete (data_exclusao timestamp)
  • 🟢 slug.referencia = quem originou; slug.corrente = versão atual
  • 🟢 Ao duplicar um repertório, o slug é undefined até o usuário compartilhar novamente
  • 🟢 ordem_musicas: number[] é um JSON array com os id_musica na ordem definida

Módulo: livros

Propósito: Gerenciar hinários (livros de cânticos) e suas edições.

Arquivos principais:

  • src/model/Livro.ts
  • src/repository/LivrosRepository.ts
  • src/store/Livros/

Entidades

Entidade Campos Confiança
EdicaoLivro id_edicao_livro, id_livro, titulo, edicao, editora, ano, site?, descricao 🟢

Schema SQL (inferido das queries)

livro (id_livro, titulo)
editora (id_editora, nome, site)
edicao_livro (id_edicao_livro, id_livro, id_editora, edicao, ano, descricao, site, detalhes JSON)
cantico_livro (id_musica, id_edicao_livro, indice, ordem)

Regras de negócio

  • 🟢 id_edicao_livro é a entidade central usada como chave de filtro na busca de músicas
  • 🟢 detalhes ->> 'sigla' é uma coluna JSON virtual para exibir a sigla do livro na lista

Módulo: biblioteca

Propósito: Download e sincronização de obras (pacotes de hinários) a partir da API Canta.app.

Arquivos principais:

  • src/model/obra/Obra.tsx
  • src/repository/BibliotecaRepository.ts
  • src/service/BibliotecaService.ts
  • src/service/ObraService.ts
  • src/store/Biblioteca/ (RTK Query)

Entidades

Entidade Campos Confiança
Obra id_obra, titulo, descricao, crc32?, disponivel 🟢
ExemplarBiblioteca id_exemplar, id_obra, titulo, descricao, crc32, ativo 🟢

Algoritmo de import de obra (🟢)

  1. Download do arquivo SQLite da obra para cache (ObraService.baixarObra)
  2. ATTACH DATABASE do arquivo em cache como alias toImport
  3. Upsert de músicas (INSERT + ON CONFLICT UPDATE) comparando crc32
  4. Upsert de livros, editoras e edições de livros
  5. Disable do exemplar anterior e create do novo
  6. DETACH do alias

Proteção contra downloads concorrentes: flag estático downloadEmAndamento — apenas um download por vez.

Regras de negócio

  • 🟢 CRC32 é usado como checksum para detectar diferenças entre versão local e remota
  • 🟢 Importação de obra é atômica por ATTACH/DETACH
  • 🟢 BibliotecaService.abrirLink() abre links externos (Liturgia Diária, CNBB, Paulus)

Módulo: sugestao-repertorio

Propósito: Sugestões de repertório litúrgico baseadas no calendário romano, sincronizadas via API.

Arquivos principais:

  • src/model/repertorio/SugestaoRepertorio.ts
  • src/repository/BibliotecaSugestoesRepertorioRepository.ts
  • src/service/SugestoesRepertorioService.ts
  • src/store/SugestoesRepertorio/

Entidades

Entidade Campos Confiança
SugestaoRepertorio id_sugestao_repertorio, chave_ciclo_liturgico?, ano_liturgico?, id_romcal?, titulo, resumo, itens: ItemSugestaoRepertorio[], metadata.referencias, cor, data, tempoLiturgico?, crc32 🟢
ItemSugestaoRepertorio id_musica, momento?, titulo_musica, trecho_letra, metadata.referencia?, metadata.indice?, metadata.ignorar? 🟢
DateString string & { __brand: 'YYYY-MM-DD' } — branded type 🟢
CicloLiturgico 'A' \| 'B' \| 'C' \| 'PAR' \| 'ÍMPAR' 🟢
CorLiturgica enum: GREEN, PURPLE, RED, WHITE, ROSE, GOLD, BLACK, DESCONHECIDO 🟢

Algoritmo de sincronização (🟢)

  1. Download do arquivo sugestoes_repertorio.db da API para cache
  2. ATTACH do arquivo como alias sugestoesImport
  3. DELETE de todas as sugestões existentes (substituição total)
  4. INSERT de todas do banco importado
  5. DETACH

Inicialização: Na primeira abertura (SQLite vazio), popula a partir do repertorios.db embarcado nos assets.

Chave de identificação: chave_ciclo_liturgico = "{CicloLiturgico}:{IdRomcal}" — permite associar sugestões a dias litúrgicos específicos de um ciclo (A, B ou C).

Regras de negócio

  • 🟢 CRC32 é usado para verificar se o banco remoto difere do local (sumCrc32())
  • 🟢 Sugestões não são incrementais — substituição total a cada sync
  • 🟢 metadata.ignorar = true marca itens excluídos da sugestão sem removê-los

Módulo: liturgia

Propósito: Calendário litúrgico (romcal offline) e leituras do dia (API DANCRF online).

Arquivos principais:

  • src/service/CalendarioLiturgicoService.ts
  • src/model/liturgia/LiturgiaDia.ts
  • src/store/Liturgia/Liturgia.api.ts (RTK Query)
  • src/repository/LiturgiaCacheRepository.ts

Entidades

Entidade Campos Confiança
DiaLiturgico idRomcal, nome, data: DateString, cicloLiturgico?, cor?, tempoLiturgico? 🟢
LiturgiaDiaDancrf data, celebracoes: CelebracaoLiturgica[] 🟢
CelebracaoLiturgica id, liturgia, cor, principal, oracoes, antifonas, leituras: LeituraLiturgica[] 🟢
LeituraLiturgica ordem, tipo, rotulo, opcoes: OpcaoLeituraLiturgica[] 🟢
OpcaoLeituraLiturgica referencia?, titulo?, refrao?, texto 🟢

Algoritmos

Calendário Romcal (🟢 — offline):

  • Instância global new Romcal({ scope: 'gregorian', localizedCalendar: Brazil_PtBr, ... })
  • Cache em memória por ano (Map<number, DiaLiturgico[]>)
  • Gera calendários do ano atual + próximo (para cobrir Advento que inicia o ciclo seguinte)
  • Chave "CicloLiturgico:IdRomcal" para busca em sugestões
  • Resolução de placeholders i18n: $(names:holy_thursday) → lookup no próprio calendário

Cache de liturgia (🟢 — persistência SQLite):

liturgia_cache (data TEXT PK, payload TEXT, atualizado_em INTEGER)
  • RTK Query busca primeiro no cache local → se fresco, retorna sem chamar API
  • 404 da API = dia sem liturgia → retorna null (não erro técnico)

Mapeamento de cores DANCRF → CorLiturgica (🟢): | DANCRF | CorLiturgica | |--------|-------------| | Verde | GREEN | | Vermelho | RED | | Roxo | PURPLE | | Rosa | ROSE | | Branco | WHITE | | (outros) | DESCONHECIDO |

Regras de negócio

  • 🟢 epiphanyOnSunday: true, corpusChristiOnSunday: true, ascensionOnSunday: true — configuração litúrgica para o Brasil
  • 🔴 FIXME no código: ciclos PAR/ÍMPAR ainda não suportados em extrairCicloLiturgico

Módulo: auth

Propósito: Autenticação via Google OAuth 2.0 (PKCE). Funcionalidade opcional — necessária apenas para compartilhamento.

Arquivos principais:

  • src/arch/auth/Auth.ts — orquestrador de autenticação
  • src/arch/auth/AuthRepository.ts — persistência de credenciais
  • src/arch/auth/CredentialsService.ts — storage de credenciais (Keychain / localStorage)
  • src/arch/auth/Conta.ts — modelo de conta
  • src/repository/UsuarioLogadoRepository.ts — estado do usuário logado (MMKV)
  • src/store/Acesso/ — estado Redux de acesso

Entidades

Entidade Campos Confiança
Credenciais accessToken, idToken, refreshToken, accessTokenExpirationDate 🟢
Conta slug, email, nome, subject, provider 🟢
UsuarioLogado slug, nome, email?, imagem? 🟢

Algoritmo de autenticação (🟢)

autenticar():
  1. getCredenciais() do Keychain/localStorage
  2. Se não existe → authorize() (OAuth PKCE Google)
     → POST /auth com idToken → recebe Conta
     → setCredenciais(conta, authState) no Keychain
     → setUsuarioLogado(credenciais, conta) no MMKV
  3. Se existe e está expirada (< 1 min de validade)
     → refresh() → setCredenciaisRefreshed()
  4. Retorna idToken

Verificação de expiração:

isAtiva = (expirationDate - 1 minuto) > agora

Dual strategy de storage (🟢): | Plataforma | Estratégia | Storage | |-----------|------------|---------| | iOS/Android | NativeCredentialsStrategy | react-native-keychain (Secure Enclave / Keystore) | | Web | WebCredentialsStrategy | localStorage + fallback memoryStorage |

Imagem do usuário: extraída do JWT via jwtDecode(idToken).picture

Regras de negócio

  • 🟢 App funciona sem login — auth é disparado apenas no fluxo de compartilhamento
  • 🟢 Refresh automático de token quando falta ≤ 1 minuto para expirar
  • 🟢 Revogação do refreshToken no logout (Google OAuth revoke endpoint)
  • 🟢 No web, sem localStorage disponível → credenciais ficam só em memória (sessão)

Módulo: configuracoes

Propósito: Preferências do usuário para aparência e comportamento da tela de música.

Arquivos principais:

  • src/store/ui/Configuracoes/Configuracoes.types.ts
  • src/repository/ConfiguracoesUsuarioRepository.ts (MMKV)
  • src/repository/ConfiguracoesAplicacaoRepository.ts (MMKV)

Entidades / Enums

Tipo Valores Confiança
TamanhoFonte NORMAL, MODERADO, GRANDE 🟢
EstiloRefrao NEGRITO, ITALICO, CAIXA_ALTA 🟢
FonteMusica PADRAO (normal), SERIFADO, MONOESPACADA 🟢
Tema CLARO, ESCURO, CLARO_LEGADO, CLARO_ALTERNATIVO 🟢
ConfiguracoesMusica fonte, tamanho, estiloRefrao 🟢
ConfiguracoesTema tema 🟢

Regras de negócio

  • 🟢 Persistência em MMKV (chave-valor, não SQLite)
  • 🟢 Padrão: FonteMusica.PADRAO, TamanhoFonte.NORMAL, EstiloRefrao.NEGRITO, Tema.CLARO
  • 🟢 ConfiguracaoAplicacao.ONBOARDING_MODAL — flag booleana de onboarding (padrão false)
  • 🟡 Tema.SISTEMA está comentado no código — feature planejada mas não implementada

Módulo: youtube-player

Propósito: Player YouTube integrado com controles avançados (velocidade, loop, mini-player arrastável).

Arquivos principais:

  • src/view/fragment/Youtube/YoutubePlayerView.tsx
  • src/view/fragment/Youtube/YoutubePlayerView.types.ts
  • src/view/fragment/Youtube/usePlayerState.ts
  • src/view/fragment/Youtube/useLoopControl.ts
  • src/view/fragment/Youtube/useMiniPlayerDrag.ts
  • src/view/fragment/Youtube/bar/YoutubeBarPlayerView.tsx

Tipos e Constantes

Constante Valor Confiança
MINI_PLAYER_DIMENSIONS.WIDTH 200px 🟢
MINI_PLAYER_DIMENSIONS.HEIGHT 112px (9:16) 🟢
MINI_PLAYER_DIMENSIONS.CONTROLS_HEIGHT 28px 🟢
FULL_PLAYER_DIMENSIONS.ASPECT_RATIO 9/16 🟢
PLAYBACK_RATES [0.5, 0.75, 1.0, 1.25, 1.5, 2.0] 🟢

Algoritmos

Loop (🟢):

  • Polling a cada 500ms via setInterval quando loop ativo
  • getCurrentTime() → se >= loopState.endseekTo(loopState.start)
  • end inicializado com duration ao carregar vídeo

Dimensionamento do player (🟢):

videoHeight = min(screenWidth * (9/16), windowHeight / 4)

Mini-player:

  • Posição arrastável com limites de tela
  • Transições com react-native-reanimated

Regras de negócio

  • 🟢 Velocidades de reprodução: 0.5x a 2.0x
  • 🟢 Loop com marcação de início/fim por botão (captura currentTime no momento do click)
  • 🟢 Mini-player fica fixo em posição inicial (x: 16, y: 16) ao ser ativado

Módulo: arch

Propósito: Infraestrutura transversal — banco de dados, migrations, tema, utilitários de plataforma.

Sub-módulos

arch/persistence

Arquivo Responsabilidade
Banco.ts Singleton de conexão SQLite (op-sqlite). Lazy init, mutex via Promise
AssetBanco.ts Enum dos arquivos de banco embarcados
AssetDatabasePreparator.ts Copia banco dos assets para o local de dados da plataforma
MigrationsRepository.ts Executa migrations sequenciais por versão (PRAGMA user_version)
PersistencePlatformStrategy.ts Interface de estratégia por plataforma
PersistencePlatformStrategies.ts Implementações iOS (IOS_LIBRARY_PATH), Android (default), Web

Estratégias de plataforma:

Plataforma databasePath cachePath
Android undefined (default) ../cache/
iOS IOS_LIBRARY_PATH Caches
Web (WebPlatformStrategy) (WebPlatformStrategy)

Padrão de mutex para inicialização:

if (database !== undefined) return database;
if (initializingPromise === undefined) initializingPromise = initialize();
return initializingPromise;

arch/auth

Ver módulo auth acima.

arch/style

  • react-native-unistyles 3.x para temas
  • Temas: base, light, material, materialNew
  • Breakpoints responsivos definidos em breakpoints.styles.ts

arch/error

  • Sentry.ts: DSN configurado, tracesSampleRate=0.01 em produção, 1.0 em dev
  • Metricas.ts: 10 métricas via Sentry Metrics (count)

arch/util

  • Toast.ts / Toast.web.ts — notificações toast por plataforma
  • linking.ts — abertura de links externos, encoding de query params
  • Animation.ts — utilitários de animação
  • TextUtil.ts / collections.ts / delay.ts etc.

Resumo Consolidado

Módulo Entidades Algoritmos Não-triviais Complexidade
musicas 6 Busca FTS5 + 3 strategies, paginação Alta
repertorios 4 Compartilhamento, folheto, geração markdown Alta
livros 1 Baixa
biblioteca 2 Import via ATTACH/DETACH + CRC32 diff Média
sugestao-repertorio 2 Sync total via ATTACH, chave ciclo litúrgico Média
liturgia 5 Romcal offline + cache SQLite + mapeamento DANCRF Alta
auth 3 OAuth PKCE + refresh + dual storage Alta
configuracoes 6 enums Baixa
youtube-player 4 tipos Loop polling, mini-player drag Média
arch Mutex singleton SQLite, migrations versionadas Média