DocumentaçãoDocumentationDocumentaciónOne Conecta
ÍndiceIndexÍndice
Baixar .mdDownload .mdBajar .md
Feature · NotificaçõesFeature · NotificationsFeature · Notificaciones

NotificaçõesNotificationsNotificaciones

A caixa de avisos do representante de vendas: uma lista de notificações vindas do backend, cada uma com título e descrição. Marcar como lido é a única escrita — feita ao abrir um aviso não lido, e enviada ao servidor pela transação NotificationRead. O sino no topo do app mostra o contador de não lidos. The sales rep's inbox of alerts: a list of notifications from the backend, each with a title and description. Marking as read is the only write — it happens when you open an unread alert, and is sent to the server via the NotificationRead transaction. The bell at the top of the app shows the unread count. La bandeja de avisos del representante de ventas: una lista de notificaciones del backend, cada una con título y descripción. Marcar como leído es la única escritura — ocurre al abrir un aviso no leído, y se envía al servidor mediante la transacción NotificationRead. La campana en la parte superior de la app muestra el contador de no leídos.

PúblicoAudiencePúblico
Representante · QA · Suporte · DevRep · QA · Support · DevRepresentante · QA · Soporte · Dev
Onde ficaWhereDónde
Sino no topo · menuTop bell · menuCampana arriba · menú
RelacionadoRelatedRelacionado
NotificationRead
AtualizadoUpdatedActualizado
22/07/20262026-07-22
Disponível emAvailable inDisponible en BR CL ZA
01

O que é e para que serveWhat it is and what it's forQué es y para qué sirve

A tela de Notificações é a caixa de avisos do representante de vendas — comunicados enviados pelo backend, listados do mais recente para o mais antigo. Responde três perguntas do dia a dia: The Notifications screen is the sales rep's inbox — messages sent by the backend, listed newest first. It answers three everyday questions: La pantalla de Notificaciones es la bandeja de avisos del representante de ventas — mensajes enviados por el backend, listados del más reciente al más antiguo. Responde tres preguntas del día a día:

Que avisos chegaram?Which alerts arrived?¿Qué avisos llegaron?

Um card por notificação, com o título sempre visível e a descrição ao expandir.One card per notification, title always visible and description on expand.Una tarjeta por notificación, título siempre visible y descripción al expandir.

Já li quais?Which have I read?¿Cuáles ya leí?

Não lidas têm um ponto azul e título em destaque; lidas ficam em cinza. O sino mostra o total de não lidas.Unread ones have a blue dot and a highlighted title; read ones turn grey. The bell shows the unread total.Las no leídas tienen un punto azul y título destacado; las leídas quedan en gris. La campana muestra el total de no leídas.

Como marco como lido?How do I mark as read?¿Cómo marco como leído?

Basta abrir (expandir) um aviso não lido: ele é marcado como lido e o contador do sino diminui.Just open (expand) an unread alert: it gets marked as read and the bell counter drops.Solo abra (expanda) un aviso no leído: se marca como leído y el contador de la campana baja.

O sino é a porta de entradaThe bell is the entry pointLa campana es la puerta O ícone de sino no topo das telas principais (Home, Visitas, Pedidos e outras) carrega um selo vermelho com a contagem de não lidas (9+ acima de nove). Tocar nele abre esta tela. The bell icon at the top of the main screens (Home, Visits, Orders and others) carries a red badge with the unread count (9+ above nine). Tapping it opens this screen. El ícono de campana en la parte superior de las pantallas principales (Home, Visitas, Pedidos y otras) lleva un sello rojo con el conteo de no leídas (9+ sobre nueve). Tocarlo abre esta pantalla.

02

Como acessarHow to openCómo acceder

  1. Pelo sino no topoFrom the top bellDesde la campana arribaToque no ícone de sino na barra superior de qualquer tela principal. A tela é empurrada como rota (com seta de voltar).Tap the bell icon in the top bar of any main screen. The screen is pushed as a route (with a back arrow).Toque el ícono de campana en la barra superior de cualquier pantalla principal. La pantalla se empuja como ruta (con flecha de volver).
  2. Pelo menu lateralFrom the side menuDesde el menú lateralO item Notificações no menu (drawer) leva à mesma tela — presente onde a configuração de mercado o habilita.The Notifications item in the menu (drawer) leads to the same screen — present where market configuration enables it.El ítem Notificaciones en el menú (drawer) lleva a la misma pantalla — presente donde la configuración de mercado lo habilita.
  3. A lista abreThe list opensLa lista abreMostra os avisos mais recentes primeiro. Puxe para baixo para atualizar; role para carregar mais.It shows the newest alerts first. Pull down to refresh; scroll to load more.Muestra los avisos más recientes primero. Deslice hacia abajo para actualizar; desplace para cargar más.
03

Estrutura da telaScreen structureEstructura de la pantalla

Última sincronizaçãoLast syncÚltima sincronización
No topo, a data da última sincronização dos dados (DataLoadInfo).At the top, the last-sync date of the data (DataLoadInfo).Arriba, la fecha de última sincronización de los datos (DataLoadInfo).
CabeçalhoHeaderEncabezado
Ícone de sino + título "Notificações".Bell icon + "Notifications" title.Ícono de campana + título "Notificaciones".
CardsCardsTarjetas
Um por notificação. Fechado: título numa "pílula" (com ponto azul se não lido) e um chevron. Aberto: revela a descrição, com borda e leve sombra.One per notification. Collapsed: title in a "pill" (with a blue dot if unread) and a chevron. Expanded: reveals the description, with border and a light shadow.Una por notificación. Cerrada: título en una "píldora" (con punto azul si no leída) y un chevron. Abierta: revela la descripción, con borde y leve sombra.
Contador "X de Y""X of Y" counterContador "X de Y"
Mostra quantos avisos estão visíveis do total; a lista carrega mais ao rolar (20 por vez).Shows how many alerts are visible out of the total; the list loads more as you scroll (20 at a time).Muestra cuántos avisos están visibles del total; la lista carga más al desplazar (20 por vez).
Estado vazioEmpty stateEstado vacío
Sem avisos, um card central (CustomEmptyState) com o ícone de sino e uma mensagem.With no alerts, a centered card (CustomEmptyState) with the bell icon and a message.Sin avisos, una tarjeta central (CustomEmptyState) con el ícono de campana y un mensaje.
04

