Uma decisão de UX que errei na primeira tentativa
Esse aqui começou como um bug pequeno de funcionalidade ausente: tocar em um card de lançamento na aba Discover não fazia nada. A correção em si acabou sendo simples, mas construí a interação errada primeiro, e essa é a parte mais interessante.
Problema
Encontrei isso enquanto verificava outra correção num dispositivo real: os cards de lançamento da aba Discover eram só exibição, sem handler de toque, sem nenhuma ação de adicionar. Fui ver o código e o próprio comentário de documentação do card entregou o problema: dizia, literalmente, que a ação de adicionar à playlist deveria ter sido lançada num issue anterior, junto com o gesto de deslizar “Not Interested”. Só metade daquilo chegou a ser construída.
Por que essa abordagem (tentativa um)
O app já tinha um método de serviço funcionando que fazia exatamente isso: adicionar um álbum do catálogo a uma playlist, sem precisar de adaptação, já que os itens do feed do Discover já são do tipo certo. Conectar isso foi a parte fácil. A pergunta de design interessante era a interação: como o usuário efetivamente dispara essa ação?
Meu primeiro instinto foi buscar consistência com o gesto “Not Interested” que já existia no mesmo card: adicionar uma segunda ação de deslizar, na borda oposta, tom verde, ícone de mais. Isso combinava com o modelo de interação já existente na linha, não exigia nenhuma hierarquia de view nova, e funcionou: construí, escrevi testes, instalei num dispositivo físico e confirmei que funcionava de ponta a ponta.
Pegadinhas
Depois passei um tempo convivendo com aquilo, e estava errado. Em todo o resto do app, Playlist, History, a ação equivalente funciona do mesmo jeito: toca no card, recebe uma folha de detalhe com botões. Discover virou a única aba onde você tinha que saber que precisava deslizar em vez disso. Eu tinha otimizado para “menor diff nesse card específico” e perdido a consistência mais importante: bater com o jeito que o resto do app já lida com essa exata interação.
Essa é uma lição de verdade sobre escolher o ponto de referência errado numa decisão de UI. Comparei a nova ação com sua irmã no mesmo card em vez de comparar com sua contraparte nas outras abas. As duas são argumentos legítimos de “combina com os padrões existentes”, mas só um deles sobrevive ao uso de verdade do app, e o jeito de descobrir isso era usando, não argumentando a partir do diff.
Implementação (tentativa dois)
Reconstruí do jeito certo: uma nova folha de detalhe e um novo view model, deliberadamente espelhando os dois que já existem para Playlist e History quase linha por linha, mesmo layout de cabeçalho (arte, título, artista, badge, ano), mesmo enum AddState (idle/adding/done/failed) controlando o texto, ícone e tom do botão, mesma integração .sensoryFeedback para a confirmação háptica no instante em que o estado muda. A versão do Discover é mais simples que suas irmãs porque não precisa de uma seção de sugestões do mesmo artista, o próprio feed do Discover já é a seção de sugestões.
Um achado incidental durante esse trabalho: o código de adicionar à playlist que já existia no History e meu código novo do Discover precisavam da lógica idêntica para resolver a playlist vinculada e mapear uma falha para um texto acionável (“link a playlist in Settings,” “this playlist isn’t yours to edit,” e por aí vai). Em vez de copiar e colar isso de novo, extraí para um pequeno arquivo compartilhado que as duas funcionalidades agora chamam. Coisa pequena, mas é o tipo de duplicação fácil de justificar pular num diff de duas linhas e bem mais difícil de justificar quando você está olhando para o mesmo bloco aparecendo duas vezes.
Resultados
Suíte completa verde em 171 testes, incluindo cobertura nova para os três branches reais do view model de detalhe de lançamento (adição bem-sucedida, falha por playlist não vinculada, dispensar e fechar). Reconstruí e reinstalei num dispositivo físico uma segunda vez e confirmei que a interação de verdade bate exatamente com Playlist e History.
Todo esse desvio custou uns vinte minutos a mais. Valeu a pena: lançar o padrão de interação errado numa superfície de UI compartilhada é bem mais caro de notar e desfazer depois do que pegar isso numa passagem por dispositivo real logo no primeiro dia.
Leitura relacionada
A configuração que funcionava perfeitamente e parecia completamente quebrada
Um filtro padrão que só era aplicado ao reabrir o app, porque uma TabView mantém os view models vivos e o estado semeado na construção fica congelado. A correção apagou um conceito.
Uma seção de tracklist, e por que levou 30 minutos
Um método de protocolo, um enum de estado reaproveitado, uma convenção de roteamento que se manteve, e um orçamento de lint que forçou uma divisão que valia a pena fazer de qualquer forma.
A playlist que já tinha o nome certo
Um artefato de renomeação, uma API sem campo de autor pra atualizar, e um teste em dispositivo real provando que o bug já tinha se corrigido sozinho, fechado como aceitar como está.
Você também pode achar útil
Proton VPN
VPN comercial com filtragem NetShield e interruptor de desligamento automático.
Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).
Saiba maiseSIM Airalo
eSIM de dados local para viagens - sem necessidade de trocar um SIM físico.
Este é meu link de indicação da Airalo. Você recebe um desconto no seu primeiro eSIM e eu ganho crédito da Airalo para o meu.
Saiba maisNordPass
Gerenciador de senhas da equipe por trás da NordVPN, com um plano gratuito.
Como afiliado da NordPass, ganho com compras qualificadas.
Saiba mais