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íniosrc/model/IndiceMusica.ts— índice de música em livrossrc/model/rotulo/CategoriaRotulos.ts— dicionário de rótulos litúrgicossrc/model/rotulo/RotuloMusica.ts— modelo de rótulosrc/repository/musicas/MusicasRepository.ts— acesso ao banco SQLitesrc/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 Reduxsrc/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 listagemTOTAL_TOKENS: 12 (phone) / 24 (tablet) — tokens do snippet de destaque FTS5
Regras de negócio
- 🟢 A busca FTS5 usa tabela virtual
musica_buscacom colunastitulo(peso 50) e trecho da letra (peso 5) - 🟢 Campo
midias.YouTubeestá deprecated — usarYouTubespara 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,tituloeinício da letrapré-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íniosrc/repository/repertorios/RepertoriosRepository.ts— CRUD SQLitesrc/store/Repertorios/— estado Reduxsrc/store/ItensRepertorio/— itens em edição ativasrc/service/RepertorioService.ts— geração de mensagem, compartilhamento, importaçãosrc/service/CompartilharService.ts— fluxo de compartilhamentosrc/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 (🟢):
- Autenticar Google → obter Bearer token
- POST/PUT
/api/v1/repertorios/{slug}com itens serializados - Receber
conta.slug+slugdo servidor - Atualizar
slug.correntelocalmente - Abrir dialog nativo de compartilhamento com mensagem Markdown
Folheto (🟢):
GerarFolhetoComSlugStrategy: autentica, envia para API, abrelivreto.canta.app/?conta:slugGerarFolhetoBase64Strategy: serializa para Base64 + abrelivreto.canta.app/?repertorio=BASE64GerarFolhetoNoopStrategy: 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_exclusaotimestamp) - 🟢
slug.referencia= quem originou;slug.corrente= versão atual - 🟢 Ao duplicar um repertório, o
slugéundefinedaté o usuário compartilhar novamente - 🟢
ordem_musicas: number[]é um JSON array com osid_musicana ordem definida
Módulo: livros
Propósito: Gerenciar hinários (livros de cânticos) e suas edições.
Arquivos principais:
src/model/Livro.tssrc/repository/LivrosRepository.tssrc/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.tsxsrc/repository/BibliotecaRepository.tssrc/service/BibliotecaService.tssrc/service/ObraService.tssrc/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 (🟢)
- Download do arquivo SQLite da obra para cache (
ObraService.baixarObra) ATTACH DATABASEdo arquivo em cache como aliastoImport- Upsert de músicas (INSERT + ON CONFLICT UPDATE) comparando
crc32 - Upsert de livros, editoras e edições de livros
- Disable do exemplar anterior e create do novo
DETACHdo 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.tssrc/repository/BibliotecaSugestoesRepertorioRepository.tssrc/service/SugestoesRepertorioService.tssrc/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 (🟢)
- Download do arquivo
sugestoes_repertorio.dbda API para cache ATTACHdo arquivo como aliassugestoesImport- DELETE de todas as sugestões existentes (substituição total)
- INSERT de todas do banco importado
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 = truemarca 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.tssrc/model/liturgia/LiturgiaDia.tssrc/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çãosrc/arch/auth/AuthRepository.ts— persistência de credenciaissrc/arch/auth/CredentialsService.ts— storage de credenciais (Keychain / localStorage)src/arch/auth/Conta.ts— modelo de contasrc/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
refreshTokenno logout (Google OAuth revoke endpoint) - 🟢 No web, sem
localStoragedisponí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.tssrc/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ãofalse) - 🟡
Tema.SISTEMAestá 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.tsxsrc/view/fragment/Youtube/YoutubePlayerView.types.tssrc/view/fragment/Youtube/usePlayerState.tssrc/view/fragment/Youtube/useLoopControl.tssrc/view/fragment/Youtube/useMiniPlayerDrag.tssrc/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
setIntervalquando loop ativo getCurrentTime()→ se >=loopState.end→seekTo(loopState.start)endinicializado comdurationao 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
currentTimeno 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
authacima.
arch/style
react-native-unistyles3.x para temas- Temas:
base,light,material,materialNew - Breakpoints responsivos definidos em
breakpoints.styles.ts
arch/error
Sentry.ts: DSN configurado,tracesSampleRate=0.01em produção,1.0em devMetricas.ts: 10 métricas via Sentry Metrics (count)
arch/util
Toast.ts/Toast.web.ts— notificações toast por plataformalinking.ts— abertura de links externos, encoding de query paramsAnimation.ts— utilitários de animaçãoTextUtil.ts/collections.ts/delay.tsetc.
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 |