Lido e não lidoRead & unreadLeído y no leído

Uma notificação tem apenas dois estados, derivados de haver ou não uma data de leitura (dateRead) — não há tags de status coloridas como em Pedidos:A notification has just two states, derived from whether it has a read date (dateRead) — there are no colored status tags like in Orders:Una notificación tiene solo dos estados, derivados de si tiene o no una fecha de lectura (dateRead) — no hay etiquetas de estado de color como en Pedidos:

Não lido · ponto azul + título em destaqueUnread · blue dot + highlighted titleNo leído · punto azul + título destacado Lido · título em cinza, sem pontoRead · grey title, no dotLeído · título en gris, sin punto

O selo do sinoThe bell badgeEl sello de la campana O número no sino é a contagem de não lidas em cache. Cai assim que um aviso é aberto e marcado como lido. Some (sem selo) quando é zero; mostra 9+ acima de nove. The number on the bell is the unread count from cache. It drops as soon as an alert is opened and marked read. It disappears (no badge) when zero; shows 9+ above nine. El número en la campana es el conteo de no leídas en caché. Baja apenas se abre un aviso y se marca como leído. Desaparece (sin sello) cuando es cero; muestra 9+ sobre nueve.

05

AçõesActionsAcciones

Abrir / recolherOpen / collapseAbrir / plegar

Tocar num card expande a descrição; tocar de novo recolhe. Só um card fica aberto por vez.Tapping a card expands the description; tapping again collapses it. Only one card is open at a time.Tocar una tarjeta expande la descripción; tocar de nuevo la pliega. Solo una tarjeta queda abierta a la vez.

Marcar como lidoMark as readMarcar como leído

Não há botão dedicado: abrir um aviso não lido o marca como lido automaticamente. O app envia a transação NotificationRead ao servidor e, com o retorno positivo, grava a data de leitura no cache e atualiza o selo do sino.There is no dedicated button: opening an unread alert marks it read automatically. The app sends the NotificationRead transaction to the server and, on a positive reply, writes the read date to cache and updates the bell badge.No hay botón dedicado: abrir un aviso no leído lo marca como leído automáticamente. La app envía la transacción NotificationRead al servidor y, con respuesta positiva, graba la fecha de lectura en caché y actualiza el sello de la campana.

Carregar mais · atualizarLoad more · refreshCargar más · actualizar

A lista mostra 20 por vez e carrega mais ao rolar. Puxar para baixo re-busca do servidor. Não há excluir nem editar — fora "marcar como lido", a tela é somente leitura.The list shows 20 at a time and loads more as you scroll. Pulling down re-fetches from the server. There is no delete or edit — apart from "mark as read", the screen is read-only.La lista muestra 20 por vez y carga más al desplazar. Deslizar hacia abajo re-consulta al servidor. No hay eliminar ni editar — aparte de "marcar como leído", la pantalla es solo lectura.

06

Arquitetura e fluxo de dadosArchitecture & data flowArquitectura y flujo de datos

Clean Architecture + Riverpod + Freezed + gRPC + ObjectBox. Há dois fluxos distintos: uma leitura da lista (um RPC getNotifications, com cache write-through) e uma escrita — marcar como lido — que passa pelo Dispatcher (transação NotificationRead) e só então grava no cache local.Clean Architecture + Riverpod + Freezed + gRPC + ObjectBox. There are two distinct flows: a read of the list (one getNotifications RPC, with cache write-through) and a write — mark as read — that goes through the Dispatcher (the NotificationRead transaction) and only then writes to the local cache.Clean Architecture + Riverpod + Freezed + gRPC + ObjectBox. Hay dos flujos distintos: una lectura de la lista (un RPC getNotifications, con cache write-through) y una escritura — marcar como leído — que pasa por el Dispatcher (la transacción NotificationRead) y solo entonces graba en el caché local.

Leitura — a lista atravessa quatro representações ligadas por mappers:Read — the list crosses four representations linked by mappers:Lectura — la lista atraviesa cuatro representaciones unidas por mappers:

  • NotificationsReplygRPC proto
    • toNotificationsDTONotificationsDTODTO · Freezed
      • toDomainNotificationsEntitydomain
        • toModelNotificationsModelObjectBox
          • toDomainNotificationsEntitydomain · cache
            • watchNotificationsNotifier + State
              • → UINotificationsPage

Escrita (marcar como lido) — disparada ao expandir um card não lido; o cache local só é atualizado após o retorno positivo do servidor (remote-first, §36):Write (mark as read) — triggered when expanding an unread card; the local cache is updated only after the server's positive reply (remote-first, §36):Escritura (marcar como leído) — disparada al expandir una tarjeta no leída; el caché local solo se actualiza tras la respuesta positiva del servidor (remote-first, §36):

  • NotificationCardWidgettap (não lido / unread)
    • toggleExpandNotificationsNotifier
      • buildBuildNotificationReadDispatcherPayloadUseCase→ DispatcherEnvelope
        • submitSubmitNotificationReadUseCase
          • dispatchDispatcherOrchestratorsendTransaction · NotificationRead
            • DispatcherAck okmarkAsReadlocal · dateRead = now
              • getCached → invalidateState + unreadNotificationsCountProvider→ sino / bell
07

Modelo de dadosData modelModelo de datos

A mesma notificação existe em quatro representações quase idênticas ao longo das camadas — Proto (wire gRPC) → DTO (Freezed) → Model (ObjectBox) → Entity (domínio) — e cada fronteira é atravessada por um mapper. Os nomes dos campos se mantêm em todas as camadas; muda muito pouco. Diferente de Pedidos, aqui a conversão de data (String→DateTime) acontece já no DTO (o DTO Freezed carrega DateTime), não no Model. O fetch é write-through: todo retorno é gravado no ObjectBox e a UI passa a ler do cache.The same notification exists in four near-identical representations across the layers — Proto (gRPC wire) → DTO (Freezed) → Model (ObjectBox) → Entity (domain) — and each boundary is crossed by a mapper. Field names stay the same across layers; very little changes. Unlike Orders, here the date conversion (String→DateTime) happens already at the DTO (the Freezed DTO carries DateTime), not at the Model. Fetch is write-through: every response is written to ObjectBox and the UI then reads from cache.La misma notificación existe en cuatro representaciones casi idénticas a lo largo de las capas — Proto (wire gRPC) → DTO (Freezed) → Model (ObjectBox) → Entity (dominio) — y cada frontera se cruza con un mapper. Los nombres se mantienen en todas las capas; cambia muy poco. A diferencia de Pedidos, aquí la conversión de fecha (String→DateTime) ocurre ya en el DTO (el DTO Freezed lleva DateTime), no en el Model. El fetch es write-through: toda respuesta se graba en ObjectBox y la UI lee del caché.

A lista chega num container NotificationsEntity (lastSyncAt gerado no mapper + items[]); cada item é uma Notification de 5 campos, sem sub-estruturas. Não há enums tipados — "lido/não lido" é derivado do campo dateRead (getter isRead). A seguir, na ordem: o proto, a estrutura de dados campo-a-campo por camada, e os mappers.The list arrives in a NotificationsEntity container (lastSyncAt generated in the mapper + items[]); each item is a 5-field Notification, with no sub-structures. There are no typed enums — "read/unread" is derived from the dateRead field (isRead getter). Next, in order: the proto, the field-by-field data structure per layer, and the mappers.La lista llega en un container NotificationsEntity (lastSyncAt generado en el mapper + items[]); cada ítem es una Notification de 5 campos, sin sub-estructuras. No hay enums tipados — "leído/no leído" se deriva del campo dateRead (getter isRead). A continuación, en orden: el proto, la estructura de datos campo a campo por capa, y los mappers.

Proto

NotificationsConectaRep.proto · proto3 · package mn.bat.conectarep.streambridge. Um serviço (NotificationsConectaRepService), um método unário:One service (NotificationsConectaRepService), a single unary method:Un servicio (NotificationsConectaRepService), un método unario:

getNotificationsunary
MétodoMethodMétodo

rpc getNotifications(NotificationsRequest) returns (NotificationsReply)

path /mn.bat.conectarep.streambridge.NotificationsConectaRepService/getNotifications

Request · NotificationsRequest
locationHierarchySfid
string · #1 · hierarquia do representante de vendas (resolvida no repository via currentResourceProvider)sales rep hierarchy (resolved in the repository via currentResourceProvider)jerarquía del representante de ventas (resuelta en el repository vía currentResourceProvider)
dateReference
string · #2 · optional
lastModifiedDate
string · #3 · optional (não usado hoje)optional (not used today)optional (no usado hoy)
Reply · NotificationsReply

repeated Notification notificationsa lista de avisos. Os 5 campos de Notification estão detalhados nas Estruturas de dados abaixo.the list of alerts. Notification's 5 fields are detailed in Data structures below.la lista de avisos. Los 5 campos de Notification están detallados en Estructuras de datos abajo.

Estruturas de dadosData structuresEstructuras de datos

Uma estrutura única — Notification, dentro do container NotificationsEntity.items[]. A tabela tem uma coluna por camada — Proto · DTO · Model · Entity; a célula com borda marca onde o tipo primeiro muda (aqui, o parse de data já no DTO).A single structure — Notification, inside the NotificationsEntity.items[] container. The table has one column per layer — Proto · DTO · Model · Entity; the bordered cell marks where the type first changes (here, the date parse already at the DTO).Una estructura única — Notification, dentro del container NotificationsEntity.items[]. La tabla tiene una columna por capa — Proto · DTO · Model · Entity; la celda con borde marca dónde primero cambia el tipo (aquí, el parse de fecha ya en el DTO).

  • Notification raiz · NotificationsEntity.items[] 5 campos
    CampoProtoDTOModelEntity
    sfidstringStringStringString
    namestringStringStringString
    descriptionstringStringStringString
    startDatestringDateTimeDateTimeDateTime
    dateReadstringDateTime?DateTime?DateTime?

Mappers

As conversões entre as camadas, todas como extension (5 direções). O container (Notifications*) e o item (Notification*) têm mappers próprios; o container itera os itens.The conversions between layers, all as extensions (5 directions). Container (Notifications*) and item (Notification*) have their own mappers; the container iterates the items.Las conversiones entre capas, todas como extension (5 direcciones). El container (Notifications*) y el ítem (Notification*) tienen mappers propios; el container itera los ítems.

DireçãoDirectionDirecciónMétodoMethodMétodo
JSON → DTOstatic fromMap(Map) (parseia ISO → DateTime; carimba lastSyncAt)(parses ISO → DateTime; stamps lastSyncAt)(parsea ISO → DateTime; sella lastSyncAt)
Proto → DTOtoNotificationsDTO() / toDTO() (parseia ISO; carimba lastSyncAt)(parses ISO; stamps lastSyncAt)(parsea ISO; sella lastSyncAt)
DTO → EntitytoDomain()
Entity → ModeltoModel() (popula ToMany; exige lastSyncAt)(fills ToMany; requires lastSyncAt)(llena ToMany; exige lastSyncAt)
Model → EntitytoDomain()

Os únicos deltasThe only deltasLos únicos deltas

  • startDate / dateRead StringDateTime/DateTime? parseados já no DTO (via DateTimeUtils.tryParse); vazio/inválido → epoch() (obrigatório) ou nullparsed already at the DTO (via DateTimeUtils.tryParse); empty/invalid → epoch() (required) or nullparseados ya en el DTO (vía DateTimeUtils.tryParse); vacío/inválido → epoch() (obligatorio) o null
  • items vira ToMany<NotificationModel> no Modelbecomes ToMany<NotificationModel> in the Modelpasa a ToMany<NotificationModel> en el Model
  • lastSyncAt gerado no mapper de fronteira com DateTimeUtils.now() (JSON e proto)generated in the boundary mapper with DateTimeUtils.now() (JSON and proto)generado en el mapper de frontera con DateTimeUtils.now() (JSON y proto)
  • toModel() lança StateError se lastSyncAt for null (guarda de persistência)throws StateError if lastSyncAt is null (persistence guard)lanza StateError si lastSyncAt es null (guarda de persistencia)
  • nenhum enum tipado — "lido/não lido" é o getter isRead (dateRead != null)no typed enums — "read/unread" is the isRead getter (dateRead != null)ningún enum tipado — "leído/no leído" es el getter isRead (dateRead != null)
08

Repository

NotificationsRepositoryImpl implementaimplementsimplementa NotificationsRepositoryInterface e injeta os 3 datasources (mock/local/remote) + ConnectivityService + a flag useMockData + Ref. Método a método:and injects the 3 datasources (mock/local/remote) + ConnectivityService + the useMockData flag + Ref. Method by method:e inyecta los 3 datasources (mock/local/remote) + ConnectivityService + la flag useMockData + Ref. Método a método:

Um dropdown por método — assinatura, retorno e comportamento. O getNotifications() traz a árvore de decisão de fonte dentro do próprio detalhe.One dropdown per method — signature, return and behavior. getNotifications() carries the source decision tree inside its own detail.Un dropdown por método — firma, retorno y comportamiento. getNotifications() trae el árbol de decisión de fuente dentro de su propio detalle.

getNotifications({source}) mock / local / remote

RetornaReturnsDevuelve Result<NotificationsEntity?, Failure>

Ponto de entrada da lista: decide a fonte pela source + flags, mapeia e grava no cache (write-through). Chamado pelo GetNotificationsUseCase.List entry point: picks the source from source + flags, maps and writes to cache (write-through). Called by GetNotificationsUseCase.Punto de entrada de la lista: elige la fuente por source + flags, mapea y graba en caché (write-through). Llamado por GetNotificationsUseCase.

Árvore de decisão de fonteSource decision treeÁrbol de decisión de fuente

  1. useMockData == true ouoro source == mock_fetchFromMock(): lê o mock, mapeia, grava no cache. A flag global tem precedência máxima._fetchFromMock(): reads the mock, maps, writes to cache. The global flag has top precedence._fetchFromMock(): lee el mock, mapea, graba en caché. La flag global tiene máxima precedencia.
  2. source == local ou offlineor offlineu offlinegetCachedNotifications() (sem rede).getCachedNotifications() (no network).getCachedNotifications() (sin red).
  3. senão (remoto + conectado)otherwise (remote + connected)si no (remoto + conectado)_fetchFromRemoteWithFallback(): lê currentResourceProvider; se null cai pro cache; senão chama o remoto com locationHierarchyId, mapeia, grava no cache; em erro, fallback pro cache (só falha se o cache também estiver vazio)._fetchFromRemoteWithFallback(): reads currentResourceProvider; if null falls back to cache; else calls remote with locationHierarchyId, maps, writes to cache; on error, falls back to cache (only fails if the cache is empty too)._fetchFromRemoteWithFallback(): lee currentResourceProvider; si null cae al caché; si no llama al remoto con locationHierarchyId, mapea, graba en caché; en error, fallback al caché (solo falla si el caché también está vacío).
getCachedNotifications() local

RetornaReturnsDevuelve Result<NotificationsEntity?, Failure>

Só cache. null vira Success(null), não erro — quem chama trata "sem dados" sem falha.Cache only. null becomes Success(null), not an error — callers handle "no data" without a failure.Solo caché. null es Success(null), no error — quien llama trata "sin datos" sin fallo.

getCachedNotificationsLastSyncAt() local

RetornaReturnsDevuelve DateTime? (sem Result)(no Result)(sin Result)

Timestamp da última sincronização do container, para o DataLoadInfo. Em erro retorna null.The container's last-sync timestamp, for DataLoadInfo. Returns null on error.Timestamp de última sincronización del container, para DataLoadInfo. En error devuelve null.

saveNotifications({entity}) local

RetornaReturnsDevuelve Result<void, Failure>

Destrutivo: clearNotifications() + regrava o container (cascata da box filha). É o cache-writer chamado após cada fetch bem-sucedido.Destructive: clearNotifications() + rewrites the container (child box cascade). It's the cache-writer called after each successful fetch.Destructivo: clearNotifications() + regraba el container (cascada de la box hija). Es el cache-writer llamado tras cada fetch exitoso.

markAsRead({sfid}) local · pós-Dispatcherpost-Dispatcherpost-Dispatcher

RetornaReturnsDevuelve Result<void, Failure>

Carimba dateRead = DateTimeUtils.now() na notificação do cache (busca linear pelo sfid). Escrita local do fluxo remote-first: o Notifier só chama isto após o DispatcherAck positivo da transação NotificationRead.Stamps dateRead = DateTimeUtils.now() on the cached notification (linear lookup by sfid). Local write of the remote-first flow: the Notifier only calls this after the positive DispatcherAck from the NotificationRead transaction.Sella dateRead = DateTimeUtils.now() en la notificación del caché (búsqueda lineal por sfid). Escritura local del flujo remote-first: el Notifier solo llama esto tras el DispatcherAck positivo de la transacción NotificationRead.

getUnreadCount() local

RetornaReturnsDevuelve Result<int, Failure>

Conta, no cache, as notificações com dateRead == null. Alimenta o selo do sino via unreadNotificationsCountProvider.Counts, in cache, the notifications with dateRead == null. Feeds the bell badge via unreadNotificationsCountProvider.Cuenta, en caché, las notificaciones con dateRead == null. Alimenta el sello de la campana vía unreadNotificationsCountProvider.

09

Datasources

Um card por datasource (dropdown). No corpo: método, envio, retorno, fluxo de uso e tratamento de erro.One card per datasource (dropdown). In the body: method, what it sends, return, usage flow and error handling.Un card por datasource (dropdown). En el cuerpo: método, envío, retorno, flujo de uso y manejo de errores.

Remote NotificationsRemoteDataSource gRPC
getNotifications({locationHierarchySfid, dateReference?, lastModifiedDate?})
EnvioSendsEnvío
monta NotificationsRequest e chama _client.getNotifications(request) no NotificationsConectaRepServiceClient (via notificationsServiceClientProvider).builds NotificationsRequest and calls _client.getNotifications(request) on NotificationsConectaRepServiceClient (via notificationsServiceClientProvider).arma NotificationsRequest y llama _client.getNotifications(request) en NotificationsConectaRepServiceClient (vía notificationsServiceClientProvider).
RetornoReturnRetorno
NotificationsDTO (via response.toNotificationsDTO())(via response.toNotificationsDTO())(vía response.toNotificationsDTO())
Fluxo de usoUsage flowFlujo de uso
chamado pelo caminho remoto do repository (_fetchFromRemoteWithFallback), quando online e sem mock; o resultado é gravado no cache.called by the repository's remote path (_fetchFromRemoteWithFallback), when online and not mocking; the result is written to cache.llamado por el camino remoto del repository (_fetchFromRemoteWithFallback), online y sin mock; el resultado se graba en caché.
Tratamento de erroError handlingManejo de errores
GrpcErrorGrpcExceptionHandler; outros → ServerException. Em erro, o repository faz fallback pro cache.GrpcErrorGrpcExceptionHandler; others → ServerException. On error, the repository falls back to cache.GrpcErrorGrpcExceptionHandler; otros → ServerException. En error, el repository hace fallback al caché.
Local NotificationsLocalDataSource ObjectBox

Envio / fluxo: persistência local via ObjectBox (ObjectBoxDatabase), boxes NotificationsModel e NotificationModel — sem rede. Alimenta os caminhos cache do repository e a contagem do sino. Erro: falhas de persistência propagam como CacheException (não engolidas).Sends / flow: local persistence via ObjectBox (ObjectBoxDatabase), NotificationsModel and NotificationModel boxes — no network. Feeds the repository's cache paths and the bell count. Error: persistence failures propagate as CacheException (not swallowed).Envío / flujo: persistencia local vía ObjectBox (ObjectBoxDatabase), boxes NotificationsModel y NotificationModel — sin red. Alimenta los caminos caché del repository y el conteo de la campana. Error: fallos de persistencia propagan como CacheException (no tragados).

getNotifications()
RetornoReturnRetorno
NotificationsEntity?
ComportamentoBehaviorComportamiento
models.first.toDomain() — o agregado único, ou null se o cache está vazio.models.first.toDomain() — the single aggregate, or null if the cache is empty.models.first.toDomain() — el agregado único, o null si el caché está vacío.
getNotificationsLastSyncAt()
RetornoReturnRetorno
DateTime?
ComportamentoBehaviorComportamiento
models.first.lastSyncAt — timestamp da última sync do container.— the container's last-sync timestamp.— timestamp de última sincronización del container.
getUnreadCount()
RetornoReturnRetorno
int
ComportamentoBehaviorComportamiento
itera _notificationBox.getAll() e conta os itens com dateRead == null.iterates _notificationBox.getAll() and counts items with dateRead == null.itera _notificationBox.getAll() y cuenta los ítems con dateRead == null.
saveNotifications({entity})
RetornoReturnRetorno
void
ComportamentoBehaviorComportamiento
destrutivo: clearNotifications() + _box.put(entity.toModel()) (grava a box filha em cascata). Cache-writer após cada fetch.destructive: clearNotifications() + _box.put(entity.toModel()) (writes the child box in cascade). Cache-writer after each fetch.destructivo: clearNotifications() + _box.put(entity.toModel()) (graba la box hija en cascada). Cache-writer tras cada fetch.
markAsRead({sfid, dateRead})
RetornoReturnRetorno
void
ComportamentoBehaviorComportamiento
busca linear pelo sfid, seta dateRead e regrava o NotificationModel. Ausente → CacheException.linear lookup by sfid, sets dateRead and re-puts the NotificationModel. Missing → CacheException.búsqueda lineal por sfid, setea dateRead y regraba el NotificationModel. Ausente → CacheException.
clearNotifications()
RetornoReturnRetorno
void
ComportamentoBehaviorComportamiento
limpa as boxes na ordem filha→raiz (NotificationModel depois NotificationsModel).clears the boxes child→root (NotificationModel then NotificationsModel).limpia las boxes hija→raíz (NotificationModel luego NotificationsModel).
Mock NotificationsMockDataSource JSON
getNotifications()
EnvioSendsEnvío
carrega o asset JSON notifications/notifications (por mercado, real vs sintético via useRealMockData) — sem rede.loads the JSON asset notifications/notifications (per market, real vs synthetic via useRealMockData) — no network.carga el asset JSON notifications/notifications (por mercado, real vs sintético vía useRealMockData) — sin red.
RetornoReturnRetorno
NotificationsDTO (via NotificationsDTOJsonMapper.fromMap)(via NotificationsDTOJsonMapper.fromMap)(vía NotificationsDTOJsonMapper.fromMap)
Fluxo de usoUsage flowFlujo de uso
usado quando useMockData está ligado ou source == mock; grava no cache como um fetch normal.used when useMockData is on or source == mock; writes to cache like a normal fetch.usado cuando useMockData está activo o source == mock; graba en caché como un fetch normal.
Tratamento de erroError handlingManejo de errores
asset ausente ou JSON inválido propaga como CacheException (sem rede envolvida).missing asset or invalid JSON propagates as a CacheException (no network involved).asset ausente o JSON inválido propaga como CacheException (sin red involucrada).
10

Enums e labelsEnums & labelsEnums y labels

Esta feature não define enums de domínio. Uma notificação tem só dois estados, derivados de um campo — não de um enum:This feature defines no domain enums. A notification has just two states, derived from a field — not from an enum:Esta feature no define enums de dominio. Una notificación tiene solo dos estados, derivados de un campo — no de un enum:

NotificationEntity.isRead
boolgetter puro = dateRead != null. É toda a lógica de "lido/não lido".pure getter = dateRead != null. This is the entire "read/unread" logic.getter puro = dateRead != null. Es toda la lógica de "leído/no leído".
DataSourceType
enum de infra compartilhado (mock / local / remote), usado como parâmetro de fonte no repository — não é próprio de Notificações.shared infra enum (mock / local / remote), used as the source parameter in the repository — not specific to Notifications.enum de infra compartido (mock / local / remote), usado como parámetro de fuente en el repository — no propio de Notificaciones.
11

UseCases

Um dropdown por UseCase; dentro, cada método com assinatura, o que retorna e uso. Os UseCases de leitura delegam ao repository (sem lógica extra); os dois da pasta dispatcher/notifications/ montam e enviam a transação de escrita. Todos os providers são keepAlive.One dropdown per UseCase; inside, each method with its signature, what it returns and use. The read UseCases delegate to the repository (no extra logic); the two in dispatcher/notifications/ build and send the write transaction. All providers are keepAlive.Un dropdown por UseCase; dentro, cada método con su firma, qué devuelve y uso. Los UseCases de lectura delegan al repository (sin lógica extra); los dos de la carpeta dispatcher/notifications/ arman y envían la transacción de escritura. Todos los providers son keepAlive.

GetNotificationsUseCase 3 · a listathe listla lista
MétodoMethodMétodoRetornaReturnsDevuelveUsoUseUso
execute({source})Result<NotificationsEntity?, Failure>Ponto de entrada da lista. Roteia por source (mock/local/remoto) → repository.getNotifications. Chamado pelo Notifier e (cold-start) pelo provider do sino.List entry point. Routes by source (mock/local/remote) → repository.getNotifications. Called by the Notifier and (cold-start) by the bell provider.Punto de entrada de la lista. Rutea por source (mock/local/remoto) → repository.getNotifications. Llamado por el Notifier y (cold-start) por el provider de la campana.
getCached()Result<NotificationsEntity?, Failure>Só cache; null vira Success(null). Usado após marcar como lido (re-lê o cache) e pelo provider do sino.Cache only; null becomes Success(null). Used after marking read (re-reads cache) and by the bell provider.Solo caché; null es Success(null). Usado tras marcar como leído (re-lee el caché) y por el provider de la campana.
getCachedLastSyncAt()DateTime?Timestamp da última sincronização (sem Result). Alimenta o DataLoadInfo.Last-sync timestamp (no Result). Feeds DataLoadInfo.Timestamp de última sincronización (sin Result). Alimenta DataLoadInfo.
GetUnreadNotificationsCountUseCase 1
MétodoMethodMétodoRetornaReturnsDevuelveUsoUseUso
execute()Result<int, Failure>Conta as não lidas no cache → repository.getUnreadCount. Consumido pelo unreadNotificationsCountProvider (selo do sino).Counts unread in cache → repository.getUnreadCount. Consumed by unreadNotificationsCountProvider (bell badge).Cuenta las no leídas en caché → repository.getUnreadCount. Consumido por unreadNotificationsCountProvider (sello de la campana).
MarkNotificationAsReadUseCase 1
MétodoMethodMétodoRetornaReturnsDevuelveUsoUseUso
execute({sfid})Result<void, Failure>Escrita local do dateReadrepository.markAsRead. Chamado pelo Notifier após o ack da transação NotificationRead.Local write of dateReadrepository.markAsRead. Called by the Notifier after the NotificationRead transaction ack.Escritura local del dateReadrepository.markAsRead. Llamado por el Notifier tras el ack de la transacción NotificationRead.
BuildNotificationReadDispatcherPayloadUseCase 1 · builderbuilderbuilder
MétodoMethodMétodoRetornaReturnsDevuelveUsoUseUso
build({input})DispatcherEnvelopeimplements DispatcherPayloadBuilder (§36). Do NotificationReadDispatcherPayloadInput (cru) monta o payload JSON e o envelope (type = notificationRead, serviceName = "NotificationRead", dateReference formatada de submittedAt). Cross-link → NotificationRead.implements DispatcherPayloadBuilder (§36). From the raw NotificationReadDispatcherPayloadInput builds the JSON payload and the envelope (type = notificationRead, serviceName = "NotificationRead", dateReference formatted from submittedAt). Cross-link → NotificationRead.implements DispatcherPayloadBuilder (§36). Del NotificationReadDispatcherPayloadInput (crudo) arma el payload JSON y el envelope (type = notificationRead, serviceName = "NotificationRead", dateReference formateada de submittedAt). Cross-link → NotificationRead.
SubmitNotificationReadUseCase 1 · enviosubmitenvío
MétodoMethodMétodoRetornaReturnsDevuelveUsoUseUso
submit({envelope})Result<DispatcherAck, Failure>Delega ao DispatcherOrchestrator.dispatch (store, tracking, reenvio idempotente). É o envio remoto do fluxo remote-first.Delegates to DispatcherOrchestrator.dispatch (store, tracking, idempotent resend). It's the remote send of the remote-first flow.Delega a DispatcherOrchestrator.dispatch (store, tracking, reenvío idempotente). Es el envío remoto del flujo remote-first.
12

Notifier & State

O NotificationsNotifier (@riverpod, with AsyncGuard<NotificationsState>) é o cérebro da tela. O build() observa os UseCases (leitura + os dois do Dispatcher) e retorna _load(). O State (NotificationsState, Freezed) é a fonte única de verdade da page: guarda o AsyncValue das notificações, o expandedSfid (qual card está aberto) e o visibleCount (paginação visual). Contagens e recortes são getters — client-side.The NotificationsNotifier (@riverpod, with AsyncGuard<NotificationsState>) is the screen's brain. build() watches the UseCases (read + the two Dispatcher ones) and returns _load(). The State (NotificationsState, Freezed) is the page's single source of truth: it holds the notifications AsyncValue, the expandedSfid (which card is open) and visibleCount (visual pagination). Counts and slices are getters — client-side.El NotificationsNotifier (@riverpod, with AsyncGuard<NotificationsState>) es el cerebro de la pantalla. build() observa los UseCases (lectura + los dos del Dispatcher) y retorna _load(). El State (NotificationsState, Freezed) es la fuente única de verdad de la page: guarda el AsyncValue de las notificaciones, el expandedSfid (qué card está abierto) y el visibleCount (paginación visual). Conteos y recortes son getters — client-side.

MétodosMethodsMétodos

build() async

RetornoReturnRetorno FutureOr<NotificationsState>

Magro: observa os 4 UseCases (get, mark-as-read, build-payload, submit) e retorna guardedBuild(() => _load()).Thin: watches the 4 UseCases (get, mark-as-read, build-payload, submit) and returns guardedBuild(() => _load()).Delgado: observa los 4 UseCases (get, mark-as-read, build-payload, submit) y retorna guardedBuild(() => _load()).

toggleExpand({sfid}) abre + marca como lidoopen + mark readabre + marca leído

RetornoReturnRetorno Future<void>

Se o card já está aberto, fecha (expandedSfid = null). Senão abre (expandedSfid = sfid). Se o aviso estava não lido, dispara a escrita: buildsubmit (Dispatcher); só com o ack positivo chama markAsRead, re-lê o cache (getCached), atualiza o State e faz ref.invalidate(unreadNotificationsCountProvider) (o selo do sino cai). Falha em qualquer etapa → retorna sem marcar.If the card is already open, it closes (expandedSfid = null). Otherwise it opens (expandedSfid = sfid). If the alert was unread, it fires the write: buildsubmit (Dispatcher); only on a positive ack it calls markAsRead, re-reads the cache (getCached), updates the State and runs ref.invalidate(unreadNotificationsCountProvider) (the bell badge drops). A failure at any step → returns without marking.Si el card ya está abierto, cierra (expandedSfid = null). Si no, abre (expandedSfid = sfid). Si el aviso estaba no leído, dispara la escritura: buildsubmit (Dispatcher); solo con el ack positivo llama markAsRead, re-lee el caché (getCached), actualiza el State y hace ref.invalidate(unreadNotificationsCountProvider) (el sello de la campana baja). Fallo en cualquier paso → retorna sin marcar.

refresh() pull-to-refresh

RetornoReturnRetorno Future<void>

Null-guard no state.value; invalida o unreadNotificationsCountProvider e recarrega via runGuarded(() => _load(source: remote)). Não seta AsyncValue.loading (o pull-to-refresh tem indicador próprio).Null-guards state.value; invalidates unreadNotificationsCountProvider and reloads via runGuarded(() => _load(source: remote)). Doesn't set AsyncValue.loading (pull-to-refresh has its own indicator).Null-guard en state.value; invalida unreadNotificationsCountProvider y recarga vía runGuarded(() => _load(source: remote)). No setea AsyncValue.loading (pull-to-refresh tiene su propio indicador).

loadMore()

RetornoReturnRetorno void

Incrementa visibleCount em 20 (kNotificationsPageSize), com clamp no total. Paginação apenas visual — os dados já estão em memória.Increments visibleCount by 20 (kNotificationsPageSize), clamped to the total. Visual-only pagination — data is already in memory.Incrementa visibleCount en 20 (kNotificationsPageSize), con clamp al total. Paginación solo visual — los datos ya están en memoria.

_load({source}) · _fetchNotifications({source}) private

RetornoReturnRetorno Future<NotificationsState> / Future<NotificationsEntity>

Dono único da montagem do State: _fetchNotifications chama execute(source) e faz getOrThrow() ?? const NotificationsEntity(); _load embrulha num NotificationsState. Chamado por build() e refresh().Sole owner of State assembly: _fetchNotifications calls execute(source) and does getOrThrow() ?? const NotificationsEntity(); _load wraps it in a NotificationsState. Called by build() and refresh().Dueño único del armado del State: _fetchNotifications llama execute(source) y hace getOrThrow() ?? const NotificationsEntity(); _load lo envuelve en un NotificationsState. Llamado por build() y refresh().

Selector compartilhado do sinoShared bell selectorSelector compartido de la campana

unreadNotificationsCountProvider shared/providers/notifications selo do sinobell badgesello campana

RetornoReturnRetorno FutureProvider<int>

Selector cross-page (presentation-only): lê o cache; se estiver frio (null/erro), faz um execute() para popular; então retorna getUnreadCount. Consumido pelo AppBarBellButton nas telas principais do app. O Notifier o invalida ao marcar como lido e no refresh.Cross-page selector (presentation-only): reads the cache; if cold (null/error), runs an execute() to populate; then returns getUnreadCount. Consumed by AppBarBellButton across the app's main screens. The Notifier invalidates it on mark-read and on refresh.Selector cross-page (presentation-only): lee el caché; si está frío (null/error), hace un execute() para poblar; luego retorna getUnreadCount. Consumido por AppBarBellButton en las pantallas principales de la app. El Notifier lo invalida al marcar como leído y en el refresh.

State disponível para a PageState available to the PageState disponible para la Page

NotificationsState campos + gettersfields + getterscampos + getters
campotipodefault
notificationsAsyncValue<NotificationsEntity>.loading()
expandedSfidString?null
visibleCountint20

Getters: lastSyncAt, items, visibleItems (recorte por visibleCount), totalItems, hasMoreToLoad, unreadCount, isExpanded({sfid}). Constante kNotificationsPageSize = 20.Getters: lastSyncAt, items, visibleItems (slice by visibleCount), totalItems, hasMoreToLoad, unreadCount, isExpanded({sfid}). Constant kNotificationsPageSize = 20.Getters: lastSyncAt, items, visibleItems (recorte por visibleCount), totalItems, hasMoreToLoad, unreadCount, isExpanded({sfid}). Constante kNotificationsPageSize = 20.

13

Page e widgetsPage & widgetsPage y widgets

A NotificationsPage (ConsumerWidget) observa o notificationsProvider e monta os widgets filhos. Loading e erro são globais (notificationsAsync.when); o conteúdo existe só no ramo data. O AppBarBellButton — porta de entrada e selo — vive na barra superior das telas principais, não nesta árvore. Árvore de composição:NotificationsPage (ConsumerWidget) watches notificationsProvider and composes the child widgets. Loading and error are global (notificationsAsync.when); content exists only in the data branch. The AppBarBellButton — entry point and badge — lives in the top bar of the main screens, not in this tree. Composition tree:NotificationsPage (ConsumerWidget) observa notificationsProvider y compone los widgets hijos. Loading y error son globales (notificationsAsync.when); el contenido existe solo en la rama data. El AppBarBellButton — puerta de entrada y sello — vive en la barra superior de las pantallas principales, no en este árbol. Árbol de composición:

  • NotificationsPage
    • AppPageShell displayBackButton · background
      • CustomLoadingIndicator loading
      • FailureStateView error → invalidate
      • CustomPullToRefresh data → refresh()
        • DataLoadInfo lastSyncAt
        • NotificationsHeaderWidget sino + título
        • NotificationsListWidget
          • CustomEmptyState totalItems == 0
          • InfiniteScrollListView → loadMore()
            • NotificationCardWidget tap → toggleExpand · ponto azul (não lido) · chevron · expand → descrição
          • PaginationCountIndicator X de Y

O scroll infinito também é apoiado por um ScrollController no corpo da page, que chama loadMore() ao aproximar do fim. Não há modais nesta feature.Infinite scroll is also backed by a ScrollController in the page body, calling loadMore() near the end. There are no modals in this feature.El scroll infinito también se apoya en un ScrollController en el cuerpo de la page, que llama loadMore() al acercarse al final. No hay modales en esta feature.

Notas por mercadoMarket notesNotas por mercado

Notificações é dirigido por configuração de mercado (End Market Configuration): o item notifications no menuConfig habilita a tela. Está presente em três mercados, os mesmos em que a transação de escrita NotificationRead está habilitada (DispatcherType.notificationRead.enabledMarkets):Notifications is driven by market configuration (End Market Configuration): the notifications item in menuConfig enables the screen. It's present in three markets — the same ones where the NotificationRead write transaction is enabled (DispatcherType.notificationRead.enabledMarkets):Notificaciones se rige por configuración de mercado (End Market Configuration): el ítem notifications en menuConfig habilita la pantalla. Está presente en tres mercados — los mismos donde la transacción de escritura NotificationRead está habilitada (DispatcherType.notificationRead.enabledMarkets):

BRx CLx ZAx AR PY PE
disponívelavailabledisponible presente, desligadopresent, offpresente, apagado ausenteabsentausente
BR

BrasilBrazilBrasil Tela e transação NotificationRead habilitadas. Screen and NotificationRead transaction enabled. Pantalla y transacción NotificationRead habilitadas.

CL

ChileChileChile Tela e transação NotificationRead habilitadas. Screen and NotificationRead transaction enabled. Pantalla y transacción NotificationRead habilitadas.

ZA

África do SulSouth AfricaSudáfrica Tela e transação NotificationRead habilitadas. Screen and NotificationRead transaction enabled. Pantalla y transacción NotificationRead habilitadas.

AR · PY · PE Existem como mercados do app (config PANGEA mínima), mas não têm bloco no End Market Configuration — sem item notifications no menu, a tela é inalcançável e a transação NotificationRead não está entre os enabledMarkets. Ficam ⚪ ausentes. They exist as app markets (minimal PANGEA config), but have no End Market Configuration block — with no notifications menu item, the screen is unreachable and the NotificationRead transaction isn't among its enabledMarkets. They stay ⚪ absent. Existen como mercados de la app (config PANGEA mínima), pero no tienen bloque en el End Market Configuration — sin ítem notifications en el menú, la pantalla es inalcanzable y la transacción NotificationRead no está entre sus enabledMarkets. Quedan ⚪ ausentes.