From 8b2307788ff995039eaca3e3f26bd6b77ca0b0d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Fonkam?= Date: Wed, 19 Jun 2024 00:38:11 +0000 Subject: [PATCH 01/36] chore: Added multi-language readme for a broader audience reach --- README.es.md | 170 +++++++++++++++++++++++++ README.md | 289 ++++++++++-------------------------------- README.pt-br.md | 327 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 561 insertions(+), 225 deletions(-) create mode 100644 README.es.md create mode 100644 README.pt-br.md diff --git a/README.es.md b/README.es.md new file mode 100644 index 0000000..d101c83 --- /dev/null +++ b/README.es.md @@ -0,0 +1,170 @@ +[![es](https://img.shields.io/badge/lang-es-yellow.svg)](https://github.com/ditointernet/sdk_mobile_flutter/blob/main/README.es.md) + +```markdown +# Dito SDK (Flutter) + +DitoSDK es una biblioteca Dart que proporciona métodos para integrar aplicaciones con la plataforma de Dito. Permite identificar usuarios, registrar eventos y enviar datos personalizados. + +## Instalación + +Para instalar la biblioteca DitoSDK en tu aplicación Flutter, debes seguir las instrucciones proporcionadas [en este enlace](https://pub.dev/packages/dito_sdk/install). + +## Métodos + +### initialize() + +Este método debe ser llamado antes de cualquier otra operación con el SDK. Inicializa las claves de API y SECRET necesarias para la autenticación en la plataforma Dito. + +```dart +void initialize({required String apiKey, required String secretKey}); +``` + +#### Parámetros + +- **apiKey** _(String, obligatorio)_: La clave de API de la plataforma Dito. +- **secretKey** _(String, obligatorio)_: El secreto de la clave de API de la plataforma Dito. + +### initializePushNotificationService() + +Este método debe ser llamado después de la inicialización del SDK. Inicializa las configuraciones y servicios necesarios para el funcionamiento de notificaciones push de la plataforma Dito. + +```dart +void initializePushNotificationService(); +``` + +#### Parámetros + +- **apiKey** _(String, obligatorio)_: La clave de API de la plataforma Dito. +- **secretKey** _(String, obligatorio)_: El secreto de la clave de API de la plataforma Dito. + +### identify() + +Este método define el ID del usuario que se utilizará para todas las operaciones subsiguientes. + +```dart +void identify(String userId); +``` + +- **userID** _(String, obligatorio)_: ID para identificar al usuario en la plataforma Dito. +- **name** _(String)_: Parámetro para identificar al usuario en la plataforma Dito. +- **email** _(String)_: Parámetro para identificar al usuario en la plataforma Dito. +- **gender** _(String)_: Parámetro para identificar al usuario en la plataforma Dito. +- **birthday** _(String)_: Parámetro para identificar al usuario en la plataforma Dito. +- **location** _(String)_: Parámetro para identificar al usuario en la plataforma Dito. +- **customData** _(Map)_: Datos personalizados para identificar al usuario en la plataforma Dito. + +### trackEvent() + +Este método permite registrar eventos personalizados en la plataforma Dito. + +```dart +void trackEvent({ + required String eventName, + double? revenue, + Map? customData, +}); +``` + +- **eventName** _(String, obligatorio)_: El nombre del evento a ser registrado. +- **revenue** _(double, opcional)_: Ingreso generado por el evento. +- **customData** _(Map, opcional)_: Datos personalizados relacionados con el evento. + +### Ejemplo de Uso + +```dart +import 'package:dito_sdk/dito_sdk.dart'; + +final dito = DitoSDK(); + +dito.initialize( + apiKey: 'tu_api_key', + secretKey: 'tu_secret_key', +); + +dito.identify( + userId: 'id_del_usuario', + name: 'Nombre del Usuario', + email: 'email@ejemplo.com', + gender: 'género', + birthday: 'fecha_de_nacimiento', + location: 'ubicación', + customData: { + 'llave_personalizada': 'valor_personalizado', + }, +); + +dito.trackEvent( + eventName: 'nombre_del_evento', + revenue: 100.0, + customData: { + 'llave_personalizada': 'valor_personalizado', + }, +); +``` + +Puedes llamar al método `identify()` en cualquier momento para agregar o actualizar los detalles del usuario, y solo cuando sea necesario, enviarlos a través del método `identifyUser()`. + +#### archivoZ.dart + +```dart +import 'package:dito_sdk/dito_sdk.dart'; + +final dito = DitoSDK(); + +// Registra un evento en Dito +dito.trackEvent( + eventName: 'compró producto', + revenue: 99.90, + customData: { + 'producto': 'productoX', + 'sku_producto': '99999999', + 'método_pago': 'Visa', + }, +); +``` + +### Uso del SDK con notificaciones push: + +Para el funcionamiento es necesario configurar la biblioteca de Firebase Cloud Message (FCM), siguiendo los siguientes pasos: + +```shell +dart pub global activate flutterfire_cli +flutter pub add firebase_core firebase_messaging +``` + +```shell +flutterfire configure +``` + +Sigue los pasos que aparecerán en la CLI, así tendrás las claves de acceso de Firebase configuradas dentro de las aplicaciones Android e iOS. + +#### main.dart +```dart +import 'package:dito_sdk/dito_sdk.dart'; + +// Método para registrar un servicio que recibirá los push cuando la aplicación esté completamente cerrada +@pragma('vm:entry-point') +Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { + final notification = DataPayload.fromJson(jsonDecode(message.data["data"])); + + dito.notificationService().showLocalNotification(CustomNotification( + id: message.hashCode, + title: notification.details.title || "El nombre de la aplicación", + body: notification.details.message, + payload: notification)); +} + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + + await Firebase.initializeApp(); + FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); + + DitoSDK dito = DitoSDK(); + dito.initialize(apiKey: 'tu_api_key', secretKey: 'tu_secret_key'); + await dito.initializePushService(); +} +``` + +> Recuerda reemplazar 'tu_api_key', 'tu_secret_key' y 'id_del_usuario' por los valores correctos en tu entorno. +``` \ No newline at end of file diff --git a/README.md b/README.md index b9bc54a..5925c41 100644 --- a/README.md +++ b/README.md @@ -1,287 +1,124 @@ +[![en](https://img.shields.io/badge/lang-en-red.svg)](https://github.com/ditointernet/sdk_mobile_flutter/blob/main/README.md) +[![pt-br](https://img.shields.io/badge/lang-pt--br-green.svg)](https://github.com/ditointernet/sdk_mobile_flutter/blob/main/README.pt-br.md) +[![es](https://img.shields.io/badge/lang-es-yellow.svg)](https://github.com/ditointernet/sdk_mobile_flutter/blob/main/README.es.md) + +```markdown # Dito SDK (Flutter) -DitoSDK é uma biblioteca Dart que fornece métodos para integrar aplicativos com a plataforma da Dito. Ela permite identificar usuários, registrar eventos e enviar dados personalizados. +DitoSDK is a Dart library that provides methods to integrate applications with the Dito platform. It allows user identification, event registration, and sending custom data. -## Instalação +## Installation -Para instalar a biblioteca DitoSDK em seu aplicativo Flutter, você deve seguir as instruções fornecidas [nesse link](https://pub.dev/packages/dito_sdk/install). +To install the DitoSDK library in your Flutter application, you should follow the instructions provided [at this link](https://pub.dev/packages/dito_sdk/install). -## Métodos +## Methods ### initialize() -Este método deve ser chamado antes de qualquer outra operação com o SDK. Ele inicializa as chaves de API e SECRET necessárias para a autenticação na plataforma Dito. +This method must be called before any other operation with the SDK. It initializes the API and SECRET keys necessary for authentication on the Dito platform. ```dart void initialize({required String apiKey, required String secretKey}); ``` -#### Parâmetros +#### Parameters -- **apiKey** _(String, obrigatório)_: A chave de API da plataforma Dito. -- **secretKey** _(String, obrigatório)_: O segredo da chave de API da plataforma Dito. +- **apiKey** _(String, required)_: The API key for the Dito platform. +- **secretKey** _(String, required)_: The secret key for the Dito platform. ### initializePushNotificationService() -Este método deve ser chamado após a inicialização da SDK. Ele inicializa as configurações e serviços necessários para o funcionamento de push notifications da plataforma Dito. +This method should be called after the SDK initialization. It initializes the settings and services necessary for the functioning of push notifications on the Dito platform. ```dart void initializePushNotificationService(); ``` -#### Parâmetros +#### Parameters -- **apiKey** _(String, obrigatório)_: A chave de API da plataforma Dito. -- **secretKey** _(String, obrigatório)_: O segredo da chave de API da plataforma Dito. +- **apiKey** _(String, required)_: The API key for the Dito platform. +- **secretKey** _(String, required)_: The secret key for the Dito platform. ### identify() -Este método define o ID do usuário que será usado para todas as operações subsequentes. +This method sets the user ID that will be used for all subsequent operations. ```dart void identify(String userId); ``` -- **userID** _(String, obrigatório)_: Id para identificar o usuário na plataforma da Dito. -- **name** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **email** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **gender** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **birthday** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **location** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **customData** _(Map)_: Parâmetro para identificar o usuário na plataforma da Dito. - - -#### identifyUser() - -Este método registra o usuário na plataforma da Dito com as informações fornecidas anteriormente usando o método `identify()`. - -```dart -Future identifyUser() async; -``` - -#### Exception - -- Caso a SDK ainda não tenha `userId` cadastrado quando esse método for chamado, irá ocorrer um erro no aplicativo. (utilize o método `setUserId()` para definir o `userId`) +- **userID** _(String, required)_: ID to identify the user on the Dito platform. +- **name** _(String)_: Parameter to identify the user on the Dito platform. +- **email** _(String)_: Parameter to identify the user on the Dito platform. +- **gender** _(String)_: Parameter to identify the user on the Dito platform. +- **birthday** _(String)_: Parameter to identify the user on the Dito platform. +- **location** _(String)_: Parameter to identify the user on the Dito platform. +- **customData** _(Map)_: Parameter to identify the user on the Dito platform. ### trackEvent() -O método `trackEvent()` tem a finalidade de registrar um evento na plataforma da Dito. Caso o userID já tenha sido registrado, o evento será enviado imediatamente. No entanto, caso o userID ainda não tenha sido registrado, o evento será armazenado localmente e posteriormente enviado quando o userID for registrado por meio do método `setUserId()`. +This method records an event on the Dito platform. ```dart -Future trackEvent({ - required String eventName, - double? revenue, - Map? customData, -}) async; +void trackEvent({required String eventName, double? revenue, Map? customData}); ``` -#### Parâmetros +- **eventName** _(String, required)_: Name of the event to be tracked. +- **revenue** _(double)_: Revenue generated from the event. +- **customData** _(Map, opcional)_: Dados personalizados adicionais associados ao evento. +## Example Usage -### registryMobileToken() +Below is an example of how to use the DitoSDK: -Este método permite registrar um token mobile para o usuário. - -```dart -Future registryMobileToken({ - required String token, - String? platform, -}); -``` - -#### Parâmetros - -- **token** _(String, obrigatório)_: O token mobile que será registrado. -- **platform** _(String, opcional)_: Nome da plataforma que o usuário está acessando o aplicativo. Valores válidos: 'iPhone' e 'Android'. - `
`_Caso não seja passado algum valor nessa prop, a sdk irá pegar por default o valor pelo `platform`._ - -#### Exception - -- Caso seja passado um valor diferente de 'iPhone' ou 'Android' na propriedade platform, irá ocorrer um erro no aplicativo. -- Caso a SDK ainda não tenha `identify` cadastrado quando esse método for chamado, irá ocorrer um erro no aplicativo. (utilize o método `identify()` para definir o id do usuário) - -### removeMobileToken() - -Este método permite remover um token mobile para o usuário. - -```dart -Future removeMobileToken({ - required String token, - String? platform, -}); -``` - -#### Parâmetros - -- **token** _(String, obrigatório)_: O token mobile que será removido. -- **platform** _(String, opcional)_: Nome da plataforma que o usuário está acessando o aplicativo. Valores válidos: 'iPhone' e 'Android'. - `
`_Caso não seja passado algum valor nessa prop, a sdk irá pegar por default o valor pelo `platform`._ - -#### Exception - -- Caso seja passado um valor diferente de 'iPhone' ou 'Android' na propriedade platform, irá ocorrer um erro no aplicativo. -- Caso a SDK ainda não tenha `identify` cadastrado quando esse método for chamado, irá ocorrer um erro no aplicativo. (utilize o método `identify()` para definir o id do usuário) - -### openNotification() - -Este método permite registrar a abertura de uma notificação mobile. - -```dart -Future openNotification({ - required String notificationId, - required String identifier, - required String reference -}) async -``` - -#### Parâmetros - -- **notificationId** _(String, obrigatório)_: Id da notificação da Dito recebida pelo aplicativo. -- **identifier** _(String, obrigatório)_: Parâmetro para dentificar a notificação na plataforma da Dito. -- **reference** _(String, obrigatório)_: Parâmetro para identificar o usuário na plataforma da Dito. - -###### Observações - -- Esses parâmetros estarão presentes no data da notificação - -## Classes - -### User - -Classe para manipulação dos dados do usuário. - -```dart -User user = User( sha1("joao@example.com"), 'João da Silva', 'joao@example.com', 'São Paulo'); -``` - -#### Parâmetros - -- **userID** _(String, obrigatório)_: Id para identificar o usuário na plataforma da Dito. -- **name** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **email** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **gender** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **birthday** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **location** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **customData** _(Map)_: Parâmetro para identificar o usuário na plataforma da Dito. - -## Exemplos - -### Uso básico da SDK: +### main.dart ```dart import 'package:dito_sdk/dito_sdk.dart'; -final dito = DitoSDK(); - -// Inicializa a SDK com suas chaves de API -dito.initialize(apiKey: 'sua_api_key', secretKey: 'sua_secret_key'); - -// Define ou atualiza informações do usuário na instância (neste momento, ainda não há comunicação com a Dito) -dito.identify( sha1("joao@example.com"), 'João da Silva', 'joao@example.com', 'São Paulo'); - -// Envia as informações do usuário (que foram definidas ou atualizadas pelo identify) para a Dito -await dito.identifyUser(); - -// Registra um evento na Dito -await dito.trackEvent(eventName: 'login'); -``` - -### Uso avançado da SDK: - -#### main.dart - -```dart -import 'package:dito_sdk/dito_sdk.dart'; - -final dito = DitoSDK(); - -// Inicializa a SDK com suas chaves de API -dito.initialize(apiKey: 'sua_api_key', secretKey: 'sua_secret_key'); -``` - -#### login.dart - -```dart -import 'package:dito_sdk/dito_sdk.dart'; - -final dito = DitoSDK(); - -// Define o ID do usuário -dito.setUserId('id_do_usuario'); -dito.identify( sha1("joao@example.com"), 'João da Silva', 'joao@example.com', 'São Paulo'); -await dito.identifyUser(); -``` - -#### arquivoX.dart - -```dart -import 'package:dito_sdk/dito_sdk.dart'; - -final dito = DitoSDK(); - -// Define ou atualiza informações do usuário na instância (neste momento, ainda não há comunicação com a Dito) -dito.identify( sha1("joao@example.com"), 'João da Silva', 'joao@example.com', 'São Paulo'); -await dito.identifyUser(); -await dito.registryMobileToken(token: token); - -``` - -#### arquivoY.dart - -```dart -import 'package:dito_sdk/dito_sdk.dart'; - -final dito = DitoSDK(); - -// Define ou atualiza informações do usuário na instância (neste momento, ainda não há comunicação com a Dito) -dito.identify( sha1("joao@example.com"), 'João da Silva', 'joao@example.com', 'Rio de Janeiro', { - 'loja preferida': 'LojaX', - 'canal preferido': 'Loja Física' -}); -await dito.identifyUser(); -``` - -Isso resultará no envio do seguinte payload do usuário ao chamar `identifyUser()`: +void main() { + DitoSDK dito = DitoSDK(); -```javascript -{ - name: 'João da Silva', - email: 'joao@example.com', - location: 'Rio de Janeiro', - customData: { - 'loja preferida': 'LojaX', - 'canal preferido': 'Loja Física' - } + dito.initialize(apiKey: 'your_api_key', secretKey: 'your_secret_key'); + dito.initializePushNotificationService(); + + dito.identify('user_id', name: 'John Doe', email: 'john.doe@example.com'); + dito.trackEvent( + eventName: 'purchased product', + revenue: 99.90, + customData: { + 'product': 'productX', + 'sku_product': '99999999', + 'payment_method': 'Visa', + }, + ); } ``` -A nossa SDK é uma instância única, o que significa que, mesmo que ela seja inicializada em vários arquivos ou mais de uma vez, ela sempre referenciará as mesmas informações previamente armazenadas. Isso nos proporciona a flexibilidade de chamar o método `identify()` a qualquer momento para adicionar ou atualizar os detalhes do usuário, e somente quando necessário, enviá-los através do método `identifyUser()`. +You can call the `identify()` method at any time to add or update user details, and only when necessary, send them through the `identifyUser()` method. -#### arquivoZ.dart +### arquivoZ.dart ```dart import 'package:dito_sdk/dito_sdk.dart'; final dito = DitoSDK(); -// Registra um evento na Dito +// Record an event on Dito dito.trackEvent( - eventName: 'comprou produto', + eventName: 'purchased product', revenue: 99.90, customData: { - 'produto': 'produtoX', - 'sku_produto': '99999999', - 'metodo_pagamento': 'Visa', + 'product': 'productX', + 'sku_product': '99999999', + 'payment_method': 'Visa', }, ); ``` -### Uso da SDK com push notification: +### Using SDK with Push Notification: -Para o funcionamento é necessário configurar a lib do Firebase Cloud Message (FCM), seguindo os seguintes passos: +To make it work, it is necessary to configure the Firebase Cloud Messaging (FCM) library by following these steps: ```shell dart pub global activate flutterfire_cli @@ -292,20 +129,21 @@ flutter pub add firebase_core firebase_messaging flutterfire configure ``` -Siga os passos que irá aparecer na CLI, assim terá as chaves de acesso do Firebase configuradas dentro dos App's Android e iOS. +Follow the steps that will appear on the CLI, so you will have the Firebase access keys configured within the Android and iOS apps. #### main.dart + ```dart import 'package:dito_sdk/dito_sdk.dart'; -// Método para registrar um serviço que irá receber os push quando o app estiver totalmente fechado +// Method to register a service that will receive push notifications when the app is completely closed @pragma('vm:entry-point') Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { final notification = DataPayload.fromJson(jsonDecode(message.data["data"])); dito.notificationService().showLocalNotification(CustomNotification( id: message.hashCode, - title: notification.details.title || "O nome do aplicativo", + title: notification.details.title || "App Name", body: notification.details.message, payload: notification)); } @@ -317,9 +155,10 @@ void main() async { FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); DitoSDK dito = DitoSDK(); - dito.initialize(apiKey: 'sua_api_key', secretKey: 'sua_secret_key'); + dito.initialize(apiKey: 'your_api_key', secretKey: 'your_secret_key'); await dito.initializePushService(); } ``` -> Lembre-se de substituir 'sua_api_key', 'sua_secret_key' e 'id_do_usuario' pelos valores corretos em seu ambiente. +> Remember to replace 'your_api_key', 'your_secret_key', and 'user_id' with the correct values in your environment. +``` \ No newline at end of file diff --git a/README.pt-br.md b/README.pt-br.md new file mode 100644 index 0000000..1b325a1 --- /dev/null +++ b/README.pt-br.md @@ -0,0 +1,327 @@ +[![en](https://img.shields.io/badge/lang-en-red.svg)](https://github.com/ditointernet/sdk_mobile_flutter/blob/main/README.md) + +# Dito SDK (Flutter) + +DitoSDK é uma biblioteca Dart que fornece métodos para integrar aplicativos com a plataforma da Dito. Ela permite identificar usuários, registrar eventos e enviar dados personalizados. + +## Instalação + +Para instalar a biblioteca DitoSDK em seu aplicativo Flutter, você deve seguir as instruções fornecidas [nesse link](https://pub.dev/packages/dito_sdk/install). + +## Métodos + +### initialize() + +Este método deve ser chamado antes de qualquer outra operação com o SDK. Ele inicializa as chaves de API e SECRET necessárias para a autenticação na plataforma Dito. + +```dart +void initialize({required String apiKey, required String secretKey}); +``` + +#### Parâmetros + +- **apiKey** _(String, obrigatório)_: A chave de API da plataforma Dito. +- **secretKey** _(String, obrigatório)_: O segredo da chave de API da plataforma Dito. + +### initializePushNotificationService() + +Este método deve ser chamado após a inicialização da SDK. Ele inicializa as configurações e serviços necessários para o funcionamento de push notifications da plataforma Dito. + +```dart +void initializePushNotificationService(); +``` + +#### Parâmetros + +- **apiKey** _(String, obrigatório)_: A chave de API da plataforma Dito. +- **secretKey** _(String, obrigatório)_: O segredo da chave de API da plataforma Dito. + +### identify() + +Este método define o ID do usuário que será usado para todas as operações subsequentes. + +```dart +void identify(String userId); +``` + +- **userID** _(String, obrigatório)_: Id para identificar o usuário na plataforma da Dito. +- **name** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. +- **email** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. +- **gender** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. +- **birthday** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. +- **location** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. +- **customData** _(Map)_: Parâmetro para identificar o usuário na plataforma da Dito. + + +#### identifyUser() + +Este método registra o usuário na plataforma da Dito com as informações fornecidas anteriormente usando o método `identify()`. + +```dart +Future identifyUser() async; +``` + +#### Exception + +- Caso a SDK ainda não tenha `userId` cadastrado quando esse método for chamado, irá ocorrer um erro no aplicativo. (utilize o método `setUserId()` para definir o `userId`) + +### trackEvent() + +O método `trackEvent()` tem a finalidade de registrar um evento na plataforma da Dito. Caso o userID já tenha sido registrado, o evento será enviado imediatamente. No entanto, caso o userID ainda não tenha sido registrado, o evento será armazenado localmente e posteriormente enviado quando o userID for registrado por meio do método `setUserId()`. + +```dart +Future trackEvent({ + required String eventName, + double? revenue, + Map? customData, +}) async; +``` + +#### Parâmetros + +- **eventName** _(String, obrigatório)_: O nome do evento a ser registrado. +- **revenue** _(double, opcional)_: A receita associada ao evento. +- **customData** _(Map, opcional)_: Dados personalizados adicionais associados ao evento. + +### registryMobileToken() + +Este método permite registrar um token mobile para o usuário. + +```dart +Future registryMobileToken({ + required String token, + String? platform, +}); +``` + +#### Parâmetros + +- **token** _(String, obrigatório)_: O token mobile que será registrado. +- **platform** _(String, opcional)_: Nome da plataforma que o usuário está acessando o aplicativo. Valores válidos: 'iPhone' e 'Android'. + `
`_Caso não seja passado algum valor nessa prop, a sdk irá pegar por default o valor pelo `platform`._ + +#### Exception + +- Caso seja passado um valor diferente de 'iPhone' ou 'Android' na propriedade platform, irá ocorrer um erro no aplicativo. +- Caso a SDK ainda não tenha `identify` cadastrado quando esse método for chamado, irá ocorrer um erro no aplicativo. (utilize o método `identify()` para definir o id do usuário) + +### removeMobileToken() + +Este método permite remover um token mobile para o usuário. + +```dart +Future removeMobileToken({ + required String token, + String? platform, +}); +``` + +#### Parâmetros + +- **token** _(String, obrigatório)_: O token mobile que será removido. +- **platform** _(String, opcional)_: Nome da plataforma que o usuário está acessando o aplicativo. Valores válidos: 'iPhone' e 'Android'. + `
`_Caso não seja passado algum valor nessa prop, a sdk irá pegar por default o valor pelo `platform`._ + +#### Exception + +- Caso seja passado um valor diferente de 'iPhone' ou 'Android' na propriedade platform, irá ocorrer um erro no aplicativo. +- Caso a SDK ainda não tenha `identify` cadastrado quando esse método for chamado, irá ocorrer um erro no aplicativo. (utilize o método `identify()` para definir o id do usuário) + +### openNotification() + +Este método permite registrar a abertura de uma notificação mobile. + +```dart +Future openNotification({ + required String notificationId, + required String identifier, + required String reference +}) async +``` + +#### Parâmetros + +- **notificationId** _(String, obrigatório)_: Id da notificação da Dito recebida pelo aplicativo. +- **identifier** _(String, obrigatório)_: Parâmetro para dentificar a notificação na plataforma da Dito. +- **reference** _(String, obrigatório)_: Parâmetro para identificar o usuário na plataforma da Dito. + +###### Observações + +- Esses parâmetros estarão presentes no data da notificação + +## Classes + +### User + +Classe para manipulação dos dados do usuário. + +```dart +User user = User( sha1("joao@example.com"), 'João da Silva', 'joao@example.com', 'São Paulo'); +``` + +#### Parâmetros + +- **userID** _(String, obrigatório)_: Id para identificar o usuário na plataforma da Dito. +- **name** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. +- **email** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. +- **gender** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. +- **birthday** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. +- **location** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. +- **customData** _(Map)_: Parâmetro para identificar o usuário na plataforma da Dito. + +## Exemplos + +### Uso básico da SDK: + +```dart +import 'package:dito_sdk/dito_sdk.dart'; + +final dito = DitoSDK(); + +// Inicializa a SDK com suas chaves de API +dito.initialize(apiKey: 'sua_api_key', secretKey: 'sua_secret_key'); + +// Define ou atualiza informações do usuário na instância (neste momento, ainda não há comunicação com a Dito) +dito.identify( sha1("joao@example.com"), 'João da Silva', 'joao@example.com', 'São Paulo'); + +// Envia as informações do usuário (que foram definidas ou atualizadas pelo identify) para a Dito +await dito.identifyUser(); + +// Registra um evento na Dito +await dito.trackEvent(eventName: 'login'); +``` + +### Uso avançado da SDK: + +#### main.dart + +```dart +import 'package:dito_sdk/dito_sdk.dart'; + +final dito = DitoSDK(); + +// Inicializa a SDK com suas chaves de API +dito.initialize(apiKey: 'sua_api_key', secretKey: 'sua_secret_key'); +``` + +#### login.dart + +```dart +import 'package:dito_sdk/dito_sdk.dart'; + +final dito = DitoSDK(); + +// Define o ID do usuário +dito.setUserId('id_do_usuario'); +dito.identify( sha1("joao@example.com"), 'João da Silva', 'joao@example.com', 'São Paulo'); +await dito.identifyUser(); +``` + +#### arquivoX.dart + +```dart +import 'package:dito_sdk/dito_sdk.dart'; + +final dito = DitoSDK(); + +// Define ou atualiza informações do usuário na instância (neste momento, ainda não há comunicação com a Dito) +dito.identify( sha1("joao@example.com"), 'João da Silva', 'joao@example.com', 'São Paulo'); +await dito.identifyUser(); +await dito.registryMobileToken(token: token); + +``` + +#### arquivoY.dart + +```dart +import 'package:dito_sdk/dito_sdk.dart'; + +final dito = DitoSDK(); + +// Define ou atualiza informações do usuário na instância (neste momento, ainda não há comunicação com a Dito) +dito.identify( sha1("joao@example.com"), 'João da Silva', 'joao@example.com', 'Rio de Janeiro', { + 'loja preferida': 'LojaX', + 'canal preferido': 'Loja Física' +}); +await dito.identifyUser(); +``` + +Isso resultará no envio do seguinte payload do usuário ao chamar `identifyUser()`: + +```javascript +{ + name: 'João da Silva', + email: 'joao@example.com', + location: 'Rio de Janeiro', + customData: { + 'loja preferida': 'LojaX', + 'canal preferido': 'Loja Física' + } +} +``` + +A nossa SDK é uma instância única, o que significa que, mesmo que ela seja inicializada em vários arquivos ou mais de uma vez, ela sempre referenciará as mesmas informações previamente armazenadas. Isso nos proporciona a flexibilidade de chamar o método `identify()` a qualquer momento para adicionar ou atualizar os detalhes do usuário, e somente quando necessário, enviá-los através do método `identifyUser()`. + +#### arquivoZ.dart + +```dart +import 'package:dito_sdk/dito_sdk.dart'; + +final dito = DitoSDK(); + +// Registra um evento na Dito +dito.trackEvent( + eventName: 'comprou produto', + revenue: 99.90, + customData: { + 'produto': 'produtoX', + 'sku_produto': '99999999', + 'metodo_pagamento': 'Visa', + }, +); +``` + +### Uso da SDK com push notification: + +Para o funcionamento é necessário configurar a lib do Firebase Cloud Message (FCM), seguindo os seguintes passos: + +```shell +dart pub global activate flutterfire_cli +flutter pub add firebase_core firebase_messaging +``` + +```shell +flutterfire configure +``` + +Siga os passos que irá aparecer na CLI, assim terá as chaves de acesso do Firebase configuradas dentro dos App's Android e iOS. + +#### main.dart +```dart +import 'package:dito_sdk/dito_sdk.dart'; + +// Método para registrar um serviço que irá receber os push quando o app estiver totalmente fechado +@pragma('vm:entry-point') +Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { + final notification = DataPayload.fromJson(jsonDecode(message.data["data"])); + + dito.notificationService().showLocalNotification(CustomNotification( + id: message.hashCode, + title: notification.details.title || "O nome do aplicativo", + body: notification.details.message, + payload: notification)); +} + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + + await Firebase.initializeApp(); + FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); + + DitoSDK dito = DitoSDK(); + dito.initialize(apiKey: 'sua_api_key', secretKey: 'sua_secret_key'); + await dito.initializePushService(); +} +``` + +> Lembre-se de substituir 'sua_api_key', 'sua_secret_key' e 'id_do_usuario' pelos valores corretos em seu ambiente. From 23c79f9999e689395dbba6c43c9d56a4c09a166c Mon Sep 17 00:00:00 2001 From: Ana Machado Date: Tue, 4 Jun 2024 08:43:21 -0300 Subject: [PATCH 02/36] [NOT-3023] DitoApi --- example/lib/app_form.dart | 16 +-- package/lib/data/dito_api.dart | 187 +++++++++++++++++++++++++++++++++ package/lib/dito_sdk.dart | 124 ++-------------------- package/lib/utils/http.dart | 26 ----- 4 files changed, 204 insertions(+), 149 deletions(-) create mode 100644 package/lib/data/dito_api.dart delete mode 100644 package/lib/utils/http.dart diff --git a/example/lib/app_form.dart b/example/lib/app_form.dart index bebae3f..fd09301 100644 --- a/example/lib/app_form.dart +++ b/example/lib/app_form.dart @@ -27,11 +27,11 @@ class AppFormState extends State { userID: cpf, cpf: cpf, name: 'Teste SDK Flutter', email: email); await dito.identifyUser(); - // final token = await dito.notificationService().getDeviceFirebaseToken(); - // - // if (token != null && token.isNotEmpty) { - // dito.registryMobileToken(token: token); - // } + final token = await dito.notificationService().getDeviceFirebaseToken(); + + if (token != null && token.isNotEmpty) { + dito.registryMobileToken(token: token); + } } handleIdentify() async { @@ -101,16 +101,16 @@ class AppFormState extends State { padding: const EdgeInsets.all(15.0), child: Column(children: [ FilledButton( - child: const Text('Registrar Identify'), onPressed: handleIdentify, + child: const Text('Registrar Identify'), ), OutlinedButton( - child: const Text('Receber Notification'), onPressed: handleNotification, + child: const Text('Receber Notification'), ), TextButton( - child: const Text('Criar notificação local'), onPressed: handleLocalNotification, + child: const Text('Criar notificação local'), ) ]))) ], diff --git a/package/lib/data/dito_api.dart b/package/lib/data/dito_api.dart new file mode 100644 index 0000000..eed71b2 --- /dev/null +++ b/package/lib/data/dito_api.dart @@ -0,0 +1,187 @@ +import 'package:http/http.dart' as http; +import 'dart:convert'; + +import '../database.dart'; +import '../constants.dart'; +import '../entity/user.dart'; +import '../entity/event.dart'; +import '../entity/domain.dart'; +import '../utils/sha1.dart'; + +enum Endpoints { + identify, + registryMobileTokens, + removeMobileTokens, + events, + openNotification; + + replace(String id) { + String? value; + + switch (toString()) { + case "Endpoint.registryMobileTokens": + value = "notification.plataformasocial.com.br/users/{}/mobile-tokens/" + .replaceFirst("{}", id); + break; + case "Endpoint.removeMobileTokens": + value = + "notification.plataformasocial.com.br/users/{}/mobile-tokens/disable/" + .replaceFirst("{}", id); + break; + case "Endpoint.events": + value = + "events.plataformasocial.com.br/users/{}".replaceFirst("{}", id); + break; + case "Endpoint.openNotification": + value = "notification.plataformasocial.com.br/notifications/{}/open" + .replaceFirst("{}", id); + break; + default: + value = "login.plataformasocial.com.br/users/portal/{}/signup" + .replaceFirst("{}", id); + break; + } + + return value; + } +} + +class DitoApi { + Constants constants = Constants(); + String? _apiKey; + String? _secretKey; + late Map _assign; + + static final DitoApi _instance = DitoApi._internal(); + + factory DitoApi() { + return _instance; + } + + DitoApi._internal(); + + void _checkConfiguration() { + if (_apiKey == null || _secretKey == null) { + throw Exception( + 'API key and Secret Key must be initialized before using. Please call the initialize() method first.'); + } + } + + Future _post({required url, Map? body}) { + _checkConfiguration(); + + return http.post( + url, + body: body, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'User-Agent': constants.platform, + }, + ); + } + + Future initialize(String apiKey, String secretKey) async { + _apiKey = apiKey; + _secretKey = secretKey; + _assign = { + 'platform_api_key': apiKey, + 'sha1_signature': convertToSHA1(secretKey), + }; + } + + Future identify(User user) async { + final queryParameters = { + 'user_data': jsonEncode(user.toJson()), + }; + + queryParameters.addAll(_assign); + final url = Domain(Endpoints.identify.replace(user.id!)).spited; + final uri = Uri.https(url[0], url[1], queryParameters); + + return await _post( + url: uri, + ); + } + + Future trackEvent(Event event, User user) async { + if (user.isNotValid) { + final database = LocalDatabase.instance; + await database.createEvent(event); + return http.Response("", 200); + } + + final body = { + 'id_type': 'id', + 'network_name': 'pt', + 'event': jsonEncode(event.toJson()) + }; + + final url = Domain(Endpoints.events.replace(user.id!)).spited; + final uri = Uri.https(url[0], url[1], _assign); + + body.addAll(_assign); + return await _post(url: uri, body: body); + } + + Future openNotification( + String notificationId, String identifier, String reference) async { + final queryParameters = { + 'channel_type': 'mobile', + }; + + final body = { + 'identifier': identifier, + 'reference': reference, + }; + + queryParameters.addAll(_assign); + + final url = + Domain(Endpoint.openNotification.replace(notificationId)).spited; + final uri = Uri.https(url[0], url[1], queryParameters); + + return await _post(url: uri, body: body); + } + + Future registryMobileToken(String token, User user) async { + if (user.isNotValid) { + throw Exception( + 'User registration is required. Please call the identify() method first.'); + } + + final queryParameters = { + 'id_type': 'id', + 'token': token, + 'platform': constants.platform, + }; + + queryParameters.addAll(_assign); + final url = Domain(Endpoint.registryMobileTokens.replace(user.id!)).spited; + final uri = Uri.https(url[0], url[1], queryParameters); + + return await _post( + url: uri, + ); + } + + Future removeMobileToken(String token, User user) async { + if (user.isNotValid) { + throw Exception( + 'User registration is required. Please call the identify() method first.'); + } + + final queryParameters = { + 'id_type': 'id', + 'token': token, + 'platform': constants.platform, + }; + + queryParameters.addAll(_assign); + final url = Domain(Endpoint.removeMobileTokens.replace(user.id!)).spited; + final uri = Uri.https(url[0], url[1], queryParameters); + + return await _post( + url: uri, + ); + } +} diff --git a/package/lib/dito_sdk.dart b/package/lib/dito_sdk.dart index edf7eb2..84b8746 100644 --- a/package/lib/dito_sdk.dart +++ b/package/lib/dito_sdk.dart @@ -1,7 +1,6 @@ library dito_sdk; -import 'dart:convert'; - +import 'package:dito_sdk/data/dito_api.dart'; import 'package:dito_sdk/entity/domain.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; @@ -12,13 +11,8 @@ import 'database.dart'; import 'entity/event.dart'; import 'entity/user.dart'; import 'services/notification_service.dart'; -import 'utils/http.dart'; -import 'utils/sha1.dart'; class DitoSDK { - String? _apiKey; - String? _secretKey; - late Map _assign; late NotificationService _notificationService; User _user = User(); Constants constants = Constants(); @@ -40,13 +34,8 @@ class DitoSDK { } void initialize({required String apiKey, required String secretKey}) async { - _apiKey = apiKey; - _secretKey = secretKey; _notificationService = NotificationService(_instance); - _assign = { - 'platform_api_key': apiKey, - 'sha1_signature': convertToSHA1(_secretKey!), - }; + DitoApi().initialize(apiKey, secretKey); } Future initializePushNotificationService() async { @@ -64,20 +53,13 @@ class DitoSDK { .listen(_notificationService.handleMessage); } - void _checkConfiguration() { - if (_apiKey == null || _secretKey == null) { - throw Exception( - 'API key and Secret Key must be initialized before using. Please call the initialize() method first.'); - } - } - Future _verifyPendingEvents() async { final database = LocalDatabase.instance; final events = await database.getEvents(); if (events.isNotEmpty) { for (final event in events) { - await _postEvent(event); + await DitoApi().trackEvent(event, _user); } database.deleteEvents(); } @@ -149,40 +131,12 @@ class DitoSDK { } Future identifyUser() async { - _checkConfiguration(); - if (_user.isNotValid) { throw Exception( 'User registration is required. Please call the identify() method first.'); } - final queryParameters = { - 'user_data': jsonEncode(_user.toJson()), - }; - - queryParameters.addAll(_assign); - final url = Domain(Endpoint.identify.replace(_user.id!)).spited; - final uri = Uri.https(url[0], url[1], queryParameters); - - return await Api().post( - url: uri, - ); - } - - Future _postEvent(Event event) async { - _checkConfiguration(); - - final body = { - 'id_type': 'id', - 'network_name': 'pt', - 'event': jsonEncode(event.toJson()) - }; - - final url = Domain(Endpoint.events.replace(_user.id!)).spited; - final uri = Uri.https(url[0], url[1], _assign); - - body.addAll(_assign); - return await Api().post(url: uri, body: body); + return await DitoApi().identify(_user); } Future trackEvent({ @@ -200,82 +154,22 @@ class DitoSDK { customData: customData, revenue: revenue); - if (_user.isNotValid) { - final database = LocalDatabase.instance; - await database.createEvent(event); - return http.Response("", 200); - } - - return await _postEvent(event); + return await DitoApi().trackEvent(event, _user); } Future registryMobileToken({required String token}) async { - _checkConfiguration(); - - if (_user.isNotValid) { - throw Exception( - 'User registration is required. Please call the identify() method first.'); - } - - final queryParameters = { - 'id_type': 'id', - 'token': token, - 'platform': constants.platform, - }; - - queryParameters.addAll(_assign); - final url = Domain(Endpoint.registryMobileTokens.replace(_user.id!)).spited; - final uri = Uri.https(url[0], url[1], queryParameters); - - return await Api().post( - url: uri, - ); + return await DitoApi().registryMobileToken(token, _user); } Future removeMobileToken({required String token}) async { - _checkConfiguration(); - - if (_user.isNotValid) { - throw Exception( - 'User registration is required. Please call the identify() method first.'); - } - - final queryParameters = { - 'id_type': 'id', - 'token': token, - 'platform': constants.platform, - }; - - queryParameters.addAll(_assign); - final url = Domain(Endpoint.removeMobileTokens.replace(_user.id!)).spited; - final uri = Uri.https(url[0], url[1], queryParameters); - - return await Api().post( - url: uri, - ); + return await DitoApi().removeMobileToken(token, _user); } Future openNotification( {required String notificationId, required String identifier, required String reference}) async { - _checkConfiguration(); - - final queryParameters = { - 'channel_type': 'mobile', - }; - - final body = { - 'identifier': identifier, - 'reference': reference, - }; - - queryParameters.addAll(_assign); - - final url = - Domain(Endpoint.openNotification.replace(notificationId)).spited; - final uri = Uri.https(url[0], url[1], queryParameters); - - return await Api().post(url: uri, body: body); + return await DitoApi() + .openNotification(notificationId, identifier, reference); } } diff --git a/package/lib/utils/http.dart b/package/lib/utils/http.dart deleted file mode 100644 index d67f26a..0000000 --- a/package/lib/utils/http.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:http/http.dart' as http; - -import '../constants.dart'; - -class Api { - Constants constants = Constants(); - - static final Api _instance = Api._internal(); - - factory Api() { - return _instance; - } - - Api._internal(); - - Future post({required url, Map? body}) async { - return await http.post( - url, - body: body, - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'User-Agent': constants.platform, - }, - ); - } -} From 592ac1650f346dcc14753f56b880883aa4270f82 Mon Sep 17 00:00:00 2001 From: Ana Machado Date: Tue, 4 Jun 2024 12:02:18 -0300 Subject: [PATCH 03/36] [NOT-3023] constants --- package/lib/constants.dart | 43 ---------------------------------- package/lib/data/dito_api.dart | 16 ++++++------- 2 files changed, 8 insertions(+), 51 deletions(-) diff --git a/package/lib/constants.dart b/package/lib/constants.dart index e964ebc..8eb6a72 100644 --- a/package/lib/constants.dart +++ b/package/lib/constants.dart @@ -4,8 +4,6 @@ import 'package:device_info_plus/device_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart'; class Constants { - String platform = Platform.isIOS ? 'iPhone' : 'Android'; - Future getUserAgent() async { final deviceInfo = DeviceInfoPlugin(); final PackageInfo packageInfo = await PackageInfo.fromPlatform(); @@ -26,44 +24,3 @@ class Constants { return '$appName/$version ($system; $model)'; } } - -enum Endpoint { - identify, - registryMobileTokens, - removeMobileTokens, - events, - openNotification; - - replace(String id) { - String? value; - - switch (toString()) { - case "Endpoint.registryMobileTokens": - value = - "notification.plataformasocial.com.br/users/{}/mobile-tokens/" - .replaceFirst("{}", id); - break; - case "Endpoint.removeMobileTokens": - value = - "notification.plataformasocial.com.br/users/{}/mobile-tokens/disable/" - .replaceFirst("{}", id); - break; - case "Endpoint.events": - value = - "events.plataformasocial.com.br/users/{}" - .replaceFirst("{}", id); - break; - case "Endpoint.openNotification": - value = - "notification.plataformasocial.com.br/notifications/{}/open" - .replaceFirst("{}", id); - break; - default: - value = "login.plataformasocial.com.br/users/portal/{}/signup" - .replaceFirst("{}", id); - break; - } - - return value; - } -} diff --git a/package/lib/data/dito_api.dart b/package/lib/data/dito_api.dart index eed71b2..ba56dc5 100644 --- a/package/lib/data/dito_api.dart +++ b/package/lib/data/dito_api.dart @@ -1,14 +1,14 @@ import 'package:http/http.dart' as http; import 'dart:convert'; +import 'dart:io'; import '../database.dart'; -import '../constants.dart'; import '../entity/user.dart'; import '../entity/event.dart'; import '../entity/domain.dart'; import '../utils/sha1.dart'; -enum Endpoints { +enum Endpoint { identify, registryMobileTokens, removeMobileTokens, @@ -47,7 +47,7 @@ enum Endpoints { } class DitoApi { - Constants constants = Constants(); + String _platform = Platform.isIOS ? 'iPhone' : 'Android'; String? _apiKey; String? _secretKey; late Map _assign; @@ -75,7 +75,7 @@ class DitoApi { body: body, headers: { 'Content-Type': 'application/x-www-form-urlencoded', - 'User-Agent': constants.platform, + 'User-Agent': _platform, }, ); } @@ -95,7 +95,7 @@ class DitoApi { }; queryParameters.addAll(_assign); - final url = Domain(Endpoints.identify.replace(user.id!)).spited; + final url = Domain(Endpoint.identify.replace(user.id!)).spited; final uri = Uri.https(url[0], url[1], queryParameters); return await _post( @@ -116,7 +116,7 @@ class DitoApi { 'event': jsonEncode(event.toJson()) }; - final url = Domain(Endpoints.events.replace(user.id!)).spited; + final url = Domain(Endpoint.events.replace(user.id!)).spited; final uri = Uri.https(url[0], url[1], _assign); body.addAll(_assign); @@ -152,7 +152,7 @@ class DitoApi { final queryParameters = { 'id_type': 'id', 'token': token, - 'platform': constants.platform, + 'platform': _platform, }; queryParameters.addAll(_assign); @@ -173,7 +173,7 @@ class DitoApi { final queryParameters = { 'id_type': 'id', 'token': token, - 'platform': constants.platform, + 'platform': _platform, }; queryParameters.addAll(_assign); From a348e079db1f40cc3e112d3bea9791c9db075c98 Mon Sep 17 00:00:00 2001 From: Ana Machado Date: Tue, 4 Jun 2024 15:31:19 -0300 Subject: [PATCH 04/36] [NOT-3023] Removendo Endpoint e Domain --- package/lib/data/dito_api.dart | 89 ++++++++++------------------------ package/lib/dito_sdk.dart | 1 - package/lib/entity/domain.dart | 14 ------ 3 files changed, 25 insertions(+), 79 deletions(-) delete mode 100644 package/lib/entity/domain.dart diff --git a/package/lib/data/dito_api.dart b/package/lib/data/dito_api.dart index ba56dc5..15f5878 100644 --- a/package/lib/data/dito_api.dart +++ b/package/lib/data/dito_api.dart @@ -5,49 +5,10 @@ import 'dart:io'; import '../database.dart'; import '../entity/user.dart'; import '../entity/event.dart'; -import '../entity/domain.dart'; import '../utils/sha1.dart'; -enum Endpoint { - identify, - registryMobileTokens, - removeMobileTokens, - events, - openNotification; - - replace(String id) { - String? value; - - switch (toString()) { - case "Endpoint.registryMobileTokens": - value = "notification.plataformasocial.com.br/users/{}/mobile-tokens/" - .replaceFirst("{}", id); - break; - case "Endpoint.removeMobileTokens": - value = - "notification.plataformasocial.com.br/users/{}/mobile-tokens/disable/" - .replaceFirst("{}", id); - break; - case "Endpoint.events": - value = - "events.plataformasocial.com.br/users/{}".replaceFirst("{}", id); - break; - case "Endpoint.openNotification": - value = "notification.plataformasocial.com.br/notifications/{}/open" - .replaceFirst("{}", id); - break; - default: - value = "login.plataformasocial.com.br/users/portal/{}/signup" - .replaceFirst("{}", id); - break; - } - - return value; - } -} - class DitoApi { - String _platform = Platform.isIOS ? 'iPhone' : 'Android'; + final String _platform = Platform.isIOS ? 'iPhone' : 'Android'; String? _apiKey; String? _secretKey; late Map _assign; @@ -67,11 +28,14 @@ class DitoApi { } } - Future _post({required url, Map? body}) { + Future _post(String url, String path, + {Map? queryParameters, Map? body}) { _checkConfiguration(); + final uri = Uri.https(url, path, queryParameters); + return http.post( - url, + uri, body: body, headers: { 'Content-Type': 'application/x-www-form-urlencoded', @@ -95,12 +59,11 @@ class DitoApi { }; queryParameters.addAll(_assign); - final url = Domain(Endpoint.identify.replace(user.id!)).spited; - final uri = Uri.https(url[0], url[1], queryParameters); - return await _post( - url: uri, - ); + const url = 'login.plataformasocial.com.br'; + final path = 'users/portal/${user.id}/signup'; + + return await _post(url, path, queryParameters: queryParameters); } Future trackEvent(Event event, User user) async { @@ -116,11 +79,12 @@ class DitoApi { 'event': jsonEncode(event.toJson()) }; - final url = Domain(Endpoint.events.replace(user.id!)).spited; - final uri = Uri.https(url[0], url[1], _assign); + const url = 'events.plataformasocial.com.br'; + final path = 'users/${user.id}'; body.addAll(_assign); - return await _post(url: uri, body: body); + + return await _post(url, path, body: body); } Future openNotification( @@ -136,11 +100,10 @@ class DitoApi { queryParameters.addAll(_assign); - final url = - Domain(Endpoint.openNotification.replace(notificationId)).spited; - final uri = Uri.https(url[0], url[1], queryParameters); + const url = 'notification.plataformasocial.com.br'; + final path = 'notifications/${notificationId}/open'; - return await _post(url: uri, body: body); + return await _post(url, path, body: body); } Future registryMobileToken(String token, User user) async { @@ -156,12 +119,11 @@ class DitoApi { }; queryParameters.addAll(_assign); - final url = Domain(Endpoint.registryMobileTokens.replace(user.id!)).spited; - final uri = Uri.https(url[0], url[1], queryParameters); - return await _post( - url: uri, - ); + const url = 'notification.plataformasocial.com.br'; + final path = 'users/${user.id}/mobile-tokens/'; + + return await _post(url, path, queryParameters: queryParameters); } Future removeMobileToken(String token, User user) async { @@ -177,11 +139,10 @@ class DitoApi { }; queryParameters.addAll(_assign); - final url = Domain(Endpoint.removeMobileTokens.replace(user.id!)).spited; - final uri = Uri.https(url[0], url[1], queryParameters); - return await _post( - url: uri, - ); + const url = 'notification.plataformasocial.com.br'; + final path = 'users/${user.id}/mobile-tokens/disable'; + + return await _post(url, path, queryParameters: queryParameters); } } diff --git a/package/lib/dito_sdk.dart b/package/lib/dito_sdk.dart index 84b8746..0202b99 100644 --- a/package/lib/dito_sdk.dart +++ b/package/lib/dito_sdk.dart @@ -1,7 +1,6 @@ library dito_sdk; import 'package:dito_sdk/data/dito_api.dart'; -import 'package:dito_sdk/entity/domain.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:http/http.dart' as http; diff --git a/package/lib/entity/domain.dart b/package/lib/entity/domain.dart deleted file mode 100644 index 38e5d61..0000000 --- a/package/lib/entity/domain.dart +++ /dev/null @@ -1,14 +0,0 @@ -class Domain { - final String url; - - Domain(this.url); - - String get uri { - return url; - } - - List get spited { - int idx = url.indexOf("/"); - return [url.substring(0, idx).trim(), url.substring(idx + 1).trim()]; - } -} From 5977f37a5d286a3862ab421e800150ca65d6211e Mon Sep 17 00:00:00 2001 From: Ana Machado Date: Tue, 4 Jun 2024 15:33:47 -0300 Subject: [PATCH 05/36] [NOT-3023] Removendo Endpoint e Domain --- package/lib/dito_sdk.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/package/lib/dito_sdk.dart b/package/lib/dito_sdk.dart index 0202b99..0a6d551 100644 --- a/package/lib/dito_sdk.dart +++ b/package/lib/dito_sdk.dart @@ -1,6 +1,5 @@ library dito_sdk; -import 'package:dito_sdk/data/dito_api.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:http/http.dart' as http; From b66d65de774b0f1c952c34bd694a5eb2ea99593f Mon Sep 17 00:00:00 2001 From: Ana Machado Date: Tue, 4 Jun 2024 16:30:43 -0300 Subject: [PATCH 06/36] [NOT-3023] ditoApi instance --- package/lib/data/dito_api.dart | 27 +++++++++------------------ package/lib/dito_sdk.dart | 25 +++++++++++++++++-------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/package/lib/data/dito_api.dart b/package/lib/data/dito_api.dart index 15f5878..83a57ad 100644 --- a/package/lib/data/dito_api.dart +++ b/package/lib/data/dito_api.dart @@ -2,7 +2,6 @@ import 'package:http/http.dart' as http; import 'dart:convert'; import 'dart:io'; -import '../database.dart'; import '../entity/user.dart'; import '../entity/event.dart'; import '../utils/sha1.dart'; @@ -15,7 +14,14 @@ class DitoApi { static final DitoApi _instance = DitoApi._internal(); - factory DitoApi() { + factory DitoApi(String apiKey, String secretKey) { + _instance._apiKey = apiKey; + _instance._secretKey = secretKey; + _instance._assign = { + 'platform_api_key': apiKey, + 'sha1_signature': convertToSHA1(secretKey), + }; + return _instance; } @@ -44,15 +50,6 @@ class DitoApi { ); } - Future initialize(String apiKey, String secretKey) async { - _apiKey = apiKey; - _secretKey = secretKey; - _assign = { - 'platform_api_key': apiKey, - 'sha1_signature': convertToSHA1(secretKey), - }; - } - Future identify(User user) async { final queryParameters = { 'user_data': jsonEncode(user.toJson()), @@ -67,12 +64,6 @@ class DitoApi { } Future trackEvent(Event event, User user) async { - if (user.isNotValid) { - final database = LocalDatabase.instance; - await database.createEvent(event); - return http.Response("", 200); - } - final body = { 'id_type': 'id', 'network_name': 'pt', @@ -101,7 +92,7 @@ class DitoApi { queryParameters.addAll(_assign); const url = 'notification.plataformasocial.com.br'; - final path = 'notifications/${notificationId}/open'; + final path = 'notifications/$notificationId/open'; return await _post(url, path, body: body); } diff --git a/package/lib/dito_sdk.dart b/package/lib/dito_sdk.dart index 0a6d551..fd727af 100644 --- a/package/lib/dito_sdk.dart +++ b/package/lib/dito_sdk.dart @@ -9,12 +9,15 @@ import 'database.dart'; import 'entity/event.dart'; import 'entity/user.dart'; import 'services/notification_service.dart'; +import 'data/dito_api.dart'; class DitoSDK { late NotificationService _notificationService; User _user = User(); Constants constants = Constants(); + late DitoApi ditoApi; + static final DitoSDK _instance = DitoSDK._internal(); factory DitoSDK() { @@ -33,7 +36,7 @@ class DitoSDK { void initialize({required String apiKey, required String secretKey}) async { _notificationService = NotificationService(_instance); - DitoApi().initialize(apiKey, secretKey); + ditoApi = DitoApi(apiKey, secretKey); } Future initializePushNotificationService() async { @@ -57,7 +60,7 @@ class DitoSDK { if (events.isNotEmpty) { for (final event in events) { - await DitoApi().trackEvent(event, _user); + await ditoApi.trackEvent(event, _user); } database.deleteEvents(); } @@ -134,7 +137,7 @@ class DitoSDK { 'User registration is required. Please call the identify() method first.'); } - return await DitoApi().identify(_user); + return await ditoApi.identify(_user); } Future trackEvent({ @@ -152,22 +155,28 @@ class DitoSDK { customData: customData, revenue: revenue); - return await DitoApi().trackEvent(event, _user); + if (user.isNotValid) { + final database = LocalDatabase.instance; + await database.createEvent(event); + return http.Response("", 200); + } + + return await ditoApi.trackEvent(event, _user); } Future registryMobileToken({required String token}) async { - return await DitoApi().registryMobileToken(token, _user); + return await ditoApi.registryMobileToken(token, _user); } Future removeMobileToken({required String token}) async { - return await DitoApi().removeMobileToken(token, _user); + return await ditoApi.removeMobileToken(token, _user); } Future openNotification( {required String notificationId, required String identifier, required String reference}) async { - return await DitoApi() - .openNotification(notificationId, identifier, reference); + return await ditoApi.openNotification( + notificationId, identifier, reference); } } From fdbf850437725a8c73decd2c03023cb588b7b5b2 Mon Sep 17 00:00:00 2001 From: Ana Machado Date: Tue, 4 Jun 2024 16:33:10 -0300 Subject: [PATCH 07/36] [NOT-3023] _user --- package/lib/dito_sdk.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/lib/dito_sdk.dart b/package/lib/dito_sdk.dart index fd727af..733a2df 100644 --- a/package/lib/dito_sdk.dart +++ b/package/lib/dito_sdk.dart @@ -155,7 +155,7 @@ class DitoSDK { customData: customData, revenue: revenue); - if (user.isNotValid) { + if (_user.isNotValid) { final database = LocalDatabase.instance; await database.createEvent(event); return http.Response("", 200); From 157e3c01a0e6c4e50dd08e10289667e72b3ad416 Mon Sep 17 00:00:00 2001 From: Igor Gottschalg <119457595+igorgsduarte@users.noreply.github.com> Date: Tue, 11 Jun 2024 10:03:01 -0300 Subject: [PATCH 08/36] feat: User domain implementation (#7) --- example/lib/app_form.dart | 6 +- example/lib/constants.dart | 2 +- example/pubspec.lock | 18 +- package/lib/data/dito_api.dart | 26 +- package/lib/dito_sdk.dart | 143 ++++----- .../user.dart => user/user_entity.dart} | 15 +- package/lib/user/user_interface.dart | 17 + package/lib/user/user_repository.dart | 50 +++ package/pubspec.lock | 24 +- package/test/dito_sdk_test.dart | 50 +-- package/test/user/user_test.dart | 34 ++ package/test/utils.dart | 8 + pubspec.lock | 295 +++++++++++++++++- 13 files changed, 525 insertions(+), 163 deletions(-) rename package/lib/{entity/user.dart => user/user_entity.dart} (77%) create mode 100644 package/lib/user/user_interface.dart create mode 100644 package/lib/user/user_repository.dart create mode 100644 package/test/user/user_test.dart create mode 100644 package/test/utils.dart diff --git a/example/lib/app_form.dart b/example/lib/app_form.dart index fd09301..c69bb4c 100644 --- a/example/lib/app_form.dart +++ b/example/lib/app_form.dart @@ -1,5 +1,6 @@ import 'package:dito_sdk/dito_sdk.dart'; import 'package:dito_sdk/entity/custom_notification.dart'; +import 'package:dito_sdk/user/user_entity.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -23,9 +24,10 @@ class AppFormState extends State { String email = "teste@dito.com.br"; identify() async { - dito.identify( + final user = UserEntity( userID: cpf, cpf: cpf, name: 'Teste SDK Flutter', email: email); - await dito.identifyUser(); + + await dito.user.identify(user); final token = await dito.notificationService().getDeviceFirebaseToken(); diff --git a/example/lib/constants.dart b/example/lib/constants.dart index f48e316..8871074 100644 --- a/example/lib/constants.dart +++ b/example/lib/constants.dart @@ -38,4 +38,4 @@ abstract class Constants { 'IOS_FIREBASE_APP_ID', defaultValue: '', ); -} \ No newline at end of file +} diff --git a/example/pubspec.lock b/example/pubspec.lock index ebf849c..48f9acf 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -103,7 +103,7 @@ packages: path: "../package" relative: true source: path - version: "0.5.1" + version: "0.5.3" fake_async: dependency: transitive description: @@ -223,6 +223,14 @@ packages: description: flutter source: sdk version: "0.0.0" + get_it: + dependency: transitive + description: + name: get_it + sha256: d85128a5dae4ea777324730dc65edd9c9f43155c109d5cc0a69cab74139fbac1 + url: "https://pub.dev" + source: hosted + version: "7.7.0" http: dependency: transitive description: @@ -239,6 +247,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + injectable: + dependency: transitive + description: + name: injectable + sha256: "3c8355a29d11ff28c0311bed754649761f345ef7a13ff66a714380954af51226" + url: "https://pub.dev" + source: hosted + version: "2.4.2" leak_tracker: dependency: transitive description: diff --git a/package/lib/data/dito_api.dart b/package/lib/data/dito_api.dart index 83a57ad..7266ad1 100644 --- a/package/lib/data/dito_api.dart +++ b/package/lib/data/dito_api.dart @@ -1,9 +1,10 @@ -import 'package:http/http.dart' as http; import 'dart:convert'; import 'dart:io'; -import '../entity/user.dart'; +import 'package:http/http.dart' as http; + import '../entity/event.dart'; +import '../user/user_entity.dart'; import '../utils/sha1.dart'; class DitoApi { @@ -14,19 +15,21 @@ class DitoApi { static final DitoApi _instance = DitoApi._internal(); - factory DitoApi(String apiKey, String secretKey) { + factory DitoApi() { + return _instance; + } + + DitoApi._internal(); + + void setKeys(String apiKey, String secretKey) { _instance._apiKey = apiKey; _instance._secretKey = secretKey; _instance._assign = { 'platform_api_key': apiKey, 'sha1_signature': convertToSHA1(secretKey), }; - - return _instance; } - DitoApi._internal(); - void _checkConfiguration() { if (_apiKey == null || _secretKey == null) { throw Exception( @@ -50,7 +53,7 @@ class DitoApi { ); } - Future identify(User user) async { + Future identify(UserEntity user) async { final queryParameters = { 'user_data': jsonEncode(user.toJson()), }; @@ -63,7 +66,7 @@ class DitoApi { return await _post(url, path, queryParameters: queryParameters); } - Future trackEvent(Event event, User user) async { + Future trackEvent(Event event, UserEntity user) async { final body = { 'id_type': 'id', 'network_name': 'pt', @@ -97,7 +100,8 @@ class DitoApi { return await _post(url, path, body: body); } - Future registryMobileToken(String token, User user) async { + Future registryMobileToken( + String token, UserEntity user) async { if (user.isNotValid) { throw Exception( 'User registration is required. Please call the identify() method first.'); @@ -117,7 +121,7 @@ class DitoApi { return await _post(url, path, queryParameters: queryParameters); } - Future removeMobileToken(String token, User user) async { + Future removeMobileToken(String token, UserEntity user) async { if (user.isNotValid) { throw Exception( 'User registration is required. Please call the identify() method first.'); diff --git a/package/lib/dito_sdk.dart b/package/lib/dito_sdk.dart index 733a2df..aeb99e9 100644 --- a/package/lib/dito_sdk.dart +++ b/package/lib/dito_sdk.dart @@ -1,22 +1,23 @@ library dito_sdk; +import 'package:dito_sdk/user/user_entity.dart'; +import 'package:dito_sdk/user/user_interface.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; -import 'package:http/http.dart' as http; +import 'package:http' + '/http.dart' as http; -import 'constants.dart'; +import 'data/dito_api.dart'; import 'database.dart'; import 'entity/event.dart'; -import 'entity/user.dart'; import 'services/notification_service.dart'; -import 'data/dito_api.dart'; +/// DitoSDK is a singleton class that provides various methods to interact with Dito API +/// and manage user data, events, and push notifications. class DitoSDK { + final UserInterface _userInterface = UserInterface(); + final DitoApi ditoApi = DitoApi(); late NotificationService _notificationService; - User _user = User(); - Constants constants = Constants(); - - late DitoApi ditoApi; static final DitoSDK _instance = DitoSDK._internal(); @@ -26,19 +27,25 @@ class DitoSDK { DitoSDK._internal(); + /// This get method provides an interface for communication with a User entity. + /// Returns an instance of UserInterface class. + UserInterface get user => _userInterface; + NotificationService notificationService() { return _notificationService; } - User get user { - return _user; - } - + /// This method initializes the SDK with the provided API key and secret key. + /// It also initializes the NotificationService and assigns API key and SHA1 signature. + /// + /// [apiKey] - The API key for the Dito platform. + /// [secretKey] - The secret key for the Dito platform. void initialize({required String apiKey, required String secretKey}) async { _notificationService = NotificationService(_instance); - ditoApi = DitoApi(apiKey, secretKey); + ditoApi.setKeys(apiKey, secretKey); } + /// This method initializes the push notification service using Firebase. Future initializePushNotificationService() async { await Firebase.initializeApp(); await _notificationService.initialize(); @@ -60,86 +67,28 @@ class DitoSDK { if (events.isNotEmpty) { for (final event in events) { - await ditoApi.trackEvent(event, _user); + await ditoApi.trackEvent(event, user.data); } database.deleteEvents(); } } - @Deprecated('migration') - Future setUserId(String userId) async { - _setUserId(userId); - } - - Future _setUserId(String userId) async { - if (_user.isValid) { - _verifyPendingEvents(); - } - } - - void identify({ - required String userID, - String? cpf, - String? name, - String? email, - String? gender, - String? birthday, - String? location, - Map? customData, - }) { - _user.userID = userID; - - if (cpf != null) { - _user.cpf = cpf; - } - - if (name != null) { - _user.name = name; - } - - if (email != null) { - _user.email = email; - } - - if (gender != null) { - _user.gender = gender; - } - - if (birthday != null) { - _user.birthday = birthday; - } - - if (location != null) { - _user.location = location; - } - - if (customData != null) { - _user.customData = customData; - } - - _setUserId(userID); - } - - Future setUser(User user) async { - _user = user; - - if (_user.isValid) { - await _setUserId(_user.id!); - } else { - throw Exception( - 'User registration is required. Please call the identify() method first.'); - } - } - - Future identifyUser() async { - if (_user.isNotValid) { - throw Exception( - 'User registration is required. Please call the identify() method first.'); - } - - return await ditoApi.identify(_user); + /// This method enables saving and sending user data to the Dito API. + /// + /// [user] - UserEntity object. + /// Returns a boolean indicating success. + Future identify(UserEntity user) async { + final result = await _userInterface.identify(user); + await _verifyPendingEvents(); + return result; } + /// This method tracks an event with optional revenue and custom data. + /// + /// [eventName] - The name of the event. + /// [revenue] - Optional revenue associated with the event. + /// [customData] - Optional custom data associated with the event. + /// Returns an http.Response. Future trackEvent({ required String eventName, double? revenue, @@ -155,23 +104,37 @@ class DitoSDK { customData: customData, revenue: revenue); - if (_user.isNotValid) { + if (_userInterface.data.isNotValid) { final database = LocalDatabase.instance; await database.createEvent(event); return http.Response("", 200); } - return await ditoApi.trackEvent(event, _user); + return await ditoApi.trackEvent(event, _userInterface.data); } + /// This method registers a mobile token for push notifications. + /// + /// [token] - The mobile token to be registered. + /// Returns an http.Response. Future registryMobileToken({required String token}) async { - return await ditoApi.registryMobileToken(token, _user); + return await ditoApi.registryMobileToken(token, _userInterface.data); } + /// This method removes a mobile token from the push notification service. + /// + /// [token] - The mobile token to be removed. + /// Returns an http.Response. Future removeMobileToken({required String token}) async { - return await ditoApi.removeMobileToken(token, _user); + return await ditoApi.removeMobileToken(token, _userInterface.data); } + /// This method opens a notification and sends its data to the Dito API. + /// + /// [notificationId] - The ID of the notification. + /// [identifier] - The identifier for the notification. + /// [reference] - The reference for the notification. + /// Returns an http.Response. Future openNotification( {required String notificationId, required String identifier, diff --git a/package/lib/entity/user.dart b/package/lib/user/user_entity.dart similarity index 77% rename from package/lib/entity/user.dart rename to package/lib/user/user_entity.dart index 605df6f..ea2d66a 100644 --- a/package/lib/entity/user.dart +++ b/package/lib/user/user_entity.dart @@ -1,6 +1,6 @@ import 'dart:convert'; -class User { +class UserEntity { String? userID; String? name; String? cpf; @@ -10,7 +10,7 @@ class User { String? location; Map? customData; - User( + UserEntity( {this.userID, this.name, this.cpf, @@ -21,11 +21,15 @@ class User { this.customData}); String? get id => userID; - bool get isValid => userID != null && userID!.isNotEmpty; + + /// User is valid when userId is not empty + bool get isValid => userID!.isNotEmpty; + bool get isNotValid => !isValid; - factory User.fromMap(Map map) { - return User( + // Factory method to instance a user from a JSON object + factory UserEntity.fromMap(Map map) { + return UserEntity( userID: map['userID'], name: map['name'], cpf: map['cpf'], @@ -39,6 +43,7 @@ class User { : null); } + // Factory method to convert a user to JSON object Map toJson() { return { 'name': name, diff --git a/package/lib/user/user_interface.dart b/package/lib/user/user_interface.dart new file mode 100644 index 0000000..9f99da3 --- /dev/null +++ b/package/lib/user/user_interface.dart @@ -0,0 +1,17 @@ +import 'dart:async'; + +import 'package:dito_sdk/user/user_entity.dart'; +import 'package:dito_sdk/user/user_repository.dart'; + +/// This is a interface from user to communicate with user repository +interface class UserInterface { + final UserRepository repository = UserRepository(); + + /// This method enable user data save and send to DitoAPI + /// Return bool with true when the identify was successes + Future identify(UserEntity? user) => repository.identify(user); + + /// This get method enable to access user data from repository + /// Returns UserEntity Class + UserEntity get data => repository.data; +} diff --git a/package/lib/user/user_repository.dart b/package/lib/user/user_repository.dart new file mode 100644 index 0000000..74bb2db --- /dev/null +++ b/package/lib/user/user_repository.dart @@ -0,0 +1,50 @@ +import 'dart:async'; + +import '../data/dito_api.dart'; +import 'user_entity.dart'; + +final class UserData extends UserEntity { + UserData._internal(); + + static final userData = UserData._internal(); + + factory UserData() => userData; +} + +class UserRepository { + final _userData = UserData(); + final DitoApi api = DitoApi(); + + /// This method get a user data on Static Data Object UserData + /// Return a UserEntity Class + UserEntity get data { + return _userData; + } + + /// This method set a user data on Static Data Object UserData + void _set(UserEntity user) { + _userData.userID = user.userID; + if (user.cpf != null) _userData.cpf = user.cpf; + if (user.name != null) _userData.name = user.name; + if (user.email != null) _userData.email = user.email; + if (user.gender != null) _userData.gender = user.gender; + if (user.birthday != null) _userData.birthday = user.birthday; + if (user.location != null) _userData.location = user.location; + if (user.customData != null) _userData.customData = user.customData; + } + + /// This method enable user data save and send to DitoAPI + /// Return bool with true when the identify was successes + Future identify(UserEntity? user) async { + if (user != null) _set(user); + + if (_userData.isNotValid) { + throw Exception('User registration id (userID) is required'); + } + + return await api + .identify(user!) + .then((response) => true) + .catchError((error) => false); + } +} diff --git a/package/pubspec.lock b/package/pubspec.lock index 318ad46..6a36290 100644 --- a/package/pubspec.lock +++ b/package/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _flutterfire_internals - sha256: "37a42d06068e2fe3deddb2da079a8c4d105f241225ba27b7122b37e9865fd8f7" + sha256: "13e611501ef36044655852215b4f30aed81123654a4f55193d0051a0e8705658" url: "https://pub.dev" source: hosted - version: "1.3.35" + version: "1.3.36" args: dependency: transitive description: @@ -117,10 +117,10 @@ packages: dependency: "direct main" description: name: firebase_core - sha256: "26de145bb9688a90962faec6f838247377b0b0d32cc0abecd9a4e43525fc856c" + sha256: "0d436d29b16fd9844a098ece2a3ce75efc290e5fe0844d282c5e8987173b0d02" url: "https://pub.dev" source: hosted - version: "2.32.0" + version: "3.0.0" firebase_core_platform_interface: dependency: transitive description: @@ -133,34 +133,34 @@ packages: dependency: transitive description: name: firebase_core_web - sha256: "43d9e951ac52b87ae9cc38ecdcca1e8fa7b52a1dd26a96085ba41ce5108db8e9" + sha256: "22fcb352744908224fc7be3caae254836099786acfe5df6e9fe901e9c2575a41" url: "https://pub.dev" source: hosted - version: "2.17.0" + version: "2.17.1" firebase_messaging: dependency: "direct main" description: name: firebase_messaging - sha256: a1662cc95d9750a324ad9df349b873360af6f11414902021f130c68ec02267c4 + sha256: "62fb18daf69ee5f65c0ea5ef4c611481ec50584f54a9d7bcb2de511ad821b45d" url: "https://pub.dev" source: hosted - version: "14.9.4" + version: "15.0.0" firebase_messaging_platform_interface: dependency: transitive description: name: firebase_messaging_platform_interface - sha256: "87c4a922cb6f811cfb7a889bdbb3622702443c52a0271636cbc90d813ceac147" + sha256: aec6972698a5f70557b44946923d6be2c0cda127b60b462a1b1f7f08a8a325a5 url: "https://pub.dev" source: hosted - version: "4.5.37" + version: "4.5.38" firebase_messaging_web: dependency: transitive description: name: firebase_messaging_web - sha256: "0d34dca01a7b103ed7f20138bffbb28eb0e61a677bf9e78a028a932e2c7322d5" + sha256: "17979bda5f4474b76c069d547294955e775e3c4484ae80f621a06aa27f5ac5d8" url: "https://pub.dev" source: hosted - version: "3.8.7" + version: "3.8.8" flutter: dependency: "direct main" description: flutter diff --git a/package/test/dito_sdk_test.dart b/package/test/dito_sdk_test.dart index 22cca08..c33ea0f 100644 --- a/package/test/dito_sdk_test.dart +++ b/package/test/dito_sdk_test.dart @@ -1,49 +1,21 @@ -import 'dart:convert'; -import 'dart:io'; - import 'package:dito_sdk/dito_sdk.dart'; +import 'package:dito_sdk/user/user_entity.dart'; import 'package:flutter_test/flutter_test.dart'; -Future testEnv() async { - final file = File('test/.env-test.json'); - final json = jsonDecode(await file.readAsString()); - return json; -} +import 'utils.dart'; -void main() { - final DitoSDK dito = DitoSDK(); - const id = '22222222222'; +final DitoSDK dito = DitoSDK(); +const id = '22222222222'; - setUp() async { - dynamic env = await testEnv(); - dito.initialize(apiKey: env["apiKey"], secretKey: env["secret"]); - } +Future setUp() async { + dynamic env = await testEnv(); + dito.initialize(apiKey: env["apiKey"], secretKey: env["secret"]); +} +void main() { group('Dito SDK: ', () { - test('Send identify', () async { - await setUp(); - - dito.identify( - userID: id, email: "teste@teste.com", customData: {"teste": "Teste"}); - expect(dito.user.id, id); - expect(dito.user.email, "teste@teste.com"); - - final response = await dito.identifyUser(); - expect(response.statusCode, 201); - }); - - test('Send event', () async { - await setUp(); - dito.identify(userID: id); - - final response = await dito.trackEvent(eventName: 'sdk-test-flutter'); - - expect(response.statusCode, 201); - }); - test('Send mobile token', () async { - await setUp(); - dito.identify(userID: id); + dito.identify(UserEntity(userID: id)); final response = await dito.registryMobileToken( token: @@ -53,8 +25,6 @@ void main() { }); test('Send open notification', () async { - await setUp(); - final response = await dito.openNotification( notificationId: '723422', identifier: '1713466024', reference: id); diff --git a/package/test/user/user_test.dart b/package/test/user/user_test.dart new file mode 100644 index 0000000..e2a41a9 --- /dev/null +++ b/package/test/user/user_test.dart @@ -0,0 +1,34 @@ +import 'package:dito_sdk/dito_sdk.dart'; +import 'package:dito_sdk/user/user_entity.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../utils.dart'; + +final DitoSDK dito = DitoSDK(); +const id = '22222222222'; + +void main() { + setUp(() async { + dynamic env = await testEnv(); + dito.initialize(apiKey: env["apiKey"], secretKey: env["secret"]); + }); + + group('User interface', () { + test('User entity start null', () { + expect(dito.user.data.userID, null); + }); + + test('Set User on memory', () async { + dito.user.identify(UserEntity(userID: id, email: "teste@teste.com")); + expect(dito.user.data.id, id); + expect(dito.user.data.email, "teste@teste.com"); + }); + + test('Send identify', () async { + final result = await dito.user.identify( + UserEntity(userID: "11111111111", email: "teste@teste.com")); + expect(result, true); + expect(dito.user.data.id, "11111111111"); + }); + }); +} diff --git a/package/test/utils.dart b/package/test/utils.dart new file mode 100644 index 0000000..d52a86d --- /dev/null +++ b/package/test/utils.dart @@ -0,0 +1,8 @@ +import 'dart:convert'; +import 'dart:io'; + +dynamic testEnv() async { + final file = File('test/.env-test.json'); + final json = jsonDecode(await file.readAsString()); + return json; +} diff --git a/pubspec.lock b/pubspec.lock index 2048d15..ae5792a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,27 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: "5aaf60d96c4cd00fe7f21594b5ad6a1b699c80a27420f8a837f4d68473ef09e3" + url: "https://pub.dev" + source: hosted + version: "68.0.0" + _macros: + dependency: transitive + description: dart + source: sdk + version: "0.1.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "21f1d3720fd1c70316399d5e2bccaebb415c434592d778cce8acb967b8578808" + url: "https://pub.dev" + source: hosted + version: "6.5.0" ansi_styles: dependency: transitive description: @@ -33,6 +54,70 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + build: + dependency: transitive + description: + name: build + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + build_config: + dependency: transitive + description: + name: build_config + sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + url: "https://pub.dev" + source: hosted + version: "1.1.1" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" + url: "https://pub.dev" + source: hosted + version: "2.4.2" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: "644dc98a0f179b872f612d3eb627924b578897c629788e858157fa5e704ca0c7" + url: "https://pub.dev" + source: hosted + version: "2.4.11" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: e3c79f69a64bdfcd8a776a3c28db4eb6e3fb5356d013ae5eb2e52007706d5dbe + url: "https://pub.dev" + source: hosted + version: "7.3.1" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb + url: "https://pub.dev" + source: hosted + version: "8.9.2" charcode: dependency: transitive description: @@ -41,6 +126,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff + url: "https://pub.dev" + source: hosted + version: "2.0.3" cli_launcher: dependency: transitive description: @@ -57,6 +150,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.4.1" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 + url: "https://pub.dev" + source: hosted + version: "4.10.0" collection: dependency: transitive description: @@ -73,6 +174,30 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.0+1" + convert: + dependency: transitive + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + crypto: + dependency: transitive + description: + name: crypto + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" + source: hosted + version: "3.0.3" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" + url: "https://pub.dev" + source: hosted + version: "2.3.6" file: dependency: transitive description: @@ -81,6 +206,30 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.0" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + get_it: + dependency: transitive + description: + name: get_it + sha256: d85128a5dae4ea777324730dc65edd9c9f43155c109d5cc0a69cab74139fbac1 + url: "https://pub.dev" + source: hosted + version: "7.7.0" glob: dependency: transitive description: @@ -105,6 +254,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.1" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" + source: hosted + version: "3.2.1" http_parser: dependency: transitive description: @@ -113,6 +270,22 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + injectable: + dependency: transitive + description: + name: injectable + sha256: "3c8355a29d11ff28c0311bed754649761f345ef7a13ff66a714380954af51226" + url: "https://pub.dev" + source: hosted + version: "2.4.2" + injectable_generator: + dependency: "direct dev" + description: + name: injectable_generator + sha256: "2ca3ada337eac0ef6b82f8049c970ddb63947738fdf32ac6cbef8d1567d7ba05" + url: "https://pub.dev" + source: hosted + version: "2.6.1" io: dependency: transitive description: @@ -121,6 +294,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" + js: + dependency: transitive + description: + name: js + sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf + url: "https://pub.dev" + source: hosted + version: "0.7.1" json_annotation: dependency: transitive description: @@ -129,6 +310,22 @@ packages: url: "https://pub.dev" source: hosted version: "4.9.0" + logging: + dependency: transitive + description: + name: logging + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + macros: + dependency: transitive + description: + name: macros + sha256: "12e8a9842b5a7390de7a781ec63d793527582398d16ea26c60fed58833c9ae79" + url: "https://pub.dev" + source: hosted + version: "0.1.0-main.0" matcher: dependency: transitive description: @@ -153,6 +350,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.15.0" + mime: + dependency: transitive + description: + name: mime + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" + url: "https://pub.dev" + source: hosted + version: "1.0.5" mustache_template: dependency: transitive description: @@ -161,6 +366,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" path: dependency: transitive description: @@ -225,6 +438,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.0" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8 + url: "https://pub.dev" + source: hosted + version: "1.3.0" quiver: dependency: transitive description: @@ -233,6 +454,38 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.1" + recase: + dependency: transitive + description: + name: recase + sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213 + url: "https://pub.dev" + source: hosted + version: "4.1.0" + shelf: + dependency: transitive + description: + name: shelf + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + url: "https://pub.dev" + source: hosted + version: "1.4.1" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" + url: "https://pub.dev" + source: hosted + version: "1.5.0" source_span: dependency: transitive description: @@ -257,6 +510,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" + source: hosted + version: "2.1.0" string_scanner: dependency: transitive description: @@ -281,6 +542,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.1" + timing: + dependency: transitive + description: + name: timing + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" + url: "https://pub.dev" + source: hosted + version: "1.0.1" typed_data: dependency: transitive description: @@ -297,6 +566,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" + watcher: + dependency: transitive + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" web: dependency: transitive description: @@ -305,6 +582,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "24301d8c293ce6fe327ffe6f59d8fd8834735f0ec36e4fd383ec7ff8a64aa078" + url: "https://pub.dev" + source: hosted + version: "0.1.5" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: a2d56211ee4d35d9b344d9d4ce60f362e4f5d1aafb988302906bd732bc731276 + url: "https://pub.dev" + source: hosted + version: "3.0.0" yaml: dependency: transitive description: @@ -322,4 +615,4 @@ packages: source: hosted version: "2.2.1" sdks: - dart: ">=3.3.0 <4.0.0" + dart: ">=3.4.0 <4.0.0" From 31d0dff6d6c2ab0f9d3548303af14c09efb63ed1 Mon Sep 17 00:00:00 2001 From: Igor Gottschalg <119457595+igorgsduarte@users.noreply.github.com> Date: Tue, 11 Jun 2024 11:11:58 -0300 Subject: [PATCH 09/36] feat: create event domain (#10) --- example/pubspec.lock | 16 - package/lib/data/dito_api.dart | 4 +- package/lib/data/event_database.dart | 160 ++++++++++ package/lib/database.dart | 76 ----- package/lib/dito_sdk.dart | 61 +--- .../event.dart => event/event_entity.dart} | 22 +- package/lib/event/event_interface.dart | 59 ++++ package/lib/event/event_repository.dart | 48 +++ .../lib/services/notification_service.dart | 5 +- package/lib/user/user_entity.dart | 4 +- package/lib/user/user_interface.dart | 43 ++- package/lib/utils/custom_data.dart | 15 + package/pubspec.yaml | 2 +- package/test/dito_sdk_test.dart | 12 +- package/test/event/database_test.dart | 99 ++++++ package/test/event/event_test.dart | 65 ++++ package/test/user/user_test.dart | 3 +- pubspec.lock | 295 +----------------- 18 files changed, 527 insertions(+), 462 deletions(-) create mode 100644 package/lib/data/event_database.dart delete mode 100644 package/lib/database.dart rename package/lib/{entity/event.dart => event/event_entity.dart} (69%) create mode 100644 package/lib/event/event_interface.dart create mode 100644 package/lib/event/event_repository.dart create mode 100644 package/lib/utils/custom_data.dart create mode 100644 package/test/event/database_test.dart create mode 100644 package/test/event/event_test.dart diff --git a/example/pubspec.lock b/example/pubspec.lock index 48f9acf..02609dc 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -223,14 +223,6 @@ packages: description: flutter source: sdk version: "0.0.0" - get_it: - dependency: transitive - description: - name: get_it - sha256: d85128a5dae4ea777324730dc65edd9c9f43155c109d5cc0a69cab74139fbac1 - url: "https://pub.dev" - source: hosted - version: "7.7.0" http: dependency: transitive description: @@ -247,14 +239,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" - injectable: - dependency: transitive - description: - name: injectable - sha256: "3c8355a29d11ff28c0311bed754649761f345ef7a13ff66a714380954af51226" - url: "https://pub.dev" - source: hosted - version: "2.4.2" leak_tracker: dependency: transitive description: diff --git a/package/lib/data/dito_api.dart b/package/lib/data/dito_api.dart index 7266ad1..7f9ecbb 100644 --- a/package/lib/data/dito_api.dart +++ b/package/lib/data/dito_api.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:http/http.dart' as http; -import '../entity/event.dart'; +import '../event/event_entity.dart'; import '../user/user_entity.dart'; import '../utils/sha1.dart'; @@ -66,7 +66,7 @@ class DitoApi { return await _post(url, path, queryParameters: queryParameters); } - Future trackEvent(Event event, UserEntity user) async { + Future trackEvent(EventEntity event, UserEntity user) async { final body = { 'id_type': 'id', 'network_name': 'pt', diff --git a/package/lib/data/event_database.dart b/package/lib/data/event_database.dart new file mode 100644 index 0000000..b0bb8a0 --- /dev/null +++ b/package/lib/data/event_database.dart @@ -0,0 +1,160 @@ +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:sqflite_common_ffi/sqflite_ffi.dart'; + +import '../event/event_entity.dart'; + +/// EventDatabaseService is a singleton class that provides methods to interact with a SQLite database +/// for storing and managing events. +class EventDatabase { + static const String _dbName = 'ditoSDK.db'; + static const String _tableName = 'events'; + static Database? _database; + + static final EventDatabase _instance = EventDatabase._internal(); + + /// Factory constructor to return the singleton instance of EventDatabaseService. + factory EventDatabase() { + return _instance; + } + + /// Private named constructor for internal initialization of singleton instance. + EventDatabase._internal(); + + /// Getter for the database instance. + /// Initializes the database if it is not already initialized. + /// Returns a Future that completes with the database instance. + Future get database async { + if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) { + databaseFactoryOrNull = null; + sqfliteFfiInit(); + databaseFactory = databaseFactoryFfi; + } + + if (_database != null) return _database!; + + _database = await _initDatabase(); + return _database!; + } + + /// Private method to initialize the database. + /// Sets up the path and opens the database, creating it if it does not exist. + /// Returns a Future that completes with the database instance. + Future _initDatabase() async { + try { + final databasePath = await getDatabasesPath(); + final String path = '$databasePath/$_dbName'; + + return await openDatabase( + path, + version: 1, + onCreate: _createTable, + ); + } catch (e) { + if (kDebugMode) { + print('Error initializing database: $e'); + } + rethrow; + } + } + + /// Callback method to create the events table when the database is first created. + /// + /// [db] - The database instance. + /// [version] - The version of the database. + Future _createTable(Database db, int version) async { + try { + await db.execute(''' + CREATE TABLE $_tableName ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + eventName TEXT, + eventMoment TEXT, + revenue REAL, + customData TEXT + ) + '''); + } catch (e) { + if (kDebugMode) { + print('Error creating table: $e'); + } + rethrow; + } + } + + /// Method to insert a new event into the events table. + /// + /// [event] - The EventEntity object to be inserted. + /// Returns a Future that completes with the row id of the inserted event. + Future create(EventEntity event) async { + try { + final db = await database; + return await db.insert(_tableName, event.toMap()) > 0; + } catch (e) { + if (kDebugMode) { + print('Error inserting event: $e'); + } + rethrow; + } + } + + /// Method to delete an event from the events table. + /// + /// [event] - The EventEntity object to be deleted. + /// Returns a Future that completes with the number of rows deleted. + Future delete(EventEntity event) async { + try { + final db = await database; + return await db.delete( + _tableName, + where: 'eventName = ? AND eventMoment = ?', + whereArgs: [event.eventName, event.eventMoment], + ) > + 0; + } catch (e) { + if (kDebugMode) { + print('Error deleting event: $e'); + } + rethrow; + } + } + + /// Method to retrieve all events from the events table. + /// Returns a Future that completes with a list of EventEntity objects. + Future> fetchAll() async { + try { + final db = await database; + final List> maps = await db.query(_tableName); + + return maps.map((map) => EventEntity.fromMap(map)).toList(); + } catch (e) { + if (kDebugMode) { + print('Error retrieving events: $e'); + } + rethrow; + } + } + + /// Method to clear all events from the events table. + /// Returns a Future that completes with the number of rows deleted. + Future clearDatabase() async { + try { + final db = await database; + await db.delete(_tableName); + } catch (e) { + if (kDebugMode) { + print('Error clearing database: $e'); + } + rethrow; + } + } + + /// Method to close the database. + /// Should be called when the database is no longer needed. + Future closeDatabase() async { + if (_database != null) { + await _database!.close(); + _database = null; + } + } +} diff --git a/package/lib/database.dart b/package/lib/database.dart deleted file mode 100644 index a7c9de4..0000000 --- a/package/lib/database.dart +++ /dev/null @@ -1,76 +0,0 @@ -import 'dart:io'; - -import 'package:sqflite_common_ffi/sqflite_ffi.dart'; - -import 'entity/event.dart'; - -class LocalDatabase { - static final LocalDatabase instance = LocalDatabase._privateConstructor(); - static Database? _database; - - LocalDatabase._privateConstructor(); - - Future get database async { - if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) { - databaseFactoryOrNull = null; - sqfliteFfiInit(); - databaseFactory = databaseFactoryFfi; - } - - if (_database != null) return _database!; - - _database = await _initDatabase(); - return _database!; - } - - Future _initDatabase() async { - final databasePath = await getDatabasesPath(); - final String path = '$databasePath/ditoSDK.db'; - - return await openDatabase( - path, - version: 1, - onCreate: _createTable, - ); - } - - Future _createTable(Database db, int version) async { - await db.execute(''' - CREATE TABLE events ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - eventName TEXT, - eventMoment TEXT, - revenue REAL, - customData TEXT - ) - '''); - } - - Future createEvent(Event event) async { - final db = await database; - await db.insert('events', event.toMap()); - } - - Future deleteEvent(Event event) async { - final db = await database; - return await db.delete( - 'events', - where: 'eventName = ? AND eventMoment = ?', - whereArgs: [event.eventName, event.eventMoment], - ); - } - - Future> getEvents() async { - final db = await database; - final List> maps = await db.query('events'); - - return List.generate(maps.length, (index) { - return Event.fromMap(maps[index]); - }); - } - - Future deleteEvents() async { - final db = await database; - return await db.delete('events'); - } -} diff --git a/package/lib/dito_sdk.dart b/package/lib/dito_sdk.dart index aeb99e9..5353cd2 100644 --- a/package/lib/dito_sdk.dart +++ b/package/lib/dito_sdk.dart @@ -1,24 +1,23 @@ library dito_sdk; -import 'package:dito_sdk/user/user_entity.dart'; -import 'package:dito_sdk/user/user_interface.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; -import 'package:http' - '/http.dart' as http; +import 'package:http/http.dart' as http; import 'data/dito_api.dart'; -import 'database.dart'; -import 'entity/event.dart'; +import 'event/event_entity.dart'; +import 'event/event_interface.dart'; import 'services/notification_service.dart'; +import 'user/user_entity.dart'; +import 'user/user_interface.dart'; /// DitoSDK is a singleton class that provides various methods to interact with Dito API /// and manage user data, events, and push notifications. class DitoSDK { final UserInterface _userInterface = UserInterface(); - final DitoApi ditoApi = DitoApi(); + final EventInterface _eventInterface = EventInterface(); + final DitoApi _ditoApi = DitoApi(); late NotificationService _notificationService; - static final DitoSDK _instance = DitoSDK._internal(); factory DitoSDK() { @@ -42,7 +41,7 @@ class DitoSDK { /// [secretKey] - The secret key for the Dito platform. void initialize({required String apiKey, required String secretKey}) async { _notificationService = NotificationService(_instance); - ditoApi.setKeys(apiKey, secretKey); + _ditoApi.setKeys(apiKey, secretKey); } /// This method initializes the push notification service using Firebase. @@ -61,25 +60,12 @@ class DitoSDK { .listen(_notificationService.handleMessage); } - Future _verifyPendingEvents() async { - final database = LocalDatabase.instance; - final events = await database.getEvents(); - - if (events.isNotEmpty) { - for (final event in events) { - await ditoApi.trackEvent(event, user.data); - } - database.deleteEvents(); - } - } - /// This method enables saving and sending user data to the Dito API. /// /// [user] - UserEntity object. /// Returns a boolean indicating success. Future identify(UserEntity user) async { final result = await _userInterface.identify(user); - await _verifyPendingEvents(); return result; } @@ -88,29 +74,16 @@ class DitoSDK { /// [eventName] - The name of the event. /// [revenue] - Optional revenue associated with the event. /// [customData] - Optional custom data associated with the event. - /// Returns an http.Response. - Future trackEvent({ + /// Returns a bool. + Future trackEvent({ required String eventName, double? revenue, - Map? customData, + Map? customData, }) async { - DateTime localDateTime = DateTime.now(); - DateTime utcDateTime = localDateTime.toUtc(); - String eventMoment = utcDateTime.toIso8601String(); - - final event = Event( - eventName: eventName, - eventMoment: eventMoment, - customData: customData, - revenue: revenue); - - if (_userInterface.data.isNotValid) { - final database = LocalDatabase.instance; - await database.createEvent(event); - return http.Response("", 200); - } + final event = EventEntity( + eventName: eventName, customData: customData, revenue: revenue); - return await ditoApi.trackEvent(event, _userInterface.data); + return await _eventInterface.trackEvent(event); } /// This method registers a mobile token for push notifications. @@ -118,7 +91,7 @@ class DitoSDK { /// [token] - The mobile token to be registered. /// Returns an http.Response. Future registryMobileToken({required String token}) async { - return await ditoApi.registryMobileToken(token, _userInterface.data); + return await _ditoApi.registryMobileToken(token, _userInterface.data); } /// This method removes a mobile token from the push notification service. @@ -126,7 +99,7 @@ class DitoSDK { /// [token] - The mobile token to be removed. /// Returns an http.Response. Future removeMobileToken({required String token}) async { - return await ditoApi.removeMobileToken(token, _userInterface.data); + return await _ditoApi.removeMobileToken(token, _userInterface.data); } /// This method opens a notification and sends its data to the Dito API. @@ -139,7 +112,7 @@ class DitoSDK { {required String notificationId, required String identifier, required String reference}) async { - return await ditoApi.openNotification( + return await _ditoApi.openNotification( notificationId, identifier, reference); } } diff --git a/package/lib/entity/event.dart b/package/lib/event/event_entity.dart similarity index 69% rename from package/lib/entity/event.dart rename to package/lib/event/event_entity.dart index afece0e..8e83249 100644 --- a/package/lib/entity/event.dart +++ b/package/lib/event/event_entity.dart @@ -1,23 +1,23 @@ import 'dart:convert'; -class Event { - final String eventName; - final String eventMoment; - final double? revenue; - final Map? customData; +class EventEntity { + String eventName; + String? eventMoment; + double? revenue; + Map? customData; - Event({ + EventEntity({ required this.eventName, - required this.eventMoment, this.revenue, + this.eventMoment, this.customData, }); - factory Event.fromMap(Map map) { - return Event( + factory EventEntity.fromMap(Map map) { + return EventEntity( eventName: map['eventName'], - eventMoment: map['eventMoment'], revenue: map['revenue'], + eventMoment: map['eventMoment'], customData: map['customData'] != null ? (json.decode(map['customData']) as Map) .map((key, value) => MapEntry(key, value as String)) @@ -30,7 +30,7 @@ class Event { 'eventName': eventName, 'eventMoment': eventMoment, 'revenue': revenue, - 'customData': customData != null ? json.encode(customData) : null, + 'customData': customData != null ? jsonEncode(customData) : null, }; } diff --git a/package/lib/event/event_interface.dart b/package/lib/event/event_interface.dart new file mode 100644 index 0000000..c41dd54 --- /dev/null +++ b/package/lib/event/event_interface.dart @@ -0,0 +1,59 @@ +import 'package:flutter/foundation.dart'; + +import '../utils/custom_data.dart'; +import 'event_entity.dart'; +import 'event_repository.dart'; + +/// EventInterface is an interface for managing events and communicating with the event repository +interface class EventInterface { + final EventRepository _repository = EventRepository(); + + /// Tracks an event by saving and sending it to the event repository. + /// + /// [event] - The EventEntity object containing event data. + /// Returns a Future that completes with true if the event was successfully tracked. + Future trackEvent(EventEntity event) async { + try { + DateTime localDateTime = DateTime.now(); + DateTime utcDateTime = localDateTime.toUtc(); + String eventMoment = utcDateTime.toIso8601String(); + + event.eventMoment = eventMoment; + + final version = await customDataVersion; + if (event.customData == null) { + event.customData = version; + } else { + event.customData?.addAll(version); + } + + return await _repository.trackEvent(event); + } catch (e) { + if (kDebugMode) { + print('Error tracking event: $e'); + } + return false; + } + } + + /// Verifies and processes any pending events. + /// + /// Throws an exception if the user is not valid. + Future verifyPendingEvents() async { + try { + final events = await _repository.fetchPendingEvents(); + + if (events.isNotEmpty) { + for (final event in events) { + await trackEvent(event); + } + await _repository.clearEvents(); + } + } catch (e) { + if (kDebugMode) { + print('Error verifying pending events: $e'); + } + rethrow; + } + } +} diff --git a/package/lib/event/event_repository.dart b/package/lib/event/event_repository.dart new file mode 100644 index 0000000..cb4779f --- /dev/null +++ b/package/lib/event/event_repository.dart @@ -0,0 +1,48 @@ +import 'dart:async'; + +import 'package:dito_sdk/user/user_repository.dart'; + +import '../data/dito_api.dart'; +import '../data/event_database.dart'; +import 'event_entity.dart'; + +/// EventRepository is responsible for managing events by interacting with +/// the local database and the Dito API. +class EventRepository { + final DitoApi _api = DitoApi(); + final UserRepository _userRepository = UserRepository(); + final _database = EventDatabase(); + + /// Tracks an event by saving it to the local database if the user is not registered, + /// or by sending it to the Dito API if the user is registered. + /// + /// [event] - The EventEntity object containing event data. + /// Returns a Future that completes with true if the event was successfully tracked, + /// or false if an error occurred. + Future trackEvent(EventEntity event) async { + // If the user is not registered, save the event to the local database + if (_userRepository.data.isNotValid) { + return await _database.create(event); + } + + // Otherwise, send the event to the Dito API + return await _api + .trackEvent(event, _userRepository.data) + .then((response) => true) + .catchError((e) => false); + } + + /// Fetches all pending events from the local database. + /// + /// Returns a Future that completes with a list of EventEntity objects. + Future> fetchPendingEvents() async { + return await _database.fetchAll(); + } + + /// Clears all events from the local database. + /// + /// Returns a Future that completes when the database has been cleared. + Future clearEvents() async { + return await _database.clearDatabase(); + } +} diff --git a/package/lib/services/notification_service.dart b/package/lib/services/notification_service.dart index 14c1457..e51229e 100644 --- a/package/lib/services/notification_service.dart +++ b/package/lib/services/notification_service.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:package_info_plus/package_info_plus.dart'; @@ -69,7 +70,9 @@ class NotificationService { void handleMessage(RemoteMessage message) { if (message.data["data"] == null) { - print("Data is not defined: ${message.data}"); + if (kDebugMode) { + print("Data is not defined: ${message.data}"); + } } final notification = DataPayload.fromJson(jsonDecode(message.data["data"])); diff --git a/package/lib/user/user_entity.dart b/package/lib/user/user_entity.dart index ea2d66a..255bad2 100644 --- a/package/lib/user/user_entity.dart +++ b/package/lib/user/user_entity.dart @@ -23,9 +23,9 @@ class UserEntity { String? get id => userID; /// User is valid when userId is not empty - bool get isValid => userID!.isNotEmpty; + bool get isValid => userID != null && userID!.isNotEmpty; - bool get isNotValid => !isValid; + bool get isNotValid => userID == null || userID!.isEmpty; // Factory method to instance a user from a JSON object factory UserEntity.fromMap(Map map) { diff --git a/package/lib/user/user_interface.dart b/package/lib/user/user_interface.dart index 9f99da3..be2b013 100644 --- a/package/lib/user/user_interface.dart +++ b/package/lib/user/user_interface.dart @@ -1,17 +1,44 @@ import 'dart:async'; +import 'package:dito_sdk/event/event_interface.dart'; import 'package:dito_sdk/user/user_entity.dart'; import 'package:dito_sdk/user/user_repository.dart'; +import 'package:flutter/foundation.dart'; -/// This is a interface from user to communicate with user repository +import '../utils/custom_data.dart'; + +/// UserInterface is an interface for communication with the user repository interface class UserInterface { - final UserRepository repository = UserRepository(); + final UserRepository _repository = UserRepository(); + final EventInterface _eventInterface = EventInterface(); + + /// Identifies the user by saving their data and sending it to DitoAPI. + /// + /// [user] - The UserEntity object containing user data. + /// Returns a Future that completes with true if the identification was successful. + Future identify(UserEntity user) async { + try { + final version = await customDataVersion; + if (user.customData == null) { + user.customData = version; + } else { + user.customData?.addAll(version); + } + + final result = await _repository.identify(user); + await _eventInterface.verifyPendingEvents(); - /// This method enable user data save and send to DitoAPI - /// Return bool with true when the identify was successes - Future identify(UserEntity? user) => repository.identify(user); + return result; + } catch (e) { + if (kDebugMode) { + print('Error identifying user: $e'); + } + return false; + } + } - /// This get method enable to access user data from repository - /// Returns UserEntity Class - UserEntity get data => repository.data; + /// Gets the user data from the repository. + /// + /// Returns the UserEntity object containing user data. + UserEntity get data => _repository.data; } diff --git a/package/lib/utils/custom_data.dart b/package/lib/utils/custom_data.dart new file mode 100644 index 0000000..725b822 --- /dev/null +++ b/package/lib/utils/custom_data.dart @@ -0,0 +1,15 @@ +import 'package:flutter/foundation.dart'; + +/// Retrieves the custom data version including the SDK version information. +/// +/// Returns a Future that completes with a Map containing the version information. +Future> get customDataVersion async { + try { + return {"dito_sdk_version": "Flutter SDK - 0.5.3"}; + } catch (e) { + if (kDebugMode) { + print('Error retrieving package info: $e'); + } + return {"dito_sdk_version": "Unknown version"}; + } +} diff --git a/package/pubspec.yaml b/package/pubspec.yaml index 641ac3e..b179018 100644 --- a/package/pubspec.yaml +++ b/package/pubspec.yaml @@ -13,12 +13,12 @@ dependencies: http: ">=0.13.3" crypto: ">=3.0.1" device_info_plus: ">=9.0.3" - package_info_plus: ">=4.1.0" sqflite: ">=2.3.3" firebase_messaging: ">=14.9.1" flutter_local_notifications: ">=17.1.0" firebase_core: ">=2.30.1" sqflite_common_ffi: ">=2.3.3" + package_info_plus: ">=4.1.0" dev_dependencies: flutter_test: diff --git a/package/test/dito_sdk_test.dart b/package/test/dito_sdk_test.dart index c33ea0f..4efa75c 100644 --- a/package/test/dito_sdk_test.dart +++ b/package/test/dito_sdk_test.dart @@ -7,15 +7,15 @@ import 'utils.dart'; final DitoSDK dito = DitoSDK(); const id = '22222222222'; -Future setUp() async { - dynamic env = await testEnv(); - dito.initialize(apiKey: env["apiKey"], secretKey: env["secret"]); -} - void main() { group('Dito SDK: ', () { + setUp(() async { + dynamic env = await testEnv(); + dito.initialize(apiKey: env["apiKey"], secretKey: env["secret"]); + }); + test('Send mobile token', () async { - dito.identify(UserEntity(userID: id)); + await dito.identify(UserEntity(userID: id)); final response = await dito.registryMobileToken( token: diff --git a/package/test/event/database_test.dart b/package/test/event/database_test.dart new file mode 100644 index 0000000..e8e4d53 --- /dev/null +++ b/package/test/event/database_test.dart @@ -0,0 +1,99 @@ +import 'package:dito_sdk/data/event_database.dart'; +import 'package:dito_sdk/event/event_entity.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:sqflite_common_ffi/sqflite_ffi.dart'; + +void main() { + setUpAll(() { + sqfliteFfiInit(); + databaseFactory = databaseFactoryFfi; + }); + + group('EventDatabaseService Tests', () { + late EventDatabase eventDatabaseService; + + setUp(() async { + eventDatabaseService = EventDatabase(); + await eventDatabaseService.database; + }); + + tearDown(() async { + await eventDatabaseService.clearDatabase(); + await eventDatabaseService.closeDatabase(); + }); + + test('should insert an event', () async { + final event = EventEntity( + eventName: 'Test Event', + eventMoment: '2024-06-01T12:34:56Z', + revenue: 100.0, + customData: {'key': 'value'}, + ); + + final success = await eventDatabaseService.create(event); + + expect(success, true); + + final events = await eventDatabaseService.fetchAll(); + expect(events.length, 1); + expect(events.first.eventName, 'Test Event'); + }); + + test('should delete an event', () async { + final event = EventEntity( + eventName: 'Test Event', + eventMoment: '2024-06-01T12:34:56Z', + revenue: 100.0, + customData: {'key': 'value'}, + ); + + await eventDatabaseService.create(event); + final success = await eventDatabaseService.delete(event); + + expect(success, true); + + final events = await eventDatabaseService.fetchAll(); + expect(events.isEmpty, true); + }); + + test('should fetch all events', () async { + final event1 = EventEntity( + eventName: 'Test Event 1', + eventMoment: '2024-06-01T12:34:56Z', + revenue: 100.0, + customData: {'key': 'value1'}, + ); + + final event2 = EventEntity( + eventName: 'Test Event 2', + eventMoment: '2024-06-02T12:34:56Z', + revenue: 200.0, + customData: {'key': 'value2'}, + ); + + await eventDatabaseService.create(event1); + await eventDatabaseService.create(event2); + + final events = await eventDatabaseService.fetchAll(); + + expect(events.length, 2); + expect(events[0].eventName, 'Test Event 1'); + expect(events[1].eventName, 'Test Event 2'); + }); + + test('should clear the database', () async { + final event = EventEntity( + eventName: 'Test Event', + eventMoment: '2024-06-01T12:34:56Z', + revenue: 100.0, + customData: {'key': 'value'}, + ); + + await eventDatabaseService.create(event); + await eventDatabaseService.clearDatabase(); + + final events = await eventDatabaseService.fetchAll(); + expect(events.isEmpty, true); + }); + }); +} diff --git a/package/test/event/event_test.dart b/package/test/event/event_test.dart new file mode 100644 index 0000000..00dde03 --- /dev/null +++ b/package/test/event/event_test.dart @@ -0,0 +1,65 @@ +import 'package:dito_sdk/data/event_database.dart'; +import 'package:dito_sdk/dito_sdk.dart'; +import 'package:dito_sdk/user/user_entity.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:sqflite_common_ffi/sqflite_ffi.dart'; + +import '../utils.dart'; + +final DitoSDK dito = DitoSDK(); +const id = '22222222222'; + +void main() async { + TestWidgetsFlutterBinding.ensureInitialized(); + dynamic env = await testEnv(); + + setUpAll(() { + sqfliteFfiInit(); + databaseFactory = databaseFactoryFfi; + dito.initialize(apiKey: env["apiKey"], secretKey: env["secret"]); + }); + + group('Events: ', () { + late EventDatabase database; + + setUp(() async { + database = EventDatabase(); + await database.database; + }); + + tearDown(() async { + await database.clearDatabase(); + await database.closeDatabase(); + }); + + test('Send event without identify', () async { + await dito.trackEvent(eventName: 'event-test-sdk-flutter'); + final events = await database.fetchAll(); + expect(events.length, 1); + expect(events.first.eventName, 'event-test-sdk-flutter'); + }); + + test('Send event with identify', () async { + dito.user.identify(UserEntity(userID: id, email: "teste@teste.com")); + final result = await dito.trackEvent(eventName: 'event-test-sdk-flutter'); + final events = await database.fetchAll(); + + expect(events.length, 0); + expect(result, true); + }); + + test('Send event with custom data', () async { + dito.user.identify(UserEntity(userID: id, email: "teste@teste.com")); + final result = await dito.trackEvent( + eventName: 'event-test-sdk-flutter', + customData: { + "data do ultimo teste": DateTime.now().toIso8601String() + }, + revenue: 10); + final events = await database.fetchAll(); + + expect(events.length, 0); + expect(result, true); + }); + }); +} diff --git a/package/test/user/user_test.dart b/package/test/user/user_test.dart index e2a41a9..b051765 100644 --- a/package/test/user/user_test.dart +++ b/package/test/user/user_test.dart @@ -19,7 +19,8 @@ void main() { }); test('Set User on memory', () async { - dito.user.identify(UserEntity(userID: id, email: "teste@teste.com")); + await dito.user + .identify(UserEntity(userID: id, email: "teste@teste.com")); expect(dito.user.data.id, id); expect(dito.user.data.email, "teste@teste.com"); }); diff --git a/pubspec.lock b/pubspec.lock index ae5792a..2048d15 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,27 +1,6 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: - _fe_analyzer_shared: - dependency: transitive - description: - name: _fe_analyzer_shared - sha256: "5aaf60d96c4cd00fe7f21594b5ad6a1b699c80a27420f8a837f4d68473ef09e3" - url: "https://pub.dev" - source: hosted - version: "68.0.0" - _macros: - dependency: transitive - description: dart - source: sdk - version: "0.1.0" - analyzer: - dependency: transitive - description: - name: analyzer - sha256: "21f1d3720fd1c70316399d5e2bccaebb415c434592d778cce8acb967b8578808" - url: "https://pub.dev" - source: hosted - version: "6.5.0" ansi_styles: dependency: transitive description: @@ -54,70 +33,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" - build: - dependency: transitive - description: - name: build - sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" - url: "https://pub.dev" - source: hosted - version: "2.4.1" - build_config: - dependency: transitive - description: - name: build_config - sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 - url: "https://pub.dev" - source: hosted - version: "1.1.1" - build_daemon: - dependency: transitive - description: - name: build_daemon - sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9" - url: "https://pub.dev" - source: hosted - version: "4.0.2" - build_resolvers: - dependency: transitive - description: - name: build_resolvers - sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" - url: "https://pub.dev" - source: hosted - version: "2.4.2" - build_runner: - dependency: "direct dev" - description: - name: build_runner - sha256: "644dc98a0f179b872f612d3eb627924b578897c629788e858157fa5e704ca0c7" - url: "https://pub.dev" - source: hosted - version: "2.4.11" - build_runner_core: - dependency: transitive - description: - name: build_runner_core - sha256: e3c79f69a64bdfcd8a776a3c28db4eb6e3fb5356d013ae5eb2e52007706d5dbe - url: "https://pub.dev" - source: hosted - version: "7.3.1" - built_collection: - dependency: transitive - description: - name: built_collection - sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" - url: "https://pub.dev" - source: hosted - version: "5.1.1" - built_value: - dependency: transitive - description: - name: built_value - sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb - url: "https://pub.dev" - source: hosted - version: "8.9.2" charcode: dependency: transitive description: @@ -126,14 +41,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" - checked_yaml: - dependency: transitive - description: - name: checked_yaml - sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff - url: "https://pub.dev" - source: hosted - version: "2.0.3" cli_launcher: dependency: transitive description: @@ -150,14 +57,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.4.1" - code_builder: - dependency: transitive - description: - name: code_builder - sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 - url: "https://pub.dev" - source: hosted - version: "4.10.0" collection: dependency: transitive description: @@ -174,30 +73,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.0+1" - convert: - dependency: transitive - description: - name: convert - sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" - url: "https://pub.dev" - source: hosted - version: "3.1.1" - crypto: - dependency: transitive - description: - name: crypto - sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab - url: "https://pub.dev" - source: hosted - version: "3.0.3" - dart_style: - dependency: transitive - description: - name: dart_style - sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" - url: "https://pub.dev" - source: hosted - version: "2.3.6" file: dependency: transitive description: @@ -206,30 +81,6 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.0" - fixnum: - dependency: transitive - description: - name: fixnum - sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - frontend_server_client: - dependency: transitive - description: - name: frontend_server_client - sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 - url: "https://pub.dev" - source: hosted - version: "4.0.0" - get_it: - dependency: transitive - description: - name: get_it - sha256: d85128a5dae4ea777324730dc65edd9c9f43155c109d5cc0a69cab74139fbac1 - url: "https://pub.dev" - source: hosted - version: "7.7.0" glob: dependency: transitive description: @@ -254,14 +105,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.1" - http_multi_server: - dependency: transitive - description: - name: http_multi_server - sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" - url: "https://pub.dev" - source: hosted - version: "3.2.1" http_parser: dependency: transitive description: @@ -270,22 +113,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" - injectable: - dependency: transitive - description: - name: injectable - sha256: "3c8355a29d11ff28c0311bed754649761f345ef7a13ff66a714380954af51226" - url: "https://pub.dev" - source: hosted - version: "2.4.2" - injectable_generator: - dependency: "direct dev" - description: - name: injectable_generator - sha256: "2ca3ada337eac0ef6b82f8049c970ddb63947738fdf32ac6cbef8d1567d7ba05" - url: "https://pub.dev" - source: hosted - version: "2.6.1" io: dependency: transitive description: @@ -294,14 +121,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" - js: - dependency: transitive - description: - name: js - sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf - url: "https://pub.dev" - source: hosted - version: "0.7.1" json_annotation: dependency: transitive description: @@ -310,22 +129,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.9.0" - logging: - dependency: transitive - description: - name: logging - sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - macros: - dependency: transitive - description: - name: macros - sha256: "12e8a9842b5a7390de7a781ec63d793527582398d16ea26c60fed58833c9ae79" - url: "https://pub.dev" - source: hosted - version: "0.1.0-main.0" matcher: dependency: transitive description: @@ -350,14 +153,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.15.0" - mime: - dependency: transitive - description: - name: mime - sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" - url: "https://pub.dev" - source: hosted - version: "1.0.5" mustache_template: dependency: transitive description: @@ -366,14 +161,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" - package_config: - dependency: transitive - description: - name: package_config - sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" - url: "https://pub.dev" - source: hosted - version: "2.1.0" path: dependency: transitive description: @@ -438,14 +225,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.0" - pubspec_parse: - dependency: transitive - description: - name: pubspec_parse - sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8 - url: "https://pub.dev" - source: hosted - version: "1.3.0" quiver: dependency: transitive description: @@ -454,38 +233,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.1" - recase: - dependency: transitive - description: - name: recase - sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213 - url: "https://pub.dev" - source: hosted - version: "4.1.0" - shelf: - dependency: transitive - description: - name: shelf - sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 - url: "https://pub.dev" - source: hosted - version: "1.4.1" - shelf_web_socket: - dependency: transitive - description: - name: shelf_web_socket - sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" - url: "https://pub.dev" - source: hosted - version: "2.0.0" - source_gen: - dependency: transitive - description: - name: source_gen - sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" - url: "https://pub.dev" - source: hosted - version: "1.5.0" source_span: dependency: transitive description: @@ -510,14 +257,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" - stream_transform: - dependency: transitive - description: - name: stream_transform - sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" - url: "https://pub.dev" - source: hosted - version: "2.1.0" string_scanner: dependency: transitive description: @@ -542,14 +281,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.1" - timing: - dependency: transitive - description: - name: timing - sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" - url: "https://pub.dev" - source: hosted - version: "1.0.1" typed_data: dependency: transitive description: @@ -566,14 +297,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" - watcher: - dependency: transitive - description: - name: watcher - sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" - url: "https://pub.dev" - source: hosted - version: "1.1.0" web: dependency: transitive description: @@ -582,22 +305,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.1" - web_socket: - dependency: transitive - description: - name: web_socket - sha256: "24301d8c293ce6fe327ffe6f59d8fd8834735f0ec36e4fd383ec7ff8a64aa078" - url: "https://pub.dev" - source: hosted - version: "0.1.5" - web_socket_channel: - dependency: transitive - description: - name: web_socket_channel - sha256: a2d56211ee4d35d9b344d9d4ce60f362e4f5d1aafb988302906bd732bc731276 - url: "https://pub.dev" - source: hosted - version: "3.0.0" yaml: dependency: transitive description: @@ -615,4 +322,4 @@ packages: source: hosted version: "2.2.1" sdks: - dart: ">=3.4.0 <4.0.0" + dart: ">=3.3.0 <4.0.0" From 415bd05b7c60a29ad54a58e46bcf230165b867a3 Mon Sep 17 00:00:00 2001 From: Igor Duarte Date: Tue, 11 Jun 2024 11:26:06 -0300 Subject: [PATCH 10/36] adding block on CHANGELOG.md Signed-off-by: Igor Duarte --- package/CHANGELOG.md | 82 +++++++++++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 27 deletions(-) diff --git a/package/CHANGELOG.md b/package/CHANGELOG.md index 1b89e77..120638d 100644 --- a/package/CHANGELOG.md +++ b/package/CHANGELOG.md @@ -1,3 +1,30 @@ +## 0.5.5 () + +### New Features: + +- User domain implementation; +- Creation of event domain; +- Changes on SDK architecture; +- Change implementation of identify to `DitoSDK.identify(UserEntity(userID: ''))`; + +### Bug Fixes: + +- Hotfix: Changed Data Payload type to string. +- Prevented sending opened notifications with empty ID. + +### Improvements: + +- Version update. +- Configured DitoApi instance. +- Removed unnecessary endpoints and domains. + +### Notable Commits: + +- [NOT-3023] DitoApi +- [NOT-3023] constants +- feat: User domain implementation +- feat: create event domain + ## 0.5.4 (Jun 05, 2024) ### Bug Fixes @@ -31,44 +58,45 @@ ### Bug Fixes - Bug Fixes - - Backward compatibility for dependent packages; - - `openNotification()` in the request submission contract. + - Backward compatibility for dependent packages; + - `openNotification()` in the request submission contract. - Additional Notes - - No additional notes in this version. + - No additional notes in this version. ## 0.5.0 (April 30, 2024) ### New Features - Added the following methods: - - `setUser()`: Method to save user data before an identify. - - `removeMobileToken()`: Method to remove a user's token. - - `initializePushNotificationService()`: Method to initialize the mobile push notification service. - - `setAndroidDetails()`: Method to customize the mobile push notification service on Android. - - `setIosDetails()`: Method to customize the mobile push notification service on iOS. + - `setUser()`: Method to save user data before an identify. + - `removeMobileToken()`: Method to remove a user's token. + - `initializePushNotificationService()`: Method to initialize the mobile push notification + service. + - `setAndroidDetails()`: Method to customize the mobile push notification service on Android. + - `setIosDetails()`: Method to customize the mobile push notification service on iOS. - Changes - - Removed setUserAgent method, which is now generated automatically. - - Deprecated setUserId method. + - Removed setUserAgent method, which is now generated automatically. + - Deprecated setUserId method. - Bug Fixes - - No bug fixes in this version. + - No bug fixes in this version. - Additional Notes - - No additional notes in this version. + - No additional notes in this version. ## 0.4.0 (November 23, 2023) ### New Features - Added the following methods: - - `registryMobileToken()`: Method to register the mobile token for the user. - - `openNotification()`: Method to notify of the opening of a mobile notification. + - `registryMobileToken()`: Method to register the mobile token for the user. + - `openNotification()`: Method to notify of the opening of a mobile notification. - Changes - Removed encoding attribute from all requests. - Removed json.encode from the data attribute in the identifyUser() method. - `identifyUser()` method returning the request result. (changed to Future). - Bug Fixes - - No bug fixes in this version. + - No bug fixes in this version. - Additional Notes - - No additional notes in this version. + - No additional notes in this version. ## 0.3.0 (October 26, 2023) @@ -77,35 +105,35 @@ - Event storage while not having a registered userID. - Sending stored events as soon as a userID is registered. - Changes - - No changes in this version. + - No changes in this version. - Bug Fixes - - No bug fixes in this version. + - No bug fixes in this version. - Additional Notes - - No additional notes in this version. + - No additional notes in this version. ## 0.2.0 (October 10, 2023) ### Refactor - Changes - - Renamed registerUser() method to identifyUser(). - - Documentation improvements. + - Renamed registerUser() method to identifyUser(). + - Documentation improvements. - Bug Fixes - - No bug fixes in this version. + - No bug fixes in this version. - Additional Notes - - No additional notes in this version. + - No additional notes in this version. ## 0.1.1 (October 2, 2023) ### Refectory - Changes - - Removed print from methods. - - Exception handling in methods that contain requests. + - Removed print from methods. + - Exception handling in methods that contain requests. - Bug Fixes - - No bug fixes in this version. + - No bug fixes in this version. - Additional Notes - - No additional notes in this version. + - No additional notes in this version. ## 0.1.0 (October 2, 2023) From 742ce898ac5a4b40a011b99618e9ee2112ea83d5 Mon Sep 17 00:00:00 2001 From: Ana Machado Date: Tue, 11 Jun 2024 07:52:19 -0300 Subject: [PATCH 11/36] [NOT-3026] Notification --- README.md | 2 +- example/lib/app_form.dart | 8 +- example/lib/main.dart | 22 +-- package/README.md | 2 +- package/lib/dito_sdk.dart | 41 ++-- package/lib/entity/custom_notification.dart | 19 -- .../notification/notification_controller.dart | 75 +++++++ .../notification_entity.dart} | 32 ++- .../notification/notification_repository.dart | 58 ++++++ .../notification/notification_service.dart | 97 ++++++++++ .../lib/services/notification_service.dart | 183 ------------------ package/test/dito_sdk_test.dart | 7 - 12 files changed, 272 insertions(+), 274 deletions(-) delete mode 100644 package/lib/entity/custom_notification.dart create mode 100644 package/lib/notification/notification_controller.dart rename package/lib/{entity/data_payload.dart => notification/notification_entity.dart} (56%) create mode 100644 package/lib/notification/notification_repository.dart create mode 100644 package/lib/notification/notification_service.dart delete mode 100644 package/lib/services/notification_service.dart diff --git a/README.md b/README.md index b9bc54a..fde35ca 100644 --- a/README.md +++ b/README.md @@ -303,7 +303,7 @@ import 'package:dito_sdk/dito_sdk.dart'; Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { final notification = DataPayload.fromJson(jsonDecode(message.data["data"])); - dito.notificationService().showLocalNotification(CustomNotification( + dito.notificationService().showLocalNotification(NotificationEntity( id: message.hashCode, title: notification.details.title || "O nome do aplicativo", body: notification.details.message, diff --git a/example/lib/app_form.dart b/example/lib/app_form.dart index c69bb4c..8418a9a 100644 --- a/example/lib/app_form.dart +++ b/example/lib/app_form.dart @@ -1,6 +1,6 @@ import 'package:dito_sdk/dito_sdk.dart'; -import 'package:dito_sdk/entity/custom_notification.dart'; import 'package:dito_sdk/user/user_entity.dart'; +import 'package:dito_sdk/notification/notification_entity.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -20,8 +20,8 @@ class AppFormState extends State { Widget build(BuildContext context) { final dito = Provider.of(context); - String cpf = "22222222222"; - String email = "teste@dito.com.br"; + String cpf = "32190381209"; + String email = "teste.sdk2@dito.com.br"; identify() async { final user = UserEntity( @@ -58,7 +58,7 @@ class AppFormState extends State { } handleLocalNotification() { - dito.notificationService().addNotificationToStream(CustomNotification( + dito.notificationService().addNotificationToStream(NotificationEntity( id: 123, title: "Notificação local", body: diff --git a/example/lib/main.dart b/example/lib/main.dart index 790ba6f..c8837fd 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,12 +1,6 @@ -import 'dart:convert'; - import 'package:dito_sdk/dito_sdk.dart'; -import 'package:dito_sdk/entity/custom_notification.dart'; -import 'package:dito_sdk/entity/data_payload.dart'; -import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; -import 'package:package_info_plus/package_info_plus.dart'; import 'package:provider/provider.dart'; import 'app.dart'; @@ -14,27 +8,13 @@ import 'constants.dart'; @pragma('vm:entry-point') Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { - PackageInfo packageInfo = await PackageInfo.fromPlatform(); - final appName = packageInfo.appName; - DitoSDK dito = DitoSDK(); - dito.initialize( + dito.onBackgroundMessageHandler(message, apiKey: Constants.ditoApiKey, secretKey: Constants.ditoSecretKey); - await dito.initializePushNotificationService(); - - final notification = DataPayload.fromJson(jsonDecode(message.data["data"])); - - dito.notificationService().showLocalNotification(CustomNotification( - id: message.hashCode, - title: appName, - body: notification.details.message, - payload: notification)); } void main() async { WidgetsFlutterBinding.ensureInitialized(); - - await Firebase.initializeApp(); FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); DitoSDK dito = DitoSDK(); diff --git a/package/README.md b/package/README.md index 7a5553c..fddb963 100644 --- a/package/README.md +++ b/package/README.md @@ -369,7 +369,7 @@ import 'package:dito_sdk/dito_sdk.dart'; Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { final notification = DataPayload.fromJson(jsonDecode(message.data["data"])); - dito.notificationService().showLocalNotification(CustomNotification( + dito.notificationService().showLocalNotification(NotificationEntity( id: message.hashCode, title: notification.details.title || "O nome do aplicativo", body: notification.details.message, diff --git a/package/lib/dito_sdk.dart b/package/lib/dito_sdk.dart index 5353cd2..298ce79 100644 --- a/package/lib/dito_sdk.dart +++ b/package/lib/dito_sdk.dart @@ -7,17 +7,18 @@ import 'package:http/http.dart' as http; import 'data/dito_api.dart'; import 'event/event_entity.dart'; import 'event/event_interface.dart'; -import 'services/notification_service.dart'; +import 'notification/notification_service.dart'; import 'user/user_entity.dart'; import 'user/user_interface.dart'; /// DitoSDK is a singleton class that provides various methods to interact with Dito API /// and manage user data, events, and push notifications. class DitoSDK { + final DitoApi _ditoApi = DitoApi(); final UserInterface _userInterface = UserInterface(); final EventInterface _eventInterface = EventInterface(); - final DitoApi _ditoApi = DitoApi(); - late NotificationService _notificationService; + final NotificationService _notificationService = NotificationService(); + static final DitoSDK _instance = DitoSDK._internal(); factory DitoSDK() { @@ -40,24 +41,12 @@ class DitoSDK { /// [apiKey] - The API key for the Dito platform. /// [secretKey] - The secret key for the Dito platform. void initialize({required String apiKey, required String secretKey}) async { - _notificationService = NotificationService(_instance); _ditoApi.setKeys(apiKey, secretKey); } /// This method initializes the push notification service using Firebase. Future initializePushNotificationService() async { - await Firebase.initializeApp(); await _notificationService.initialize(); - - RemoteMessage? initialMessage = - await FirebaseMessaging.instance.getInitialMessage(); - - if (initialMessage != null) { - _notificationService.handleMessage(initialMessage); - } - - FirebaseMessaging.onMessageOpenedApp - .listen(_notificationService.handleMessage); } /// This method enables saving and sending user data to the Dito API. @@ -91,7 +80,7 @@ class DitoSDK { /// [token] - The mobile token to be registered. /// Returns an http.Response. Future registryMobileToken({required String token}) async { - return await _ditoApi.registryMobileToken(token, _userInterface.data); + return await _notificationService.registryMobileToken(token); } /// This method removes a mobile token from the push notification service. @@ -99,20 +88,14 @@ class DitoSDK { /// [token] - The mobile token to be removed. /// Returns an http.Response. Future removeMobileToken({required String token}) async { - return await _ditoApi.removeMobileToken(token, _userInterface.data); + return await _notificationService.removeMobileToken(token); } - /// This method opens a notification and sends its data to the Dito API. - /// - /// [notificationId] - The ID of the notification. - /// [identifier] - The identifier for the notification. - /// [reference] - The reference for the notification. - /// Returns an http.Response. - Future openNotification( - {required String notificationId, - required String identifier, - required String reference}) async { - return await _ditoApi.openNotification( - notificationId, identifier, reference); + Future onBackgroundMessageHandler(RemoteMessage message, + {required String apiKey, required String secretKey}) async { + _ditoApi.setKeys(apiKey, secretKey); + await Firebase.initializeApp(); + await _notificationService.initialize(); + return await _notificationService.onMessage(message); } } diff --git a/package/lib/entity/custom_notification.dart b/package/lib/entity/custom_notification.dart deleted file mode 100644 index 5543655..0000000 --- a/package/lib/entity/custom_notification.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:firebase_messaging/firebase_messaging.dart'; - -import 'data_payload.dart'; - -class CustomNotification { - final int id; - final String title; - final String body; - final DataPayload? payload; - final RemoteMessage? remoteMessage; - - CustomNotification({ - required this.id, - required this.title, - required this.body, - this.payload, - this.remoteMessage, - }); -} diff --git a/package/lib/notification/notification_controller.dart b/package/lib/notification/notification_controller.dart new file mode 100644 index 0000000..aacaad8 --- /dev/null +++ b/package/lib/notification/notification_controller.dart @@ -0,0 +1,75 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +import 'notification_entity.dart'; + +class NotificationController { + final Function(DataPayload) onSelectNotification; + late FlutterLocalNotificationsPlugin localNotificationsPlugin; + + NotificationController({required this.onSelectNotification}); + + AndroidNotificationDetails androidDetails = const AndroidNotificationDetails( + 'dito_notifications', + 'Notifications sended by Dito', + channelDescription: 'Notifications sended by Dito', + importance: Importance.max, + priority: Priority.max, + enableVibration: true, + ); + + DarwinNotificationDetails iosDetails = const DarwinNotificationDetails( + presentAlert: true, + presentBadge: true, + presentSound: true, + presentBanner: true); + + initialize() async { + localNotificationsPlugin = FlutterLocalNotificationsPlugin(); + const android = AndroidInitializationSettings('@mipmap/ic_launcher'); + const ios = DarwinInitializationSettings(); + + const InitializationSettings initializationSettings = + InitializationSettings(android: android, iOS: ios); + + await localNotificationsPlugin.initialize(initializationSettings, + onDidReceiveNotificationResponse: onTapNotification); + +// Precisa completar a config no app? https://firebase.flutter.dev/docs/messaging/notifications/#notification-channels + await _setupAndroidChannel(); + } + + _setupAndroidChannel() async { + const AndroidNotificationChannel channel = AndroidNotificationChannel( + 'dito_notifications', + 'Notifications sended by Dito', + importance: Importance.max, + ); + + await localNotificationsPlugin + .resolvePlatformSpecificImplementation< + AndroidFlutterLocalNotificationsPlugin>() + ?.createNotificationChannel(channel); + } + + showNotification(NotificationEntity notification) { + localNotificationsPlugin.show( + notification.id, + notification.title, + notification.body, + NotificationDetails(android: androidDetails, iOS: iosDetails), + payload: jsonEncode(notification.payload?.toJson()), + ); + } + + Future onTapNotification(NotificationResponse? response) async { + final payload = response?.payload; + + if (payload != null && payload.isNotEmpty) { + final data = DataPayload.fromJson(jsonDecode(payload)); + + onSelectNotification(data); + } + } +} diff --git a/package/lib/entity/data_payload.dart b/package/lib/notification/notification_entity.dart similarity index 56% rename from package/lib/entity/data_payload.dart rename to package/lib/notification/notification_entity.dart index f2cf865..69e4632 100644 --- a/package/lib/entity/data_payload.dart +++ b/package/lib/notification/notification_entity.dart @@ -1,25 +1,24 @@ class Details { final String? link; final String message; + final String? title; - Details(this.link, this.message); + Details(this.link, this.message, this.title); factory Details.fromJson(dynamic json) { assert(json is Map); - return Details(json["link"], json["message"]); + return Details(json["link"], json["message"], json["title"]); } - Map toJson() => { - 'link': link, - 'message': message, - }; + Map toJson() => + {'link': link, 'message': message, 'title': title}; } class DataPayload { final String reference; final String identifier; - final String notification; - final String notification_log_id; + final String? notification; + final String? notification_log_id; final Details details; DataPayload(this.reference, this.identifier, this.notification, @@ -30,7 +29,8 @@ class DataPayload { return DataPayload( json["reference"], - json["identifier"], + // removendo json["identifier"] por causa do erro + json["notification_log_id"], json["notification"], json["notification_log_id"], Details.fromJson(json["details"])); @@ -44,3 +44,17 @@ class DataPayload { 'details': details.toJson(), }; } + +class NotificationEntity { + final int id; + final String title; + final String body; + final DataPayload? payload; + + NotificationEntity({ + required this.id, + required this.title, + required this.body, + this.payload, + }); +} diff --git a/package/lib/notification/notification_repository.dart b/package/lib/notification/notification_repository.dart new file mode 100644 index 0000000..f4e3fe7 --- /dev/null +++ b/package/lib/notification/notification_repository.dart @@ -0,0 +1,58 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:http/http.dart' as http; + +import '../data/dito_api.dart'; +import '../user/user_interface.dart'; + +class NotificationRepository { + final DitoApi _ditoApi = DitoApi(); + final UserInterface _userInterface = UserInterface(); + + Future initializeFirebaseMessaging( + Function(RemoteMessage) onMessage) async { + await Firebase.initializeApp(); + +// por que só para android? + if (Platform.isAndroid) { + await FirebaseMessaging.instance.setAutoInitEnabled(true); + } + +// parece não ter utilidade https://firebase.flutter.dev/docs/messaging/notifications/#handling-interaction + RemoteMessage? initialMessage = + await FirebaseMessaging.instance.getInitialMessage(); + + if (initialMessage != null) { + onMessage(initialMessage); + } + + FirebaseMessaging.onMessageOpenedApp.listen(onMessage); + +// alterar só para iOS? https://firebase.flutter.dev/docs/messaging/notifications/#ios-configuration + await FirebaseMessaging.instance + .setForegroundNotificationPresentationOptions( + badge: true, sound: true, alert: true); + + FirebaseMessaging.onMessage.listen(onMessage); + } + + Future checkPermissions() async { + var settings = await FirebaseMessaging.instance.requestPermission(); + return settings.authorizationStatus == AuthorizationStatus.authorized; + } + + Future getFirebaseToken() async { + return FirebaseMessaging.instance.getToken(); + } + + Future registryMobileToken(String token) async { + return await _ditoApi.registryMobileToken(token, _userInterface.data); + } + + Future removeMobileToken(String token) async { + return await _ditoApi.removeMobileToken(token, _userInterface.data); + } +} diff --git a/package/lib/notification/notification_service.dart b/package/lib/notification/notification_service.dart new file mode 100644 index 0000000..09123a6 --- /dev/null +++ b/package/lib/notification/notification_service.dart @@ -0,0 +1,97 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:flutter/foundation.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:package_info_plus/package_info_plus.dart'; + +import '../data/dito_api.dart'; +import 'notification_entity.dart'; +import 'notification_repository.dart'; +import 'notification_controller.dart'; + +class NotificationService { + final NotificationRepository _repository = NotificationRepository(); + late NotificationController _controller; + final DitoApi _ditoApi = DitoApi(); + + final StreamController + _didReceiveLocalNotificationStream = + StreamController.broadcast(); + final StreamController _selectNotificationStream = + StreamController.broadcast(); + + Future initialize() async { + _controller = + NotificationController(onSelectNotification: onSelectNotification); + await _repository.initializeFirebaseMessaging(onMessage); + await _controller.initialize(); + _listenStream(); + } + +// continua exportando esse metodo? + addNotificationToStream(NotificationEntity notification) { + _didReceiveLocalNotificationStream.add(notification); + } + + void dispose() { + _didReceiveLocalNotificationStream.close(); + _selectNotificationStream.close(); + } + + _listenStream() { + _didReceiveLocalNotificationStream.stream + .listen((NotificationEntity receivedNotification) { + _controller.showNotification(receivedNotification); + }); + _selectNotificationStream.stream.listen((DataPayload data) async { + final notificationId = data.notification; + + if (notificationId != null && notificationId.isNotEmpty) { + await _ditoApi.openNotification( + notificationId, data.identifier, data.reference); + } + }); + } + + Future onMessage(RemoteMessage message) async { + if (message.data["data"] == null) { + if (kDebugMode) { + print("Data is not defined: ${message.data}"); + } + } + + final notification = DataPayload.fromJson(jsonDecode(message.data["data"])); + + final messagingAllowed = await _repository.checkPermissions(); + + PackageInfo packageInfo = await PackageInfo.fromPlatform(); + final appName = packageInfo.appName; + + if (messagingAllowed && notification.details.message.isNotEmpty) { + addNotificationToStream(NotificationEntity( + id: message.hashCode, + title: notification.details.title ?? appName, + body: notification.details.message, + payload: notification)); + } + } + + Future onSelectNotification(DataPayload? data) async { + if (data != null) { + _selectNotificationStream.add(data); + } + } + + Future getDeviceFirebaseToken() async { + return _repository.getFirebaseToken(); + } + + registryMobileToken(String token) async { + return await _repository.registryMobileToken(token); + } + + removeMobileToken(String token) async { + return await _repository.removeMobileToken(token); + } +} diff --git a/package/lib/services/notification_service.dart b/package/lib/services/notification_service.dart deleted file mode 100644 index e51229e..0000000 --- a/package/lib/services/notification_service.dart +++ /dev/null @@ -1,183 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; - -import 'package:firebase_messaging/firebase_messaging.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter_local_notifications/flutter_local_notifications.dart'; -import 'package:package_info_plus/package_info_plus.dart'; - -import '../dito_sdk.dart'; -import '../entity/custom_notification.dart'; -import '../entity/data_payload.dart'; - -class NotificationService { - bool _messagingAllowed = false; - late String _appName; - late DitoSDK _dito; - late FlutterLocalNotificationsPlugin localNotificationsPlugin; - final StreamController didReceiveLocalNotificationStream = - StreamController.broadcast(); - final StreamController selectNotificationStream = - StreamController.broadcast(); - - AndroidNotificationDetails androidDetails = const AndroidNotificationDetails( - 'dito_notifications', - 'Notifications sended by Dito', - channelDescription: 'Notifications sended by Dito', - importance: Importance.max, - priority: Priority.max, - enableVibration: true, - ); - - DarwinNotificationDetails iosDetails = const DarwinNotificationDetails( - presentAlert: true, - presentBadge: true, - presentSound: true, - presentBanner: true); - - NotificationService(DitoSDK dito) { - _dito = dito; - } - - Future initialize() async { - if (Platform.isAndroid) { - await FirebaseMessaging.instance.setAutoInitEnabled(true); - } - - localNotificationsPlugin = FlutterLocalNotificationsPlugin(); - _setupNotifications(); - - await FirebaseMessaging.instance - .setForegroundNotificationPresentationOptions( - badge: true, sound: true, alert: true); - - PackageInfo packageInfo = await PackageInfo.fromPlatform(); - _appName = packageInfo.appName; - - await checkPermissions(); - _onMessage(); - } - - Future getDeviceFirebaseToken() async { - return FirebaseMessaging.instance.getToken(); - } - - _onMessage() { - FirebaseMessaging.onMessage.listen(handleMessage); - FirebaseMessaging.onMessageOpenedApp.listen(handleMessage); - } - - void handleMessage(RemoteMessage message) { - if (message.data["data"] == null) { - if (kDebugMode) { - print("Data is not defined: ${message.data}"); - } - } - - final notification = DataPayload.fromJson(jsonDecode(message.data["data"])); - - if (_messagingAllowed && notification.details.message.isNotEmpty) { - didReceiveLocalNotificationStream.add(CustomNotification( - id: message.hashCode, - title: _appName, - body: notification.details.message, - payload: notification)); - } - } - - addNotificationToStream(CustomNotification notification) { - didReceiveLocalNotificationStream.add(notification); - } - - Future checkPermissions() async { - var settings = await FirebaseMessaging.instance.getNotificationSettings(); - - if (settings.authorizationStatus != AuthorizationStatus.authorized) { - await FirebaseMessaging.instance.requestPermission(); - settings = await FirebaseMessaging.instance.getNotificationSettings(); - _messagingAllowed = - (settings.authorizationStatus == AuthorizationStatus.authorized); - } else { - _messagingAllowed = true; - } - } - - _setupAndroidChannel() async { - const AndroidNotificationChannel channel = AndroidNotificationChannel( - 'dito_notifications', - 'Notifications sended by Dito', - importance: Importance.max, - ); - - await localNotificationsPlugin - .resolvePlatformSpecificImplementation< - AndroidFlutterLocalNotificationsPlugin>() - ?.createNotificationChannel(channel); - } - - _setupNotifications() async { - await _initializeNotifications(); - await _setupAndroidChannel(); - _listenStream(); - } - - _initializeNotifications() async { - const android = AndroidInitializationSettings('@mipmap/ic_launcher'); - const ios = DarwinInitializationSettings(); - - const InitializationSettings initializationSettings = - InitializationSettings(android: android, iOS: ios); - - await localNotificationsPlugin.initialize(initializationSettings, - onDidReceiveNotificationResponse: onTapNotification); - } - - void dispose() { - didReceiveLocalNotificationStream.close(); - selectNotificationStream.close(); - } - - _listenStream() { - didReceiveLocalNotificationStream.stream - .listen((CustomNotification receivedNotification) { - showLocalNotification(receivedNotification); - }); - selectNotificationStream.stream.listen((String? payload) async { - if (payload != null) { - final data = DataPayload.fromJson(jsonDecode(payload)); - - if (data.notification.isNotEmpty) { - await _dito.openNotification( - notificationId: data.notification, - identifier: data.identifier, - reference: data.reference); - } - } - }); - } - - setAndroidDetails(AndroidNotificationDetails details) { - androidDetails = details; - } - - setIosDetails(DarwinNotificationDetails details) { - iosDetails = details; - } - - showLocalNotification(CustomNotification notification) { - localNotificationsPlugin.show( - notification.id, - notification.title, - notification.body, - NotificationDetails(android: androidDetails, iOS: iosDetails), - payload: jsonEncode(notification.payload?.toJson()), - ); - } - - Future onTapNotification(NotificationResponse? response) async { - if (response?.payload != null) { - selectNotificationStream.add(response?.payload); - } - } -} diff --git a/package/test/dito_sdk_test.dart b/package/test/dito_sdk_test.dart index 4efa75c..41d660f 100644 --- a/package/test/dito_sdk_test.dart +++ b/package/test/dito_sdk_test.dart @@ -23,12 +23,5 @@ void main() { expect(response.statusCode, 200); }); - - test('Send open notification', () async { - final response = await dito.openNotification( - notificationId: '723422', identifier: '1713466024', reference: id); - - expect(response.statusCode, 200); - }); }); } From 08bf5a1670fb5aef958204f6fb2ed50ad8647e5c Mon Sep 17 00:00:00 2001 From: Ana Machado Date: Wed, 12 Jun 2024 09:12:16 -0300 Subject: [PATCH 12/36] [NOT-3026] Add doc and change notification service to interface --- example/lib/app_form.dart | 19 +------- package/lib/dito_sdk.dart | 33 +++++++------ .../notification/notification_controller.dart | 7 +++ ...rvice.dart => notification_interface.dart} | 47 +++++++++++++++---- .../notification/notification_repository.dart | 31 +++++++++--- 5 files changed, 89 insertions(+), 48 deletions(-) rename package/lib/notification/{notification_service.dart => notification_interface.dart} (64%) diff --git a/example/lib/app_form.dart b/example/lib/app_form.dart index 8418a9a..4bb0318 100644 --- a/example/lib/app_form.dart +++ b/example/lib/app_form.dart @@ -1,6 +1,5 @@ import 'package:dito_sdk/dito_sdk.dart'; import 'package:dito_sdk/user/user_entity.dart'; -import 'package:dito_sdk/notification/notification_entity.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -29,7 +28,7 @@ class AppFormState extends State { await dito.user.identify(user); - final token = await dito.notificationService().getDeviceFirebaseToken(); + final token = await dito.getMobileToken(); if (token != null && token.isNotEmpty) { dito.registryMobileToken(token: token); @@ -57,18 +56,6 @@ class AppFormState extends State { } } - handleLocalNotification() { - dito.notificationService().addNotificationToStream(NotificationEntity( - id: 123, - title: "Notificação local", - body: - "Está é uma mensagem de teste, validando o stream de dados das notificações locais")); - - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Push enviado para a fila')), - ); - } - return Form( key: _formKey, child: Column( @@ -110,10 +97,6 @@ class AppFormState extends State { onPressed: handleNotification, child: const Text('Receber Notification'), ), - TextButton( - onPressed: handleLocalNotification, - child: const Text('Criar notificação local'), - ) ]))) ], ), diff --git a/package/lib/dito_sdk.dart b/package/lib/dito_sdk.dart index 298ce79..c07565b 100644 --- a/package/lib/dito_sdk.dart +++ b/package/lib/dito_sdk.dart @@ -7,17 +7,17 @@ import 'package:http/http.dart' as http; import 'data/dito_api.dart'; import 'event/event_entity.dart'; import 'event/event_interface.dart'; -import 'notification/notification_service.dart'; +import 'notification/notification_interface.dart'; import 'user/user_entity.dart'; import 'user/user_interface.dart'; /// DitoSDK is a singleton class that provides various methods to interact with Dito API /// and manage user data, events, and push notifications. class DitoSDK { - final DitoApi _ditoApi = DitoApi(); + final DitoApi _api = DitoApi(); final UserInterface _userInterface = UserInterface(); final EventInterface _eventInterface = EventInterface(); - final NotificationService _notificationService = NotificationService(); + final NotificationInterface _notificationInterface = NotificationInterface(); static final DitoSDK _instance = DitoSDK._internal(); @@ -31,22 +31,18 @@ class DitoSDK { /// Returns an instance of UserInterface class. UserInterface get user => _userInterface; - NotificationService notificationService() { - return _notificationService; - } - /// This method initializes the SDK with the provided API key and secret key. /// It also initializes the NotificationService and assigns API key and SHA1 signature. /// /// [apiKey] - The API key for the Dito platform. /// [secretKey] - The secret key for the Dito platform. void initialize({required String apiKey, required String secretKey}) async { - _ditoApi.setKeys(apiKey, secretKey); + _api.setKeys(apiKey, secretKey); } /// This method initializes the push notification service using Firebase. Future initializePushNotificationService() async { - await _notificationService.initialize(); + await _notificationInterface.initialize(); } /// This method enables saving and sending user data to the Dito API. @@ -75,12 +71,19 @@ class DitoSDK { return await _eventInterface.trackEvent(event); } + /// This method get the mobile token for push notifications. + /// + /// Returns a String or null. + Future getMobileToken() async { + return _notificationInterface.getDeviceFirebaseToken(); + } + /// This method registers a mobile token for push notifications. /// /// [token] - The mobile token to be registered. /// Returns an http.Response. Future registryMobileToken({required String token}) async { - return await _notificationService.registryMobileToken(token); + return await _notificationInterface.registryMobileToken(token); } /// This method removes a mobile token from the push notification service. @@ -88,14 +91,16 @@ class DitoSDK { /// [token] - The mobile token to be removed. /// Returns an http.Response. Future removeMobileToken({required String token}) async { - return await _notificationService.removeMobileToken(token); + return await _notificationInterface.removeMobileToken(token); } + /// This method is a handler for manage messages in the background. + /// It initializes Firebase and Dito, then push the message. Future onBackgroundMessageHandler(RemoteMessage message, {required String apiKey, required String secretKey}) async { - _ditoApi.setKeys(apiKey, secretKey); + _api.setKeys(apiKey, secretKey); await Firebase.initializeApp(); - await _notificationService.initialize(); - return await _notificationService.onMessage(message); + await _notificationInterface.initialize(); + return await _notificationInterface.onMessage(message); } } diff --git a/package/lib/notification/notification_controller.dart b/package/lib/notification/notification_controller.dart index aacaad8..47619a9 100644 --- a/package/lib/notification/notification_controller.dart +++ b/package/lib/notification/notification_controller.dart @@ -25,6 +25,7 @@ class NotificationController { presentSound: true, presentBanner: true); + /// This method initializes localNotificationsPlugin initialize() async { localNotificationsPlugin = FlutterLocalNotificationsPlugin(); const android = AndroidInitializationSettings('@mipmap/ic_launcher'); @@ -53,6 +54,9 @@ class NotificationController { ?.createNotificationChannel(channel); } + /// This method uses local notifications plugin to show messages on the screen + /// + /// [notification] - NotificationEntity object showNotification(NotificationEntity notification) { localNotificationsPlugin.show( notification.id, @@ -63,6 +67,9 @@ class NotificationController { ); } + /// This method is called when user clicks on the notification + /// + /// [response] - NotificationResponse object Future onTapNotification(NotificationResponse? response) async { final payload = response?.payload; diff --git a/package/lib/notification/notification_service.dart b/package/lib/notification/notification_interface.dart similarity index 64% rename from package/lib/notification/notification_service.dart rename to package/lib/notification/notification_interface.dart index 09123a6..40468d3 100644 --- a/package/lib/notification/notification_service.dart +++ b/package/lib/notification/notification_interface.dart @@ -10,17 +10,23 @@ import 'notification_entity.dart'; import 'notification_repository.dart'; import 'notification_controller.dart'; -class NotificationService { +/// NotificationInterface is an interface for communication with the notification repository and notification controller +class NotificationInterface { final NotificationRepository _repository = NotificationRepository(); late NotificationController _controller; - final DitoApi _ditoApi = DitoApi(); + final DitoApi _api = DitoApi(); + /// The broadcast stream for received notifications final StreamController _didReceiveLocalNotificationStream = StreamController.broadcast(); + + /// The broadcast stream for selected notifications final StreamController _selectNotificationStream = StreamController.broadcast(); + /// This method initializes notification controller and notification repository. + /// Start listening to notifications Future initialize() async { _controller = NotificationController(onSelectNotification: onSelectNotification); @@ -29,16 +35,13 @@ class NotificationService { _listenStream(); } -// continua exportando esse metodo? - addNotificationToStream(NotificationEntity notification) { - _didReceiveLocalNotificationStream.add(notification); - } - + // This method turns off the streams when this class is unmounted void dispose() { _didReceiveLocalNotificationStream.close(); _selectNotificationStream.close(); } + // This method initializes the listeners on streams _listenStream() { _didReceiveLocalNotificationStream.stream .listen((NotificationEntity receivedNotification) { @@ -47,13 +50,25 @@ class NotificationService { _selectNotificationStream.stream.listen((DataPayload data) async { final notificationId = data.notification; + // Only sends the event if the message is linked to a notification if (notificationId != null && notificationId.isNotEmpty) { - await _ditoApi.openNotification( + await _api.openNotification( notificationId, data.identifier, data.reference); } }); } + /// This method adds a received notification to stream + /// + /// [notification] - NotificationEntity object. + _addNotificationToStream(NotificationEntity notification) { + _didReceiveLocalNotificationStream.add(notification); + } + + /// This method is a handler for new remote messages. + /// Check permissions and add notification to the stream. + /// + /// [message] - RemoteMessage object. Future onMessage(RemoteMessage message) async { if (message.data["data"] == null) { if (kDebugMode) { @@ -69,7 +84,7 @@ class NotificationService { final appName = packageInfo.appName; if (messagingAllowed && notification.details.message.isNotEmpty) { - addNotificationToStream(NotificationEntity( + _addNotificationToStream(NotificationEntity( id: message.hashCode, title: notification.details.title ?? appName, body: notification.details.message, @@ -77,20 +92,34 @@ class NotificationService { } } + /// This method adds a selected notification to stream + /// + /// [data] - DataPayload object. Future onSelectNotification(DataPayload? data) async { if (data != null) { _selectNotificationStream.add(data); } } + /// This method get the mobile token for push notifications. + /// + /// Returns a String or null. Future getDeviceFirebaseToken() async { return _repository.getFirebaseToken(); } + /// This method registers a mobile token for push notifications. + /// + /// [token] - The mobile token to be registered. + /// Returns an http.Response. registryMobileToken(String token) async { return await _repository.registryMobileToken(token); } + /// This method removes a mobile token for push notifications. + /// + /// [token] - The mobile token to be removed. + /// Returns an http.Response. removeMobileToken(String token) async { return await _repository.removeMobileToken(token); } diff --git a/package/lib/notification/notification_repository.dart b/package/lib/notification/notification_repository.dart index f4e3fe7..21a6989 100644 --- a/package/lib/notification/notification_repository.dart +++ b/package/lib/notification/notification_repository.dart @@ -9,9 +9,10 @@ import '../data/dito_api.dart'; import '../user/user_interface.dart'; class NotificationRepository { - final DitoApi _ditoApi = DitoApi(); + final DitoApi _api = DitoApi(); final UserInterface _userInterface = UserInterface(); + /// This method initializes FirebaseMessaging Future initializeFirebaseMessaging( Function(RemoteMessage) onMessage) async { await Firebase.initializeApp(); @@ -31,28 +32,44 @@ class NotificationRepository { FirebaseMessaging.onMessageOpenedApp.listen(onMessage); -// alterar só para iOS? https://firebase.flutter.dev/docs/messaging/notifications/#ios-configuration - await FirebaseMessaging.instance - .setForegroundNotificationPresentationOptions( - badge: true, sound: true, alert: true); + if (Platform.isIOS) { + await FirebaseMessaging.instance + .setForegroundNotificationPresentationOptions( + badge: true, sound: true, alert: true); + } + /// Shows the message when FCM payload is received FirebaseMessaging.onMessage.listen(onMessage); } + /// This method asks for permission to show the notifications. + /// + /// Returns a bool. Future checkPermissions() async { var settings = await FirebaseMessaging.instance.requestPermission(); return settings.authorizationStatus == AuthorizationStatus.authorized; } + /// This method get the mobile token for push notifications. + /// + /// Returns a String or null. Future getFirebaseToken() async { return FirebaseMessaging.instance.getToken(); } + /// This method registers a mobile token for push notifications. + /// + /// [token] - The mobile token to be registered. + /// Returns an http.Response. Future registryMobileToken(String token) async { - return await _ditoApi.registryMobileToken(token, _userInterface.data); + return await _api.registryMobileToken(token, _userInterface.data); } + /// This method removes a mobile token for push notifications. + /// + /// [token] - The mobile token to be removed. + /// Returns an http.Response. Future removeMobileToken(String token) async { - return await _ditoApi.removeMobileToken(token, _userInterface.data); + return await _api.removeMobileToken(token, _userInterface.data); } } From 770b286377b8cb9ae523b78ed4417d2c2231b363 Mon Sep 17 00:00:00 2001 From: Ana Machado Date: Wed, 12 Jun 2024 10:44:27 -0300 Subject: [PATCH 13/36] [NOT-3026] mobileToken > token --- example/lib/app_form.dart | 4 ++-- package/lib/data/dito_api.dart | 5 ++--- package/lib/dito_sdk.dart | 10 +++++----- .../lib/notification/notification_interface.dart | 8 ++++---- .../lib/notification/notification_repository.dart | 13 +++++-------- package/test/dito_sdk_test.dart | 2 +- 6 files changed, 19 insertions(+), 23 deletions(-) diff --git a/example/lib/app_form.dart b/example/lib/app_form.dart index 4bb0318..806634c 100644 --- a/example/lib/app_form.dart +++ b/example/lib/app_form.dart @@ -28,10 +28,10 @@ class AppFormState extends State { await dito.user.identify(user); - final token = await dito.getMobileToken(); + final token = await dito.getToken(); if (token != null && token.isNotEmpty) { - dito.registryMobileToken(token: token); + dito.registryToken(token: token); } } diff --git a/package/lib/data/dito_api.dart b/package/lib/data/dito_api.dart index 7f9ecbb..817b475 100644 --- a/package/lib/data/dito_api.dart +++ b/package/lib/data/dito_api.dart @@ -100,8 +100,7 @@ class DitoApi { return await _post(url, path, body: body); } - Future registryMobileToken( - String token, UserEntity user) async { + Future registryToken(String token, UserEntity user) async { if (user.isNotValid) { throw Exception( 'User registration is required. Please call the identify() method first.'); @@ -121,7 +120,7 @@ class DitoApi { return await _post(url, path, queryParameters: queryParameters); } - Future removeMobileToken(String token, UserEntity user) async { + Future removeToken(String token, UserEntity user) async { if (user.isNotValid) { throw Exception( 'User registration is required. Please call the identify() method first.'); diff --git a/package/lib/dito_sdk.dart b/package/lib/dito_sdk.dart index c07565b..004c60f 100644 --- a/package/lib/dito_sdk.dart +++ b/package/lib/dito_sdk.dart @@ -74,7 +74,7 @@ class DitoSDK { /// This method get the mobile token for push notifications. /// /// Returns a String or null. - Future getMobileToken() async { + Future getToken() async { return _notificationInterface.getDeviceFirebaseToken(); } @@ -82,16 +82,16 @@ class DitoSDK { /// /// [token] - The mobile token to be registered. /// Returns an http.Response. - Future registryMobileToken({required String token}) async { - return await _notificationInterface.registryMobileToken(token); + Future registryToken({required String token}) async { + return await _notificationInterface.registryToken(token); } /// This method removes a mobile token from the push notification service. /// /// [token] - The mobile token to be removed. /// Returns an http.Response. - Future removeMobileToken({required String token}) async { - return await _notificationInterface.removeMobileToken(token); + Future removeToken({required String token}) async { + return await _notificationInterface.removeToken(token); } /// This method is a handler for manage messages in the background. diff --git a/package/lib/notification/notification_interface.dart b/package/lib/notification/notification_interface.dart index 40468d3..bece53a 100644 --- a/package/lib/notification/notification_interface.dart +++ b/package/lib/notification/notification_interface.dart @@ -112,15 +112,15 @@ class NotificationInterface { /// /// [token] - The mobile token to be registered. /// Returns an http.Response. - registryMobileToken(String token) async { - return await _repository.registryMobileToken(token); + registryToken(String token) async { + return await _repository.registryToken(token); } /// This method removes a mobile token for push notifications. /// /// [token] - The mobile token to be removed. /// Returns an http.Response. - removeMobileToken(String token) async { - return await _repository.removeMobileToken(token); + removeToken(String token) async { + return await _repository.removeToken(token); } } diff --git a/package/lib/notification/notification_repository.dart b/package/lib/notification/notification_repository.dart index 21a6989..68942da 100644 --- a/package/lib/notification/notification_repository.dart +++ b/package/lib/notification/notification_repository.dart @@ -17,10 +17,7 @@ class NotificationRepository { Function(RemoteMessage) onMessage) async { await Firebase.initializeApp(); -// por que só para android? - if (Platform.isAndroid) { - await FirebaseMessaging.instance.setAutoInitEnabled(true); - } + await FirebaseMessaging.instance.setAutoInitEnabled(true); // parece não ter utilidade https://firebase.flutter.dev/docs/messaging/notifications/#handling-interaction RemoteMessage? initialMessage = @@ -61,15 +58,15 @@ class NotificationRepository { /// /// [token] - The mobile token to be registered. /// Returns an http.Response. - Future registryMobileToken(String token) async { - return await _api.registryMobileToken(token, _userInterface.data); + Future registryToken(String token) async { + return await _api.registryToken(token, _userInterface.data); } /// This method removes a mobile token for push notifications. /// /// [token] - The mobile token to be removed. /// Returns an http.Response. - Future removeMobileToken(String token) async { - return await _api.removeMobileToken(token, _userInterface.data); + Future removeToken(String token) async { + return await _api.removeToken(token, _userInterface.data); } } diff --git a/package/test/dito_sdk_test.dart b/package/test/dito_sdk_test.dart index 41d660f..ffbcc40 100644 --- a/package/test/dito_sdk_test.dart +++ b/package/test/dito_sdk_test.dart @@ -17,7 +17,7 @@ void main() { test('Send mobile token', () async { await dito.identify(UserEntity(userID: id)); - final response = await dito.registryMobileToken( + final response = await dito.registryToken( token: "eXb4Y_piSZS2RKv7WeqjW0:APA91bHJUQ6kL8ZrevvO8zAgYIEdtCWSa7RkmszRFdYz32jYblJvOkIiDcpDdqVqZvOm8CSiEHTzljHajvMO66FFxiqteB6od2sMe01UIOwvKrpUOFXz-L4Slif9jSY9pUaMxyqCtoxR"); From ced95995949b55bb9f161e12827891e9d2348436 Mon Sep 17 00:00:00 2001 From: Ana Machado Date: Wed, 12 Jun 2024 13:35:52 -0300 Subject: [PATCH 14/36] [NOT-3026] NotificationConstroller singleton --- .../lib/notification/notification_controller.dart | 14 +++++++++++--- .../lib/notification/notification_interface.dart | 6 ++---- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/package/lib/notification/notification_controller.dart b/package/lib/notification/notification_controller.dart index 47619a9..aafcea7 100644 --- a/package/lib/notification/notification_controller.dart +++ b/package/lib/notification/notification_controller.dart @@ -5,10 +5,17 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'notification_entity.dart'; class NotificationController { - final Function(DataPayload) onSelectNotification; + late Function(DataPayload) onSelectNotification; late FlutterLocalNotificationsPlugin localNotificationsPlugin; - NotificationController({required this.onSelectNotification}); + static final NotificationController _instance = + NotificationController._internal(); + + factory NotificationController() { + return _instance; + } + + NotificationController._internal(); AndroidNotificationDetails androidDetails = const AndroidNotificationDetails( 'dito_notifications', @@ -26,7 +33,8 @@ class NotificationController { presentBanner: true); /// This method initializes localNotificationsPlugin - initialize() async { + initialize(Function(DataPayload) onSelectNotification) async { + _instance.onSelectNotification = onSelectNotification; localNotificationsPlugin = FlutterLocalNotificationsPlugin(); const android = AndroidInitializationSettings('@mipmap/ic_launcher'); const ios = DarwinInitializationSettings(); diff --git a/package/lib/notification/notification_interface.dart b/package/lib/notification/notification_interface.dart index bece53a..66840a7 100644 --- a/package/lib/notification/notification_interface.dart +++ b/package/lib/notification/notification_interface.dart @@ -13,7 +13,7 @@ import 'notification_controller.dart'; /// NotificationInterface is an interface for communication with the notification repository and notification controller class NotificationInterface { final NotificationRepository _repository = NotificationRepository(); - late NotificationController _controller; + final NotificationController _controller = NotificationController(); final DitoApi _api = DitoApi(); /// The broadcast stream for received notifications @@ -28,10 +28,8 @@ class NotificationInterface { /// This method initializes notification controller and notification repository. /// Start listening to notifications Future initialize() async { - _controller = - NotificationController(onSelectNotification: onSelectNotification); await _repository.initializeFirebaseMessaging(onMessage); - await _controller.initialize(); + await _controller.initialize(onSelectNotification); _listenStream(); } From 393e8bc89c8b948bf775aee6d638d02f4f6db784 Mon Sep 17 00:00:00 2001 From: Ana Machado Date: Thu, 13 Jun 2024 08:04:59 -0300 Subject: [PATCH 15/36] [NOT-3026] fix --- package/lib/data/dito_api.dart | 2 +- package/lib/notification/notification_repository.dart | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/package/lib/data/dito_api.dart b/package/lib/data/dito_api.dart index 817b475..f2327db 100644 --- a/package/lib/data/dito_api.dart +++ b/package/lib/data/dito_api.dart @@ -97,7 +97,7 @@ class DitoApi { const url = 'notification.plataformasocial.com.br'; final path = 'notifications/$notificationId/open'; - return await _post(url, path, body: body); + return await _post(url, path, queryParameters: queryParameters, body: body); } Future registryToken(String token, UserEntity user) async { diff --git a/package/lib/notification/notification_repository.dart b/package/lib/notification/notification_repository.dart index 68942da..4da494e 100644 --- a/package/lib/notification/notification_repository.dart +++ b/package/lib/notification/notification_repository.dart @@ -19,7 +19,7 @@ class NotificationRepository { await FirebaseMessaging.instance.setAutoInitEnabled(true); -// parece não ter utilidade https://firebase.flutter.dev/docs/messaging/notifications/#handling-interaction + /* RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage(); @@ -28,6 +28,7 @@ class NotificationRepository { } FirebaseMessaging.onMessageOpenedApp.listen(onMessage); + */ if (Platform.isIOS) { await FirebaseMessaging.instance From 1f40f0064cfa7f63dbd0855f6f581e4b66964037 Mon Sep 17 00:00:00 2001 From: Ana Machado Date: Thu, 13 Jun 2024 11:31:53 -0300 Subject: [PATCH 16/36] [NOT-3027] onTokenRefresh --- example/lib/app_form.dart | 15 ++++++----- example/pubspec.lock | 2 +- package/lib/dito_sdk.dart | 23 ---------------- .../notification/notification_interface.dart | 23 ---------------- .../notification/notification_repository.dart | 27 ++++++++++++++++--- package/lib/user/user_entity.dart | 2 ++ package/test/dito_sdk_test.dart | 10 ------- 7 files changed, 36 insertions(+), 66 deletions(-) diff --git a/example/lib/app_form.dart b/example/lib/app_form.dart index 806634c..7d992ff 100644 --- a/example/lib/app_form.dart +++ b/example/lib/app_form.dart @@ -1,5 +1,6 @@ import 'package:dito_sdk/dito_sdk.dart'; import 'package:dito_sdk/user/user_entity.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -27,12 +28,6 @@ class AppFormState extends State { userID: cpf, cpf: cpf, name: 'Teste SDK Flutter', email: email); await dito.user.identify(user); - - final token = await dito.getToken(); - - if (token != null && token.isNotEmpty) { - dito.registryToken(token: token); - } } handleIdentify() async { @@ -56,6 +51,10 @@ class AppFormState extends State { } } + handleDeleteToken() async { + await FirebaseMessaging.instance.deleteToken(); + } + return Form( key: _formKey, child: Column( @@ -97,6 +96,10 @@ class AppFormState extends State { onPressed: handleNotification, child: const Text('Receber Notification'), ), + OutlinedButton( + onPressed: handleDeleteToken, + child: const Text('Deletar token'), + ), ]))) ], ), diff --git a/example/pubspec.lock b/example/pubspec.lock index 02609dc..eaca55e 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -103,7 +103,7 @@ packages: path: "../package" relative: true source: path - version: "0.5.3" + version: "0.5.4" fake_async: dependency: transitive description: diff --git a/package/lib/dito_sdk.dart b/package/lib/dito_sdk.dart index 004c60f..b54b139 100644 --- a/package/lib/dito_sdk.dart +++ b/package/lib/dito_sdk.dart @@ -71,29 +71,6 @@ class DitoSDK { return await _eventInterface.trackEvent(event); } - /// This method get the mobile token for push notifications. - /// - /// Returns a String or null. - Future getToken() async { - return _notificationInterface.getDeviceFirebaseToken(); - } - - /// This method registers a mobile token for push notifications. - /// - /// [token] - The mobile token to be registered. - /// Returns an http.Response. - Future registryToken({required String token}) async { - return await _notificationInterface.registryToken(token); - } - - /// This method removes a mobile token from the push notification service. - /// - /// [token] - The mobile token to be removed. - /// Returns an http.Response. - Future removeToken({required String token}) async { - return await _notificationInterface.removeToken(token); - } - /// This method is a handler for manage messages in the background. /// It initializes Firebase and Dito, then push the message. Future onBackgroundMessageHandler(RemoteMessage message, diff --git a/package/lib/notification/notification_interface.dart b/package/lib/notification/notification_interface.dart index 66840a7..42e081c 100644 --- a/package/lib/notification/notification_interface.dart +++ b/package/lib/notification/notification_interface.dart @@ -98,27 +98,4 @@ class NotificationInterface { _selectNotificationStream.add(data); } } - - /// This method get the mobile token for push notifications. - /// - /// Returns a String or null. - Future getDeviceFirebaseToken() async { - return _repository.getFirebaseToken(); - } - - /// This method registers a mobile token for push notifications. - /// - /// [token] - The mobile token to be registered. - /// Returns an http.Response. - registryToken(String token) async { - return await _repository.registryToken(token); - } - - /// This method removes a mobile token for push notifications. - /// - /// [token] - The mobile token to be removed. - /// Returns an http.Response. - removeToken(String token) async { - return await _repository.removeToken(token); - } } diff --git a/package/lib/notification/notification_repository.dart b/package/lib/notification/notification_repository.dart index 4da494e..fd8b8b8 100644 --- a/package/lib/notification/notification_repository.dart +++ b/package/lib/notification/notification_repository.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter/foundation.dart'; import 'package:http/http.dart' as http; import '../data/dito_api.dart'; @@ -19,6 +20,8 @@ class NotificationRepository { await FirebaseMessaging.instance.setAutoInitEnabled(true); + _handleToken(); + /* RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage(); @@ -40,6 +43,24 @@ class NotificationRepository { FirebaseMessaging.onMessage.listen(onMessage); } + _handleToken() async { + _userInterface.data.token = await _getFirebaseToken(); + FirebaseMessaging.instance.onTokenRefresh.listen((token) { + final lastToken = _userInterface.data.token; + if (lastToken != token) { + if (lastToken != null && lastToken.isNotEmpty) { + _removeToken(lastToken); + } + _registryToken(token); + _userInterface.data.token = token; + } + }).onError((err) { + if (kDebugMode) { + print('Error getting token.: $err'); + } + }); + } + /// This method asks for permission to show the notifications. /// /// Returns a bool. @@ -51,7 +72,7 @@ class NotificationRepository { /// This method get the mobile token for push notifications. /// /// Returns a String or null. - Future getFirebaseToken() async { + Future _getFirebaseToken() async { return FirebaseMessaging.instance.getToken(); } @@ -59,7 +80,7 @@ class NotificationRepository { /// /// [token] - The mobile token to be registered. /// Returns an http.Response. - Future registryToken(String token) async { + Future _registryToken(String token) async { return await _api.registryToken(token, _userInterface.data); } @@ -67,7 +88,7 @@ class NotificationRepository { /// /// [token] - The mobile token to be removed. /// Returns an http.Response. - Future removeToken(String token) async { + Future _removeToken(String token) async { return await _api.removeToken(token, _userInterface.data); } } diff --git a/package/lib/user/user_entity.dart b/package/lib/user/user_entity.dart index 255bad2..4726a59 100644 --- a/package/lib/user/user_entity.dart +++ b/package/lib/user/user_entity.dart @@ -8,6 +8,7 @@ class UserEntity { String? gender; String? birthday; String? location; + String? token; Map? customData; UserEntity( @@ -18,6 +19,7 @@ class UserEntity { this.gender, this.birthday, this.location, + this.token, this.customData}); String? get id => userID; diff --git a/package/test/dito_sdk_test.dart b/package/test/dito_sdk_test.dart index ffbcc40..f61662c 100644 --- a/package/test/dito_sdk_test.dart +++ b/package/test/dito_sdk_test.dart @@ -13,15 +13,5 @@ void main() { dynamic env = await testEnv(); dito.initialize(apiKey: env["apiKey"], secretKey: env["secret"]); }); - - test('Send mobile token', () async { - await dito.identify(UserEntity(userID: id)); - - final response = await dito.registryToken( - token: - "eXb4Y_piSZS2RKv7WeqjW0:APA91bHJUQ6kL8ZrevvO8zAgYIEdtCWSa7RkmszRFdYz32jYblJvOkIiDcpDdqVqZvOm8CSiEHTzljHajvMO66FFxiqteB6od2sMe01UIOwvKrpUOFXz-L4Slif9jSY9pUaMxyqCtoxR"); - - expect(response.statusCode, 200); - }); }); } From c70244a13c844ed0c8100c912e723de91f454797 Mon Sep 17 00:00:00 2001 From: Ana Machado Date: Thu, 13 Jun 2024 14:14:56 -0300 Subject: [PATCH 17/36] [NOT-3027] export removeToken --- package/lib/dito_sdk.dart | 8 ++++++++ package/lib/notification/notification_interface.dart | 3 +++ package/lib/notification/notification_repository.dart | 10 ++++------ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/package/lib/dito_sdk.dart b/package/lib/dito_sdk.dart index b54b139..ad203b9 100644 --- a/package/lib/dito_sdk.dart +++ b/package/lib/dito_sdk.dart @@ -71,6 +71,14 @@ class DitoSDK { return await _eventInterface.trackEvent(event); } + /// This method removes a mobile token for push notifications. + /// + /// [token] - The mobile token to be removed. + /// Returns an http.Response. + removeToken(String token) async { + return await _notificationInterface.removeToken(token); + } + /// This method is a handler for manage messages in the background. /// It initializes Firebase and Dito, then push the message. Future onBackgroundMessageHandler(RemoteMessage message, diff --git a/package/lib/notification/notification_interface.dart b/package/lib/notification/notification_interface.dart index 42e081c..293b354 100644 --- a/package/lib/notification/notification_interface.dart +++ b/package/lib/notification/notification_interface.dart @@ -98,4 +98,7 @@ class NotificationInterface { _selectNotificationStream.add(data); } } + + /// This method returns the repository method that removes the token. + removeToken(String token) => _repository.removeToken(token); } diff --git a/package/lib/notification/notification_repository.dart b/package/lib/notification/notification_repository.dart index fd8b8b8..b3f2b5d 100644 --- a/package/lib/notification/notification_repository.dart +++ b/package/lib/notification/notification_repository.dart @@ -43,13 +43,13 @@ class NotificationRepository { FirebaseMessaging.onMessage.listen(onMessage); } - _handleToken() async { + void _handleToken() async { _userInterface.data.token = await _getFirebaseToken(); FirebaseMessaging.instance.onTokenRefresh.listen((token) { final lastToken = _userInterface.data.token; if (lastToken != token) { if (lastToken != null && lastToken.isNotEmpty) { - _removeToken(lastToken); + removeToken(lastToken); } _registryToken(token); _userInterface.data.token = token; @@ -72,9 +72,7 @@ class NotificationRepository { /// This method get the mobile token for push notifications. /// /// Returns a String or null. - Future _getFirebaseToken() async { - return FirebaseMessaging.instance.getToken(); - } + Future _getFirebaseToken() => FirebaseMessaging.instance.getToken(); /// This method registers a mobile token for push notifications. /// @@ -88,7 +86,7 @@ class NotificationRepository { /// /// [token] - The mobile token to be removed. /// Returns an http.Response. - Future _removeToken(String token) async { + Future removeToken(String token) async { return await _api.removeToken(token, _userInterface.data); } } From e8bac57002a84c6c4ab87e034ccad0b38cd9ec4c Mon Sep 17 00:00:00 2001 From: Ana Machado Date: Thu, 13 Jun 2024 14:18:48 -0300 Subject: [PATCH 18/36] [NOT-3027] removeToken --- package/lib/dito_sdk.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package/lib/dito_sdk.dart b/package/lib/dito_sdk.dart index ad203b9..f77f3a5 100644 --- a/package/lib/dito_sdk.dart +++ b/package/lib/dito_sdk.dart @@ -71,11 +71,11 @@ class DitoSDK { return await _eventInterface.trackEvent(event); } - /// This method removes a mobile token for push notifications. + /// This method removes a mobile token from the push notification service. /// /// [token] - The mobile token to be removed. /// Returns an http.Response. - removeToken(String token) async { + Future removeToken({required String token}) async { return await _notificationInterface.removeToken(token); } From f150dcda8665baab03deb4dc11533497783b5a70 Mon Sep 17 00:00:00 2001 From: Ana Machado Date: Tue, 18 Jun 2024 11:03:57 -0300 Subject: [PATCH 19/36] [NOT-3027] exportando metodos de adicionar/remover tokens com o param opcional --- package/lib/dito_sdk.dart | 10 ++++++++- .../notification/notification_interface.dart | 21 +++++++++++++++++-- .../notification/notification_repository.dart | 8 +++---- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/package/lib/dito_sdk.dart b/package/lib/dito_sdk.dart index f77f3a5..5337e23 100644 --- a/package/lib/dito_sdk.dart +++ b/package/lib/dito_sdk.dart @@ -71,11 +71,19 @@ class DitoSDK { return await _eventInterface.trackEvent(event); } + /// This method registers a mobile token for push notifications. + /// + /// [token] - The mobile token to be registered. + /// Returns an http.Response. + Future registryToken({String? token}) async { + return await _notificationInterface.registryToken(token); + } + /// This method removes a mobile token from the push notification service. /// /// [token] - The mobile token to be removed. /// Returns an http.Response. - Future removeToken({required String token}) async { + Future removeToken({String? token}) async { return await _notificationInterface.removeToken(token); } diff --git a/package/lib/notification/notification_interface.dart b/package/lib/notification/notification_interface.dart index 293b354..848a09d 100644 --- a/package/lib/notification/notification_interface.dart +++ b/package/lib/notification/notification_interface.dart @@ -99,6 +99,23 @@ class NotificationInterface { } } - /// This method returns the repository method that removes the token. - removeToken(String token) => _repository.removeToken(token); + /// This method registers a mobile token for push notifications. + /// + /// [token] - The mobile token to be registered. + /// Returns an http.Response. + registryToken(String? token) async { + String? newToken = token; + newToken ??= await _repository.getFirebaseToken(); + if (newToken != null) _repository.registryToken(newToken); + } + + /// This method removes a mobile token for push notifications. + /// + /// [token] - The mobile token to be removed. + /// Returns an http.Response. + removeToken(String? token) async { + String? newToken = token; + newToken ??= await _repository.getFirebaseToken(); + if (newToken != null) _repository.removeToken(newToken); + } } diff --git a/package/lib/notification/notification_repository.dart b/package/lib/notification/notification_repository.dart index b3f2b5d..ce26365 100644 --- a/package/lib/notification/notification_repository.dart +++ b/package/lib/notification/notification_repository.dart @@ -44,14 +44,14 @@ class NotificationRepository { } void _handleToken() async { - _userInterface.data.token = await _getFirebaseToken(); + _userInterface.data.token = await getFirebaseToken(); FirebaseMessaging.instance.onTokenRefresh.listen((token) { final lastToken = _userInterface.data.token; if (lastToken != token) { if (lastToken != null && lastToken.isNotEmpty) { removeToken(lastToken); } - _registryToken(token); + registryToken(token); _userInterface.data.token = token; } }).onError((err) { @@ -72,13 +72,13 @@ class NotificationRepository { /// This method get the mobile token for push notifications. /// /// Returns a String or null. - Future _getFirebaseToken() => FirebaseMessaging.instance.getToken(); + Future getFirebaseToken() => FirebaseMessaging.instance.getToken(); /// This method registers a mobile token for push notifications. /// /// [token] - The mobile token to be registered. /// Returns an http.Response. - Future _registryToken(String token) async { + Future registryToken(String token) async { return await _api.registryToken(token, _userInterface.data); } From 00b939f7e3f9acb70a35455005ebf015b0f07c76 Mon Sep 17 00:00:00 2001 From: Ana Machado Date: Tue, 18 Jun 2024 08:24:36 -0300 Subject: [PATCH 20/36] [NOT-3059] onMessageClicked --- example/lib/main.dart | 4 +++- package/lib/dito_sdk.dart | 12 ++++++++---- .../notification/notification_controller.dart | 1 - .../notification/notification_interface.dart | 19 +++++++------------ .../notification/notification_repository.dart | 11 ----------- 5 files changed, 18 insertions(+), 29 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index c8837fd..ab71a1a 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -20,7 +20,9 @@ void main() async { DitoSDK dito = DitoSDK(); dito.initialize( apiKey: Constants.ditoApiKey, secretKey: Constants.ditoSecretKey); - await dito.initializePushNotificationService(); + await dito.initializePushNotificationService(onMessageClicked: (data) { + print(data.toJson()); + }); runApp(MultiProvider(providers: [ Provider( diff --git a/package/lib/dito_sdk.dart b/package/lib/dito_sdk.dart index 5337e23..e293ad7 100644 --- a/package/lib/dito_sdk.dart +++ b/package/lib/dito_sdk.dart @@ -1,5 +1,6 @@ library dito_sdk; +import 'package:dito_sdk/notification/notification_entity.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:http/http.dart' as http; @@ -41,8 +42,9 @@ class DitoSDK { } /// This method initializes the push notification service using Firebase. - Future initializePushNotificationService() async { - await _notificationInterface.initialize(); + Future initializePushNotificationService( + {Function(DataPayload)? onMessageClicked}) async { + await _notificationInterface.initialize(onMessageClicked); } /// This method enables saving and sending user data to the Dito API. @@ -90,10 +92,12 @@ class DitoSDK { /// This method is a handler for manage messages in the background. /// It initializes Firebase and Dito, then push the message. Future onBackgroundMessageHandler(RemoteMessage message, - {required String apiKey, required String secretKey}) async { + {required String apiKey, + required String secretKey, + Function(DataPayload)? onMessageClicked}) async { _api.setKeys(apiKey, secretKey); await Firebase.initializeApp(); - await _notificationInterface.initialize(); + await _notificationInterface.initialize(onMessageClicked); return await _notificationInterface.onMessage(message); } } diff --git a/package/lib/notification/notification_controller.dart b/package/lib/notification/notification_controller.dart index aafcea7..36a3d37 100644 --- a/package/lib/notification/notification_controller.dart +++ b/package/lib/notification/notification_controller.dart @@ -45,7 +45,6 @@ class NotificationController { await localNotificationsPlugin.initialize(initializationSettings, onDidReceiveNotificationResponse: onTapNotification); -// Precisa completar a config no app? https://firebase.flutter.dev/docs/messaging/notifications/#notification-channels await _setupAndroidChannel(); } diff --git a/package/lib/notification/notification_interface.dart b/package/lib/notification/notification_interface.dart index 848a09d..507a0a0 100644 --- a/package/lib/notification/notification_interface.dart +++ b/package/lib/notification/notification_interface.dart @@ -27,10 +27,10 @@ class NotificationInterface { /// This method initializes notification controller and notification repository. /// Start listening to notifications - Future initialize() async { + Future initialize(Function(DataPayload)? onMessageClicked) async { await _repository.initializeFirebaseMessaging(onMessage); await _controller.initialize(onSelectNotification); - _listenStream(); + _listenStream(onMessageClicked); } // This method turns off the streams when this class is unmounted @@ -40,12 +40,14 @@ class NotificationInterface { } // This method initializes the listeners on streams - _listenStream() { + _listenStream(Function(DataPayload)? onMessageClicked) { _didReceiveLocalNotificationStream.stream .listen((NotificationEntity receivedNotification) { _controller.showNotification(receivedNotification); }); _selectNotificationStream.stream.listen((DataPayload data) async { + if (onMessageClicked != null) onMessageClicked(data); + final notificationId = data.notification; // Only sends the event if the message is linked to a notification @@ -56,13 +58,6 @@ class NotificationInterface { }); } - /// This method adds a received notification to stream - /// - /// [notification] - NotificationEntity object. - _addNotificationToStream(NotificationEntity notification) { - _didReceiveLocalNotificationStream.add(notification); - } - /// This method is a handler for new remote messages. /// Check permissions and add notification to the stream. /// @@ -82,11 +77,11 @@ class NotificationInterface { final appName = packageInfo.appName; if (messagingAllowed && notification.details.message.isNotEmpty) { - _addNotificationToStream(NotificationEntity( + _didReceiveLocalNotificationStream.add((NotificationEntity( id: message.hashCode, title: notification.details.title ?? appName, body: notification.details.message, - payload: notification)); + payload: notification))); } } diff --git a/package/lib/notification/notification_repository.dart b/package/lib/notification/notification_repository.dart index ce26365..65f82e2 100644 --- a/package/lib/notification/notification_repository.dart +++ b/package/lib/notification/notification_repository.dart @@ -22,17 +22,6 @@ class NotificationRepository { _handleToken(); - /* - RemoteMessage? initialMessage = - await FirebaseMessaging.instance.getInitialMessage(); - - if (initialMessage != null) { - onMessage(initialMessage); - } - - FirebaseMessaging.onMessageOpenedApp.listen(onMessage); - */ - if (Platform.isIOS) { await FirebaseMessaging.instance .setForegroundNotificationPresentationOptions( From 6b3d8a97fee19969ed4615ecfc0d96f3874882f9 Mon Sep 17 00:00:00 2001 From: Ana Machado Date: Wed, 19 Jun 2024 08:08:49 -0300 Subject: [PATCH 21/36] [NOT-3059] EventBus --- example/lib/app_form.dart | 5 ++--- example/lib/main.dart | 3 ++- example/pubspec.lock | 8 ++++++++ package/lib/dito_sdk.dart | 15 +++++++++++---- .../lib/notification/notification_interface.dart | 16 ++++++++++++---- package/pubspec.lock | 8 ++++++++ package/pubspec.yaml | 1 + pubspec.lock | 8 ++++++++ pubspec.yaml | 2 ++ 9 files changed, 54 insertions(+), 12 deletions(-) diff --git a/example/lib/app_form.dart b/example/lib/app_form.dart index 7d992ff..6980259 100644 --- a/example/lib/app_form.dart +++ b/example/lib/app_form.dart @@ -20,8 +20,8 @@ class AppFormState extends State { Widget build(BuildContext context) { final dito = Provider.of(context); - String cpf = "32190381209"; - String email = "teste.sdk2@dito.com.br"; + String cpf = "32190381210"; + String email = "teste.sdk3@dito.com.br"; identify() async { final user = UserEntity( @@ -42,7 +42,6 @@ class AppFormState extends State { handleNotification() async { if (_formKey.currentState!.validate()) { - await identify(); await dito.trackEvent(eventName: 'action-test'); ScaffoldMessenger.of(context).showSnackBar( diff --git a/example/lib/main.dart b/example/lib/main.dart index ab71a1a..3849820 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -20,7 +20,8 @@ void main() async { DitoSDK dito = DitoSDK(); dito.initialize( apiKey: Constants.ditoApiKey, secretKey: Constants.ditoSecretKey); - await dito.initializePushNotificationService(onMessageClicked: (data) { + await dito.initializePushNotificationService(); + dito.setOnMessageClick((data) { print(data.toJson()); }); diff --git a/example/pubspec.lock b/example/pubspec.lock index eaca55e..ba15da1 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -104,6 +104,14 @@ packages: relative: true source: path version: "0.5.4" + event_bus: + dependency: transitive + description: + name: event_bus + sha256: "44baa799834f4c803921873e7446a2add0f3efa45e101a054b1f0ab9b95f8edc" + url: "https://pub.dev" + source: hosted + version: "2.0.0" fake_async: dependency: transitive description: diff --git a/package/lib/dito_sdk.dart b/package/lib/dito_sdk.dart index e293ad7..036dc63 100644 --- a/package/lib/dito_sdk.dart +++ b/package/lib/dito_sdk.dart @@ -4,6 +4,7 @@ import 'package:dito_sdk/notification/notification_entity.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:http/http.dart' as http; +import 'package:event_bus/event_bus.dart'; import 'data/dito_api.dart'; import 'event/event_entity.dart'; @@ -19,6 +20,7 @@ class DitoSDK { final UserInterface _userInterface = UserInterface(); final EventInterface _eventInterface = EventInterface(); final NotificationInterface _notificationInterface = NotificationInterface(); + EventBus eventBus = EventBus(); static final DitoSDK _instance = DitoSDK._internal(); @@ -42,9 +44,14 @@ class DitoSDK { } /// This method initializes the push notification service using Firebase. - Future initializePushNotificationService( - {Function(DataPayload)? onMessageClicked}) async { - await _notificationInterface.initialize(onMessageClicked); + Future initializePushNotificationService() async { + await _notificationInterface.initialize(); + } + + setOnMessageClick(Function(DataPayload) onMessageClicked) { + eventBus.on().listen((event) { + onMessageClicked(event.data); + }); } /// This method enables saving and sending user data to the Dito API. @@ -97,7 +104,7 @@ class DitoSDK { Function(DataPayload)? onMessageClicked}) async { _api.setKeys(apiKey, secretKey); await Firebase.initializeApp(); - await _notificationInterface.initialize(onMessageClicked); + await _notificationInterface.initialize(); return await _notificationInterface.onMessage(message); } } diff --git a/package/lib/notification/notification_interface.dart b/package/lib/notification/notification_interface.dart index 507a0a0..8afac1a 100644 --- a/package/lib/notification/notification_interface.dart +++ b/package/lib/notification/notification_interface.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:convert'; +import 'package:event_bus/event_bus.dart'; import 'package:flutter/foundation.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:package_info_plus/package_info_plus.dart'; @@ -10,11 +11,18 @@ import 'notification_entity.dart'; import 'notification_repository.dart'; import 'notification_controller.dart'; +class NotificationClickedEvent { + DataPayload data; + + NotificationClickedEvent(this.data); +} + /// NotificationInterface is an interface for communication with the notification repository and notification controller class NotificationInterface { final NotificationRepository _repository = NotificationRepository(); final NotificationController _controller = NotificationController(); final DitoApi _api = DitoApi(); + EventBus eventBus = EventBus(); /// The broadcast stream for received notifications final StreamController @@ -27,10 +35,10 @@ class NotificationInterface { /// This method initializes notification controller and notification repository. /// Start listening to notifications - Future initialize(Function(DataPayload)? onMessageClicked) async { + Future initialize() async { await _repository.initializeFirebaseMessaging(onMessage); await _controller.initialize(onSelectNotification); - _listenStream(onMessageClicked); + _listenStream(); } // This method turns off the streams when this class is unmounted @@ -40,13 +48,13 @@ class NotificationInterface { } // This method initializes the listeners on streams - _listenStream(Function(DataPayload)? onMessageClicked) { + _listenStream() { _didReceiveLocalNotificationStream.stream .listen((NotificationEntity receivedNotification) { _controller.showNotification(receivedNotification); }); _selectNotificationStream.stream.listen((DataPayload data) async { - if (onMessageClicked != null) onMessageClicked(data); + eventBus.fire(NotificationClickedEvent(data)); final notificationId = data.notification; diff --git a/package/pubspec.lock b/package/pubspec.lock index 6a36290..68a840b 100644 --- a/package/pubspec.lock +++ b/package/pubspec.lock @@ -89,6 +89,14 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.0" + event_bus: + dependency: "direct main" + description: + name: event_bus + sha256: "44baa799834f4c803921873e7446a2add0f3efa45e101a054b1f0ab9b95f8edc" + url: "https://pub.dev" + source: hosted + version: "2.0.0" fake_async: dependency: transitive description: diff --git a/package/pubspec.yaml b/package/pubspec.yaml index b179018..d3b801e 100644 --- a/package/pubspec.yaml +++ b/package/pubspec.yaml @@ -19,6 +19,7 @@ dependencies: firebase_core: ">=2.30.1" sqflite_common_ffi: ">=2.3.3" package_info_plus: ">=4.1.0" + event_bus: ^2.0.0 dev_dependencies: flutter_test: diff --git a/pubspec.lock b/pubspec.lock index 2048d15..f2c1697 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -73,6 +73,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.0+1" + event_bus: + dependency: "direct main" + description: + name: event_bus + sha256: "44baa799834f4c803921873e7446a2add0f3efa45e101a054b1f0ab9b95f8edc" + url: "https://pub.dev" + source: hosted + version: "2.0.0" file: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 436f1ce..ba234ae 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,3 +5,5 @@ environment: dev_dependencies: melos: ^6.0.0 +dependencies: + event_bus: ^2.0.0 From 22968882e7d62ac7f9c38aac8d98c4a90d1760d4 Mon Sep 17 00:00:00 2001 From: Ana Machado Date: Wed, 19 Jun 2024 08:11:02 -0300 Subject: [PATCH 22/36] =?UTF-8?q?[NOT-3059]=20Alterando=20dados=20do=20usu?= =?UTF-8?q?=C3=A1rio=20de=20teste?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/lib/app_form.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example/lib/app_form.dart b/example/lib/app_form.dart index 6980259..e265a03 100644 --- a/example/lib/app_form.dart +++ b/example/lib/app_form.dart @@ -20,8 +20,8 @@ class AppFormState extends State { Widget build(BuildContext context) { final dito = Provider.of(context); - String cpf = "32190381210"; - String email = "teste.sdk3@dito.com.br"; + String cpf = "32190381209"; + String email = "teste.sdk2@dito.com.br"; identify() async { final user = UserEntity( From 1395ec8480146dcbed1b08a805ab0e7736a417f9 Mon Sep 17 00:00:00 2001 From: Ana Machado Date: Wed, 19 Jun 2024 08:27:39 -0300 Subject: [PATCH 23/36] [NOT-3059] background state --- example/lib/main.dart | 5 +++++ package/lib/dito_sdk.dart | 4 +--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 3849820..589e961 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -11,6 +11,10 @@ Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { DitoSDK dito = DitoSDK(); dito.onBackgroundMessageHandler(message, apiKey: Constants.ditoApiKey, secretKey: Constants.ditoSecretKey); + dito.setOnMessageClick((data) { + print('app is in a terminated or background state'); + print(data.toJson()); + }); } void main() async { @@ -22,6 +26,7 @@ void main() async { apiKey: Constants.ditoApiKey, secretKey: Constants.ditoSecretKey); await dito.initializePushNotificationService(); dito.setOnMessageClick((data) { + print('app is open'); print(data.toJson()); }); diff --git a/package/lib/dito_sdk.dart b/package/lib/dito_sdk.dart index 036dc63..c937b7e 100644 --- a/package/lib/dito_sdk.dart +++ b/package/lib/dito_sdk.dart @@ -99,9 +99,7 @@ class DitoSDK { /// This method is a handler for manage messages in the background. /// It initializes Firebase and Dito, then push the message. Future onBackgroundMessageHandler(RemoteMessage message, - {required String apiKey, - required String secretKey, - Function(DataPayload)? onMessageClicked}) async { + {required String apiKey, required String secretKey}) async { _api.setKeys(apiKey, secretKey); await Firebase.initializeApp(); await _notificationInterface.initialize(); From dbefa73802a875ee7029715c3f575d8b9ed280a4 Mon Sep 17 00:00:00 2001 From: Ana Machado Date: Wed, 19 Jun 2024 10:29:22 -0300 Subject: [PATCH 24/36] [NOT-3059] notification events --- example/lib/main.dart | 5 ----- package/lib/dito_sdk.dart | 6 ++--- .../lib/notification/notification_events.dart | 22 +++++++++++++++++++ .../notification/notification_interface.dart | 11 +++------- pubspec.yaml | 2 +- 5 files changed, 29 insertions(+), 17 deletions(-) create mode 100644 package/lib/notification/notification_events.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index 589e961..3849820 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -11,10 +11,6 @@ Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { DitoSDK dito = DitoSDK(); dito.onBackgroundMessageHandler(message, apiKey: Constants.ditoApiKey, secretKey: Constants.ditoSecretKey); - dito.setOnMessageClick((data) { - print('app is in a terminated or background state'); - print(data.toJson()); - }); } void main() async { @@ -26,7 +22,6 @@ void main() async { apiKey: Constants.ditoApiKey, secretKey: Constants.ditoSecretKey); await dito.initializePushNotificationService(); dito.setOnMessageClick((data) { - print('app is open'); print(data.toJson()); }); diff --git a/package/lib/dito_sdk.dart b/package/lib/dito_sdk.dart index c937b7e..f82edb2 100644 --- a/package/lib/dito_sdk.dart +++ b/package/lib/dito_sdk.dart @@ -1,10 +1,10 @@ library dito_sdk; import 'package:dito_sdk/notification/notification_entity.dart'; +import 'package:dito_sdk/notification/notification_events.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:http/http.dart' as http; -import 'package:event_bus/event_bus.dart'; import 'data/dito_api.dart'; import 'event/event_entity.dart'; @@ -20,7 +20,7 @@ class DitoSDK { final UserInterface _userInterface = UserInterface(); final EventInterface _eventInterface = EventInterface(); final NotificationInterface _notificationInterface = NotificationInterface(); - EventBus eventBus = EventBus(); + final NotificationEvents _notificationEvents = NotificationEvents(); static final DitoSDK _instance = DitoSDK._internal(); @@ -49,7 +49,7 @@ class DitoSDK { } setOnMessageClick(Function(DataPayload) onMessageClicked) { - eventBus.on().listen((event) { + _notificationEvents.stream.on().listen((event) { onMessageClicked(event.data); }); } diff --git a/package/lib/notification/notification_events.dart b/package/lib/notification/notification_events.dart new file mode 100644 index 0000000..d985b0b --- /dev/null +++ b/package/lib/notification/notification_events.dart @@ -0,0 +1,22 @@ +import 'package:dito_sdk/notification/notification_entity.dart'; +import 'package:event_bus/event_bus.dart'; + +class MessageClickedEvent { + DataPayload data; + + MessageClickedEvent(this.data); +} + +class NotificationEvents { + EventBus eventBus = EventBus(); + + static final NotificationEvents _instance = NotificationEvents._internal(); + + factory NotificationEvents() { + return _instance; + } + + EventBus get stream => eventBus; + + NotificationEvents._internal(); +} diff --git a/package/lib/notification/notification_interface.dart b/package/lib/notification/notification_interface.dart index 8afac1a..39ce9ad 100644 --- a/package/lib/notification/notification_interface.dart +++ b/package/lib/notification/notification_interface.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:convert'; +import 'package:dito_sdk/notification/notification_events.dart'; import 'package:event_bus/event_bus.dart'; import 'package:flutter/foundation.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; @@ -11,18 +12,12 @@ import 'notification_entity.dart'; import 'notification_repository.dart'; import 'notification_controller.dart'; -class NotificationClickedEvent { - DataPayload data; - - NotificationClickedEvent(this.data); -} - /// NotificationInterface is an interface for communication with the notification repository and notification controller class NotificationInterface { final NotificationRepository _repository = NotificationRepository(); final NotificationController _controller = NotificationController(); + final NotificationEvents _notificationEvents = NotificationEvents(); final DitoApi _api = DitoApi(); - EventBus eventBus = EventBus(); /// The broadcast stream for received notifications final StreamController @@ -54,7 +49,7 @@ class NotificationInterface { _controller.showNotification(receivedNotification); }); _selectNotificationStream.stream.listen((DataPayload data) async { - eventBus.fire(NotificationClickedEvent(data)); + _notificationEvents.stream.fire(MessageClickedEvent(data)); final notificationId = data.notification; diff --git a/pubspec.yaml b/pubspec.yaml index ba234ae..d8cb633 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,4 +6,4 @@ environment: dev_dependencies: melos: ^6.0.0 dependencies: - event_bus: ^2.0.0 + event_bus: 2.0.0 From dbb65c2c666288243a29096eb633b7eda60247ca Mon Sep 17 00:00:00 2001 From: Ana Machado Date: Thu, 20 Jun 2024 14:00:41 -0300 Subject: [PATCH 25/36] =?UTF-8?q?[NOT-3058]=20Atualiza=C3=A7=C3=A3o=20do?= =?UTF-8?q?=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/lib/app_form.dart | 2 +- package/README.md | 338 ++++++++++---------------------------- package/lib/dito_sdk.dart | 2 +- 3 files changed, 86 insertions(+), 256 deletions(-) diff --git a/example/lib/app_form.dart b/example/lib/app_form.dart index e265a03..b81b4e7 100644 --- a/example/lib/app_form.dart +++ b/example/lib/app_form.dart @@ -27,7 +27,7 @@ class AppFormState extends State { final user = UserEntity( userID: cpf, cpf: cpf, name: 'Teste SDK Flutter', email: email); - await dito.user.identify(user); + await dito.identify(user); } handleIdentify() async { diff --git a/package/README.md b/package/README.md index fddb963..27ea730 100644 --- a/package/README.md +++ b/package/README.md @@ -8,9 +8,44 @@ Dito. Ela permite identificar usuários, registrar eventos e enviar dados person Para instalar a biblioteca DitoSDK em seu aplicativo Flutter, você deve seguir as instruções fornecidas [nesse link](https://pub.dev/packages/dito_sdk/install). +## Entidades + +### UserEntity + +```dart +class UserEntity { + String? userID; + String? name; + String? cpf; + String? email; + String? gender; + String? birthday; + String? location; + Map? customData; +} +``` + +### DataPayload + +```dart +class Details { + final String? link; + final String message; + final String? title; +} + +class DataPayload { + final String reference; + final String identifier; + final String? notification; + final String? notification_log_id; + final Details details; +} +``` + ## Métodos -### initialize() +### initialize() Este método deve ser chamado antes de qualquer outra operação com o SDK. Ele inicializa as chaves de API e SECRET necessárias para a autenticação na plataforma Dito. @@ -24,7 +59,7 @@ void initialize({required String apiKey, required String secretKey}); - **apiKey** _(String, obrigatório)_: A chave de API da plataforma Dito. - **secretKey** _(String, obrigatório)_: O segredo da chave de API da plataforma Dito. -### initializePushNotificationService() +### initializePushNotificationService() Este método deve ser chamado após a inicialização da SDK. Ele inicializa as configurações e serviços necessários para o funcionamento de push notifications da plataforma Dito. @@ -33,66 +68,29 @@ necessários para o funcionamento de push notifications da plataforma Dito. void initializePushNotificationService(); ``` -#### Parâmetros - -- **apiKey** _(String, obrigatório)_: A chave de API da plataforma Dito. -- **secretKey** _(String, obrigatório)_: O segredo da chave de API da plataforma Dito. - ### identify() -Este método define o ID do usuário que será usado para todas as operações subsequentes. - -```dart -void identify(String userId); -``` - -- **userID** _(String, obrigatório)_: Id para identificar o usuário na plataforma da Dito. -- **name** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **email** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **gender** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **birthday** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **location** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **customData** _(Map)_: Parâmetro para identificar o usuário na plataforma da - Dito. - -#### identifyUser() - -Este método registra o usuário na plataforma da Dito com as informações fornecidas anteriormente -usando o método `identify()`. +Este método define as configurações do usuário que será usado para todas as operações subsequentes. ```dart -Future identifyUser - -() async; +void identify(UserEntity user); ``` -#### Exception - -- Caso a SDK ainda não tenha `userId` cadastrado quando esse método for chamado, irá ocorrer um erro - no aplicativo. (utilize o método `setUserId()` para definir o `userId`) +- **user** _(UserEntity, obrigatório)_: Parâmetro para identificar o usuário na plataforma da Dito. ### trackEvent() -O método `trackEvent()` tem a finalidade de registrar um evento na plataforma da Dito. Caso o userID -já tenha sido registrado, o evento será enviado imediatamente. No entanto, caso o userID ainda não -tenha sido registrado, o evento será armazenado localmente e posteriormente enviado quando o userID -for registrado por meio do método `setUserId()`. +O método `trackEvent()` tem a finalidade de registrar um evento na plataforma da Dito. Caso o usuário +já tenha sido registrado, o evento será enviado imediatamente. No entanto, caso o usuário ainda não +tenha sido registrado, o evento será armazenado localmente e posteriormente enviado quando o usuário +for registrado por meio do método `identify()`. ```dart -Future trackEvent -( -{ -required -String -eventName, -double? revenue, -Map -? -customData -, -} -) -async; +Future trackEvent({ + required String eventName, + double? revenue, + Map? customData, +}); ``` #### Parâmetros @@ -102,110 +100,66 @@ async; - **customData** _(Map, opcional)_: Dados personalizados adicionais associados ao evento. -### registryMobileToken() +### setOnMessageClick() -Este método permite registrar um token mobile para o usuário. +O método `setOnMessageClick()` configura uma callback para o evento de clique na notificação push. ```dart -Future registryMobileToken({ - required String token, - String? platform, -}); +Future setOnMessageClick( + Function(DataPayload) onMessageClicked +); ``` #### Parâmetros -- **token** _(String, obrigatório)_: O token mobile que será registrado. -- **platform** _(String, opcional)_: Nome da plataforma que o usuário está acessando o aplicativo. - Valores válidos: 'iPhone' e 'Android'. - `
`_Caso não seja passado algum valor nessa prop, a sdk irá pegar por default o valor - pelo `platform`._ +- **onMessageClicked** _(Function(DataPayload), obrigatório)_: Função que será chamada ao clicar na mensagem -#### Exception -- Caso seja passado um valor diferente de 'iPhone' ou 'Android' na propriedade platform, irá ocorrer - um erro no aplicativo. -- Caso a SDK ainda não tenha `identify` cadastrado quando esse método for chamado, irá ocorrer um - erro no aplicativo. (utilize o método `identify()` para definir o id do usuário) +## Gerenciamento de tokens -### removeMobileToken() +A nossa SDK garante o registro do token atual do usuário além da deleção dos tokens inválidos. Mas também disponibilizamos os métodos a seguir caso necessite de adicionar/remover algum token. -Este método permite remover um token mobile para o usuário. +### registryMobileToken() + +Este método permite registrar um token mobile para o usuário. ```dart -Future removeMobileToken({ - required String token, - String? platform, +Future registryToken({ + String? token, }); ``` #### Parâmetros -- **token** _(String, obrigatório)_: O token mobile que será removido. -- **platform** _(String, opcional)_: Nome da plataforma que o usuário está acessando o aplicativo. - Valores válidos: 'iPhone' e 'Android'. - `
`_Caso não seja passado algum valor nessa prop, a sdk irá pegar por default o valor - pelo `platform`._ +- **token** _(String)_: O token mobile que será registrado, caso não seja enviado pegamos o valor do Firebase. #### Exception -- Caso seja passado um valor diferente de 'iPhone' ou 'Android' na propriedade platform, irá ocorrer - um erro no aplicativo. - Caso a SDK ainda não tenha `identify` cadastrado quando esse método for chamado, irá ocorrer um - erro no aplicativo. (utilize o método `identify()` para definir o id do usuário) + erro no aplicativo. (utilize o método `identify()` para definir o usuário) -### openNotification() +### removeMobileToken() -Este método permite registrar a abertura de uma notificação mobile. +Este método permite remover um token mobile para o usuário. ```dart -Future openNotification -( -{ -required -String -notificationId, -required String identifier, -required String reference -}) async +Future removeMobileToken({ + String? token, +}); ``` #### Parâmetros -- **notificationId** _(String, obrigatório)_: Id da notificação da Dito recebida pelo aplicativo. -- **identifier** _(String, obrigatório)_: Parâmetro para dentificar a notificação na plataforma da - Dito. -- **reference** _(String, obrigatório)_: Parâmetro para identificar o usuário na plataforma da Dito. - -###### Observações - -- Esses parâmetros estarão presentes no data da notificação - -## Classes - -### User - -Classe para manipulação dos dados do usuário. +- **token** _(String)_: O token mobile que será removido, caso não seja enviado pegamos o valor do Firebase. -```dart - -User user = User(sha1("joao@example.com"), 'João da Silva', 'joao@example.com', 'São Paulo'); -``` - -#### Parâmetros +#### Exception -- **userID** _(String, obrigatório)_: Id para identificar o usuário na plataforma da Dito. -- **name** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **email** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **gender** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **birthday** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **location** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **customData** _(Map)_: Parâmetro para identificar o usuário na plataforma da - Dito. +- Caso a SDK ainda não tenha `identify` cadastrado quando esse método for chamado, irá ocorrer um + erro no aplicativo. (utilize o método `identify()` para definir o usuário) ## Exemplos -### Uso básico da SDK: +### Uso da SDK somente com tracking de eventos: ```dart import 'package:dito_sdk/dito_sdk.dart'; @@ -213,133 +167,15 @@ import 'package:dito_sdk/dito_sdk.dart'; final dito = DitoSDK(); // Inicializa a SDK com suas chaves de API -dito.initialize -( -apiKey: 'sua_api_key', secretKey: 'sua_secret_key'); +dito.initialize( apiKey: 'sua_api_key', secretKey: 'sua_secret_key'); -// Define ou atualiza informações do usuário na instância (neste momento, ainda não há comunicação com a Dito) -dito.identify( sha1("joao@example.com"), 'João da Silva', 'joao@example.com', 'São Paulo'); +// Define ou atualiza informações do usuário na instância +final user = UserEntity(userID: cpf, cpf: cpf, name: name, email: email); +await dito.identify(user); -// Envia as informações do usuário (que foram definidas ou atualizadas pelo identify) para a Dito -await dito.identifyUser(); // Registra um evento na Dito -await dito.trackEvent(eventName: ' -login -' -); -``` - -### Uso avançado da SDK: - -#### main.dart - -```dart -import 'package:dito_sdk/dito_sdk.dart'; - -final dito = DitoSDK(); - -// Inicializa a SDK com suas chaves de API -dito.initialize -( -apiKey: 'sua_api_key', secretKey: 'sua_secret_key'); -``` - -#### login.dart - -```dart -import 'package:dito_sdk/dito_sdk.dart'; - -final dito = DitoSDK(); - -// Define o ID do usuário -dito.setUserId -('id_do_usuario -' -);dito.identify( sha1("joao@example.com"), 'João da Silva', 'joao@example.com', 'São Paulo'); -await dito.identifyUser(); -``` - -#### arquivoX.dart - -```dart -import 'package:dito_sdk/dito_sdk.dart'; - -final dito = DitoSDK(); - -// Define ou atualiza informações do usuário na instância (neste momento, ainda não há comunicação com a Dito) -dito.identify -( -sha1("joao@example.com"), 'João da Silva', 'joao@example.com', 'São Paulo'); -await dito.identifyUser(); -await dito.registryMobileToken( -token -: -token -); - -``` - -#### arquivoY.dart - -```dart -import 'package:dito_sdk/dito_sdk.dart'; - -final dito = DitoSDK(); - -// Define ou atualiza informações do usuário na instância (neste momento, ainda não há comunicação com a Dito) -dito.identify -( -sha1("joao@example.com"), 'João da Silva', 'joao@example.com', 'Rio de Janeiro', { -'loja preferida': 'LojaX', -'canal preferido': 'Loja Física' -}); -await -dito -. -identifyUser -( -); -``` - -Isso resultará no envio do seguinte payload do usuário ao chamar `identifyUser()`: - -```javascript -{ - name: 'João da Silva', - email: 'joao@example.com', - location: 'Rio de Janeiro', - customData: { - 'loja preferida': 'LojaX', - 'canal preferido': 'Loja Física' - } -} -``` - -A nossa SDK é uma instância única, o que significa que, mesmo que ela seja inicializada em vários -arquivos ou mais de uma vez, ela sempre referenciará as mesmas informações previamente armazenadas. -Isso nos proporciona a flexibilidade de chamar o método `identify()` a qualquer momento para -adicionar ou atualizar os detalhes do usuário, e somente quando necessário, enviá-los através do -método `identifyUser()`. - -#### arquivoZ.dart - -```dart -import 'package:dito_sdk/dito_sdk.dart'; - -final dito = DitoSDK(); - -// Registra um evento na Dito -dito.trackEvent -( -eventName: 'comprou produto', -revenue: 99.90, -customData: { -'produto': 'produtoX', -'sku_produto': '99999999', -'metodo_pagamento': 'Visa', -}, -); +await dito.trackEvent(eventName: 'login'); ``` ### Uso da SDK com push notification: @@ -364,22 +200,16 @@ dentro dos App's Android e iOS. ```dart import 'package:dito_sdk/dito_sdk.dart'; -// Método para registrar um serviço que irá receber os push quando o app estiver totalmente fechado +// Método para registrar um serviço que irá receber as mensagens quando o app estiver totalmente fechado ou em segundo plano @pragma('vm:entry-point') Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { - final notification = DataPayload.fromJson(jsonDecode(message.data["data"])); - - dito.notificationService().showLocalNotification(NotificationEntity( - id: message.hashCode, - title: notification.details.title || "O nome do aplicativo", - body: notification.details.message, - payload: notification)); + DitoSDK dito = DitoSDK(); + dito.onBackgroundMessageHandler(message, + apiKey: Constants.ditoApiKey, secretKey: Constants.ditoSecretKey); } void main() async { WidgetsFlutterBinding.ensureInitialized(); - - await Firebase.initializeApp(); FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); DitoSDK dito = DitoSDK(); @@ -388,5 +218,5 @@ void main() async { } ``` -> Lembre-se de substituir 'sua_api_key', 'sua_secret_key' e 'id_do_usuario' pelos valores corretos +> Lembre-se de substituir 'sua_api_key', 'sua_secret_key' pelos valores corretos > em seu ambiente. diff --git a/package/lib/dito_sdk.dart b/package/lib/dito_sdk.dart index f82edb2..8a96e56 100644 --- a/package/lib/dito_sdk.dart +++ b/package/lib/dito_sdk.dart @@ -48,7 +48,7 @@ class DitoSDK { await _notificationInterface.initialize(); } - setOnMessageClick(Function(DataPayload) onMessageClicked) { + void setOnMessageClick(Function(DataPayload) onMessageClicked) { _notificationEvents.stream.on().listen((event) { onMessageClicked(event.data); }); From 80f148b5a30eb12615534a584819a0282d5b8b52 Mon Sep 17 00:00:00 2001 From: Ana Machado Date: Tue, 25 Jun 2024 11:42:54 -0300 Subject: [PATCH 26/36] pt-br --- README.pt-br.md | 293 ++++++++++++++++-------------------------------- 1 file changed, 94 insertions(+), 199 deletions(-) diff --git a/README.pt-br.md b/README.pt-br.md index 1b325a1..27ea730 100644 --- a/README.pt-br.md +++ b/README.pt-br.md @@ -1,34 +1,57 @@ -[![en](https://img.shields.io/badge/lang-en-red.svg)](https://github.com/ditointernet/sdk_mobile_flutter/blob/main/README.md) - # Dito SDK (Flutter) -DitoSDK é uma biblioteca Dart que fornece métodos para integrar aplicativos com a plataforma da Dito. Ela permite identificar usuários, registrar eventos e enviar dados personalizados. +DitoSDK é uma biblioteca Dart que fornece métodos para integrar aplicativos com a plataforma da +Dito. Ela permite identificar usuários, registrar eventos e enviar dados personalizados. ## Instalação -Para instalar a biblioteca DitoSDK em seu aplicativo Flutter, você deve seguir as instruções fornecidas [nesse link](https://pub.dev/packages/dito_sdk/install). - -## Métodos +Para instalar a biblioteca DitoSDK em seu aplicativo Flutter, você deve seguir as instruções +fornecidas [nesse link](https://pub.dev/packages/dito_sdk/install). -### initialize() +## Entidades -Este método deve ser chamado antes de qualquer outra operação com o SDK. Ele inicializa as chaves de API e SECRET necessárias para a autenticação na plataforma Dito. +### UserEntity ```dart -void initialize({required String apiKey, required String secretKey}); +class UserEntity { + String? userID; + String? name; + String? cpf; + String? email; + String? gender; + String? birthday; + String? location; + Map? customData; +} ``` -#### Parâmetros +### DataPayload -- **apiKey** _(String, obrigatório)_: A chave de API da plataforma Dito. -- **secretKey** _(String, obrigatório)_: O segredo da chave de API da plataforma Dito. +```dart +class Details { + final String? link; + final String message; + final String? title; +} -### initializePushNotificationService() +class DataPayload { + final String reference; + final String identifier; + final String? notification; + final String? notification_log_id; + final Details details; +} +``` + +## Métodos -Este método deve ser chamado após a inicialização da SDK. Ele inicializa as configurações e serviços necessários para o funcionamento de push notifications da plataforma Dito. +### initialize() + +Este método deve ser chamado antes de qualquer outra operação com o SDK. Ele inicializa as chaves de +API e SECRET necessárias para a autenticação na plataforma Dito. ```dart -void initializePushNotificationService(); +void initialize({required String apiKey, required String secretKey}); ``` #### Parâmetros @@ -36,142 +59,107 @@ void initializePushNotificationService(); - **apiKey** _(String, obrigatório)_: A chave de API da plataforma Dito. - **secretKey** _(String, obrigatório)_: O segredo da chave de API da plataforma Dito. -### identify() +### initializePushNotificationService() -Este método define o ID do usuário que será usado para todas as operações subsequentes. +Este método deve ser chamado após a inicialização da SDK. Ele inicializa as configurações e serviços +necessários para o funcionamento de push notifications da plataforma Dito. ```dart -void identify(String userId); +void initializePushNotificationService(); ``` -- **userID** _(String, obrigatório)_: Id para identificar o usuário na plataforma da Dito. -- **name** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **email** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **gender** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **birthday** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **location** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **customData** _(Map)_: Parâmetro para identificar o usuário na plataforma da Dito. - - -#### identifyUser() +### identify() -Este método registra o usuário na plataforma da Dito com as informações fornecidas anteriormente usando o método `identify()`. +Este método define as configurações do usuário que será usado para todas as operações subsequentes. ```dart -Future identifyUser() async; +void identify(UserEntity user); ``` -#### Exception - -- Caso a SDK ainda não tenha `userId` cadastrado quando esse método for chamado, irá ocorrer um erro no aplicativo. (utilize o método `setUserId()` para definir o `userId`) +- **user** _(UserEntity, obrigatório)_: Parâmetro para identificar o usuário na plataforma da Dito. ### trackEvent() -O método `trackEvent()` tem a finalidade de registrar um evento na plataforma da Dito. Caso o userID já tenha sido registrado, o evento será enviado imediatamente. No entanto, caso o userID ainda não tenha sido registrado, o evento será armazenado localmente e posteriormente enviado quando o userID for registrado por meio do método `setUserId()`. +O método `trackEvent()` tem a finalidade de registrar um evento na plataforma da Dito. Caso o usuário +já tenha sido registrado, o evento será enviado imediatamente. No entanto, caso o usuário ainda não +tenha sido registrado, o evento será armazenado localmente e posteriormente enviado quando o usuário +for registrado por meio do método `identify()`. ```dart Future trackEvent({ required String eventName, double? revenue, Map? customData, -}) async; +}); ``` #### Parâmetros - **eventName** _(String, obrigatório)_: O nome do evento a ser registrado. - **revenue** _(double, opcional)_: A receita associada ao evento. -- **customData** _(Map, opcional)_: Dados personalizados adicionais associados ao evento. +- **customData** _(Map, opcional)_: Dados personalizados adicionais associados ao + evento. -### registryMobileToken() +### setOnMessageClick() -Este método permite registrar um token mobile para o usuário. +O método `setOnMessageClick()` configura uma callback para o evento de clique na notificação push. ```dart -Future registryMobileToken({ - required String token, - String? platform, -}); +Future setOnMessageClick( + Function(DataPayload) onMessageClicked +); ``` #### Parâmetros -- **token** _(String, obrigatório)_: O token mobile que será registrado. -- **platform** _(String, opcional)_: Nome da plataforma que o usuário está acessando o aplicativo. Valores válidos: 'iPhone' e 'Android'. - `
`_Caso não seja passado algum valor nessa prop, a sdk irá pegar por default o valor pelo `platform`._ +- **onMessageClicked** _(Function(DataPayload), obrigatório)_: Função que será chamada ao clicar na mensagem -#### Exception -- Caso seja passado um valor diferente de 'iPhone' ou 'Android' na propriedade platform, irá ocorrer um erro no aplicativo. -- Caso a SDK ainda não tenha `identify` cadastrado quando esse método for chamado, irá ocorrer um erro no aplicativo. (utilize o método `identify()` para definir o id do usuário) +## Gerenciamento de tokens -### removeMobileToken() +A nossa SDK garante o registro do token atual do usuário além da deleção dos tokens inválidos. Mas também disponibilizamos os métodos a seguir caso necessite de adicionar/remover algum token. -Este método permite remover um token mobile para o usuário. +### registryMobileToken() + +Este método permite registrar um token mobile para o usuário. ```dart -Future removeMobileToken({ - required String token, - String? platform, +Future registryToken({ + String? token, }); ``` #### Parâmetros -- **token** _(String, obrigatório)_: O token mobile que será removido. -- **platform** _(String, opcional)_: Nome da plataforma que o usuário está acessando o aplicativo. Valores válidos: 'iPhone' e 'Android'. - `
`_Caso não seja passado algum valor nessa prop, a sdk irá pegar por default o valor pelo `platform`._ +- **token** _(String)_: O token mobile que será registrado, caso não seja enviado pegamos o valor do Firebase. #### Exception -- Caso seja passado um valor diferente de 'iPhone' ou 'Android' na propriedade platform, irá ocorrer um erro no aplicativo. -- Caso a SDK ainda não tenha `identify` cadastrado quando esse método for chamado, irá ocorrer um erro no aplicativo. (utilize o método `identify()` para definir o id do usuário) +- Caso a SDK ainda não tenha `identify` cadastrado quando esse método for chamado, irá ocorrer um + erro no aplicativo. (utilize o método `identify()` para definir o usuário) -### openNotification() +### removeMobileToken() -Este método permite registrar a abertura de uma notificação mobile. +Este método permite remover um token mobile para o usuário. ```dart -Future openNotification({ - required String notificationId, - required String identifier, - required String reference -}) async +Future removeMobileToken({ + String? token, +}); ``` #### Parâmetros -- **notificationId** _(String, obrigatório)_: Id da notificação da Dito recebida pelo aplicativo. -- **identifier** _(String, obrigatório)_: Parâmetro para dentificar a notificação na plataforma da Dito. -- **reference** _(String, obrigatório)_: Parâmetro para identificar o usuário na plataforma da Dito. +- **token** _(String)_: O token mobile que será removido, caso não seja enviado pegamos o valor do Firebase. -###### Observações - -- Esses parâmetros estarão presentes no data da notificação - -## Classes - -### User - -Classe para manipulação dos dados do usuário. - -```dart -User user = User( sha1("joao@example.com"), 'João da Silva', 'joao@example.com', 'São Paulo'); -``` - -#### Parâmetros +#### Exception -- **userID** _(String, obrigatório)_: Id para identificar o usuário na plataforma da Dito. -- **name** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **email** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **gender** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **birthday** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **location** _(String)_: Parâmetro para identificar o usuário na plataforma da Dito. -- **customData** _(Map)_: Parâmetro para identificar o usuário na plataforma da Dito. +- Caso a SDK ainda não tenha `identify` cadastrado quando esse método for chamado, irá ocorrer um + erro no aplicativo. (utilize o método `identify()` para definir o usuário) ## Exemplos -### Uso básico da SDK: +### Uso da SDK somente com tracking de eventos: ```dart import 'package:dito_sdk/dito_sdk.dart'; @@ -179,111 +167,21 @@ import 'package:dito_sdk/dito_sdk.dart'; final dito = DitoSDK(); // Inicializa a SDK com suas chaves de API -dito.initialize(apiKey: 'sua_api_key', secretKey: 'sua_secret_key'); +dito.initialize( apiKey: 'sua_api_key', secretKey: 'sua_secret_key'); -// Define ou atualiza informações do usuário na instância (neste momento, ainda não há comunicação com a Dito) -dito.identify( sha1("joao@example.com"), 'João da Silva', 'joao@example.com', 'São Paulo'); +// Define ou atualiza informações do usuário na instância +final user = UserEntity(userID: cpf, cpf: cpf, name: name, email: email); +await dito.identify(user); -// Envia as informações do usuário (que foram definidas ou atualizadas pelo identify) para a Dito -await dito.identifyUser(); // Registra um evento na Dito await dito.trackEvent(eventName: 'login'); ``` -### Uso avançado da SDK: - -#### main.dart - -```dart -import 'package:dito_sdk/dito_sdk.dart'; - -final dito = DitoSDK(); - -// Inicializa a SDK com suas chaves de API -dito.initialize(apiKey: 'sua_api_key', secretKey: 'sua_secret_key'); -``` - -#### login.dart - -```dart -import 'package:dito_sdk/dito_sdk.dart'; - -final dito = DitoSDK(); - -// Define o ID do usuário -dito.setUserId('id_do_usuario'); -dito.identify( sha1("joao@example.com"), 'João da Silva', 'joao@example.com', 'São Paulo'); -await dito.identifyUser(); -``` - -#### arquivoX.dart - -```dart -import 'package:dito_sdk/dito_sdk.dart'; - -final dito = DitoSDK(); - -// Define ou atualiza informações do usuário na instância (neste momento, ainda não há comunicação com a Dito) -dito.identify( sha1("joao@example.com"), 'João da Silva', 'joao@example.com', 'São Paulo'); -await dito.identifyUser(); -await dito.registryMobileToken(token: token); - -``` - -#### arquivoY.dart - -```dart -import 'package:dito_sdk/dito_sdk.dart'; - -final dito = DitoSDK(); - -// Define ou atualiza informações do usuário na instância (neste momento, ainda não há comunicação com a Dito) -dito.identify( sha1("joao@example.com"), 'João da Silva', 'joao@example.com', 'Rio de Janeiro', { - 'loja preferida': 'LojaX', - 'canal preferido': 'Loja Física' -}); -await dito.identifyUser(); -``` - -Isso resultará no envio do seguinte payload do usuário ao chamar `identifyUser()`: - -```javascript -{ - name: 'João da Silva', - email: 'joao@example.com', - location: 'Rio de Janeiro', - customData: { - 'loja preferida': 'LojaX', - 'canal preferido': 'Loja Física' - } -} -``` - -A nossa SDK é uma instância única, o que significa que, mesmo que ela seja inicializada em vários arquivos ou mais de uma vez, ela sempre referenciará as mesmas informações previamente armazenadas. Isso nos proporciona a flexibilidade de chamar o método `identify()` a qualquer momento para adicionar ou atualizar os detalhes do usuário, e somente quando necessário, enviá-los através do método `identifyUser()`. - -#### arquivoZ.dart - -```dart -import 'package:dito_sdk/dito_sdk.dart'; - -final dito = DitoSDK(); - -// Registra um evento na Dito -dito.trackEvent( - eventName: 'comprou produto', - revenue: 99.90, - customData: { - 'produto': 'produtoX', - 'sku_produto': '99999999', - 'metodo_pagamento': 'Visa', - }, -); -``` - ### Uso da SDK com push notification: -Para o funcionamento é necessário configurar a lib do Firebase Cloud Message (FCM), seguindo os seguintes passos: +Para o funcionamento é necessário configurar a lib do Firebase Cloud Message (FCM), seguindo os +seguintes passos: ```shell dart pub global activate flutterfire_cli @@ -294,28 +192,24 @@ flutter pub add firebase_core firebase_messaging flutterfire configure ``` -Siga os passos que irá aparecer na CLI, assim terá as chaves de acesso do Firebase configuradas dentro dos App's Android e iOS. +Siga os passos que irá aparecer na CLI, assim terá as chaves de acesso do Firebase configuradas +dentro dos App's Android e iOS. #### main.dart + ```dart import 'package:dito_sdk/dito_sdk.dart'; -// Método para registrar um serviço que irá receber os push quando o app estiver totalmente fechado +// Método para registrar um serviço que irá receber as mensagens quando o app estiver totalmente fechado ou em segundo plano @pragma('vm:entry-point') Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { - final notification = DataPayload.fromJson(jsonDecode(message.data["data"])); - - dito.notificationService().showLocalNotification(CustomNotification( - id: message.hashCode, - title: notification.details.title || "O nome do aplicativo", - body: notification.details.message, - payload: notification)); + DitoSDK dito = DitoSDK(); + dito.onBackgroundMessageHandler(message, + apiKey: Constants.ditoApiKey, secretKey: Constants.ditoSecretKey); } void main() async { WidgetsFlutterBinding.ensureInitialized(); - - await Firebase.initializeApp(); FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); DitoSDK dito = DitoSDK(); @@ -324,4 +218,5 @@ void main() async { } ``` -> Lembre-se de substituir 'sua_api_key', 'sua_secret_key' e 'id_do_usuario' pelos valores corretos em seu ambiente. +> Lembre-se de substituir 'sua_api_key', 'sua_secret_key' pelos valores corretos +> em seu ambiente. From 608a870ae10c748f47a012f00a56ccdec0e0315b Mon Sep 17 00:00:00 2001 From: Ana Machado Date: Tue, 25 Jun 2024 13:48:42 -0300 Subject: [PATCH 27/36] en_us --- README.md | 191 +++++++++++++++++++++++++++++++----------------- README.pt-br.md | 6 +- 2 files changed, 126 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index c481654..97a9aa8 100644 --- a/README.md +++ b/README.md @@ -2,20 +2,54 @@ [![pt-br](https://img.shields.io/badge/lang-pt--br-green.svg)](https://github.com/ditointernet/sdk_mobile_flutter/blob/main/README.pt-br.md) [![es](https://img.shields.io/badge/lang-es-yellow.svg)](https://github.com/ditointernet/sdk_mobile_flutter/blob/main/README.es.md) -```markdown # Dito SDK (Flutter) -DitoSDK is a Dart library that provides methods to integrate applications with the Dito platform. It allows user identification, event registration, and sending custom data. +DitoSDK is a Dart library that provides methods to integrate applications with the Dito platform. It allows to identify users, record events, and send personalized data. ## Installation -To install the DitoSDK library in your Flutter application, you should follow the instructions provided [at this link](https://pub.dev/packages/dito_sdk/install). +To install the DitoSDK library in your Flutter application, you must follow the instructions provided at [this link](https://pub.dev/packages/dito_sdk/install). + +## Entities + +### UserEntity + +```dart +class UserEntity { + String? userID; + String? name; + String? cpf; + String? email; + String? gender; + String? birthday; + String? location; + Map? customData; +} +``` + +### DataPayload + +```dart +class Details { + final String? link; + final String message; + final String? title; +} + +class DataPayload { + final String reference; + final String identifier; + final String? notification; + final String? notification_log_id; + final Details details; +} +``` ## Methods -### initialize() +### initialize() -This method must be called before any other operation with the SDK. It initializes the API and SECRET keys necessary for authentication on the Dito platform. +This method must be called before any other operation with the SDK. It initializes the API and SECRET keys required for authentication on the Dito platform. ```dart void initialize({required String apiKey, required String secretKey}); @@ -26,7 +60,7 @@ void initialize({required String apiKey, required String secretKey}); - **apiKey** _(String, required)_: The API key for the Dito platform. - **secretKey** _(String, required)_: The secret key for the Dito platform. -### initializePushNotificationService() +### initializePushNotificationService() This method should be called after the SDK initialization. It initializes the settings and services necessary for the functioning of push notifications on the Dito platform. @@ -34,91 +68,115 @@ This method should be called after the SDK initialization. It initializes the se void initializePushNotificationService(); ``` -#### Parameters - -- **apiKey** _(String, required)_: The API key for the Dito platform. -- **secretKey** _(String, required)_: The secret key for the Dito platform. - ### identify() -This method sets the user ID that will be used for all subsequent operations. +This method defines user settings that will be used for all subsequent operations. ```dart -void identify(String userId); +void identify(UserEntity user); ``` -- **userID** _(String, required)_: ID to identify the user on the Dito platform. -- **name** _(String)_: Parameter to identify the user on the Dito platform. -- **email** _(String)_: Parameter to identify the user on the Dito platform. -- **gender** _(String)_: Parameter to identify the user on the Dito platform. -- **birthday** _(String)_: Parameter to identify the user on the Dito platform. -- **location** _(String)_: Parameter to identify the user on the Dito platform. -- **customData** _(Map)_: Parameter to identify the user on the Dito platform. +#### Parameters + +- **user** _(UserEntity, obrigatório)_: Parameter to identify the user on Dito’s platform. ### trackEvent() -This method records an event on the Dito platform. +This method records an event on the Dito platform. If the user has already been registered, the event will be sent immediately. However, if the user has not yet been registered, the event will be stored locally and sent later. ```dart -void trackEvent({required String eventName, double? revenue, Map? customData}); +Future trackEvent({ + required String eventName, + double? revenue, + Map? customData, +}); ``` +#### Parameters + - **eventName** _(String, required)_: Name of the event to be tracked. - **revenue** _(double)_: Revenue generated from the event. - **customData** _(Map setOnMessageClick( + Function(DataPayload) onMessageClicked +); +``` + +#### Parameters -### main.dart +- **onMessageClicked** _(Function(DataPayload), required)_: Function that will be called when clicking on the message + + +## Token management + +Our SDK ensures the registration of the current user's token in addition to the deletion of invalid tokens. However, we also provide the following methods in case you need to add/remove any token. + +### registryMobileToken() + +This method allows you to register a mobile token for the user. ```dart -import 'package:dito_sdk/dito_sdk.dart'; +Future registryToken({ + String? token, +}); +``` -void main() { - DitoSDK dito = DitoSDK(); +#### Parâmetros - dito.initialize(apiKey: 'your_api_key', secretKey: 'your_secret_key'); - dito.initializePushNotificationService(); - - dito.identify('user_id', name: 'John Doe', email: 'john.doe@example.com'); - dito.trackEvent( - eventName: 'purchased product', - revenue: 99.90, - customData: { - 'product': 'productX', - 'sku_product': '99999999', - 'payment_method': 'Visa', - }, - ); -} +- **token** _(String)_: The mobile token that will be registered, if it is not sent we will get the value from Firebase. + +#### Exception + +- If the SDK does not already have `user` registered when this method is called, an error will occur in the application. (Use the `identify()` method to define the user) +### removeMobileToken() + +This method allows you to remove a mobile token for the user. + +```dart +Future removeMobileToken({ + String? token, +}); ``` -You can call the `identify()` method at any time to add or update user details, and only when necessary, send them through the `identifyUser()` method. +#### Parameters + +- **token** _(String)_: The mobile token that will be removed, if it is not sent we will get the value from Firebase. + +#### Exception -### arquivoZ.dart +- If the SDK does not already have `user` registered when this method is called, an error will occur in the application. (Use the `identify()` method to define the user) + +## Examples + +### Using the SDK only for event tracking: ```dart import 'package:dito_sdk/dito_sdk.dart'; final dito = DitoSDK(); -// Record an event on Dito -dito.trackEvent( - eventName: 'purchased product', - revenue: 99.90, - customData: { - 'product': 'productX', - 'sku_product': '99999999', - 'payment_method': 'Visa', - }, -); +// Inicializa a SDK com suas chaves de API +dito.initialize( apiKey: 'your_api_key', secretKey: 'your_secret_key'); + +// Define ou atualiza informações do usuário na instância +final user = UserEntity(userID: cpf, cpf: cpf, name: name, email: email); +await dito.identify(user); + + +// Registra um evento na Dito +await dito.trackEvent(eventName: 'login'); ``` -### Using SDK with Push Notification: +### Using the SDK for push notification: -To make it work, it is necessary to configure the Firebase Cloud Messaging (FCM) library by following these steps: +For it to work, you need to configure the Firebase Cloud Message (FCM) lib, following the +following steps: ```shell dart pub global activate flutterfire_cli @@ -129,29 +187,24 @@ flutter pub add firebase_core firebase_messaging flutterfire configure ``` -Follow the steps that will appear on the CLI, so you will have the Firebase access keys configured within the Android and iOS apps. +Follow the steps that will appear in the CLI, so you will have the Firebase access keys configured +within the Android and iOS Apps. #### main.dart ```dart import 'package:dito_sdk/dito_sdk.dart'; -// Method to register a service that will receive push notifications when the app is completely closed +// Method to register a service that will receive messages when the app is completely closed or in the background @pragma('vm:entry-point') Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { - final notification = DataPayload.fromJson(jsonDecode(message.data["data"])); - - dito.notificationService().showLocalNotification(NotificationEntity( - id: message.hashCode, - title: notification.details.title || "App Name", - body: notification.details.message, - payload: notification)); + DitoSDK dito = DitoSDK(); + dito.onBackgroundMessageHandler(message, + apiKey: Constants.ditoApiKey, secretKey: Constants.ditoSecretKey); } void main() async { WidgetsFlutterBinding.ensureInitialized(); - - await Firebase.initializeApp(); FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); DitoSDK dito = DitoSDK(); @@ -160,5 +213,5 @@ void main() async { } ``` -> Remember to replace 'your_api_key', 'your_secret_key', and 'user_id' with the correct values in your environment. -``` \ No newline at end of file +> Remember to replace 'your_api_key', 'your_secret_key' with the correct values +> in your environment. diff --git a/README.pt-br.md b/README.pt-br.md index 27ea730..d25a7ac 100644 --- a/README.pt-br.md +++ b/README.pt-br.md @@ -76,6 +76,8 @@ Este método define as configurações do usuário que será usado para todas as void identify(UserEntity user); ``` +#### Parameters + - **user** _(UserEntity, obrigatório)_: Parâmetro para identificar o usuário na plataforma da Dito. ### trackEvent() @@ -135,7 +137,7 @@ Future registryToken({ #### Exception -- Caso a SDK ainda não tenha `identify` cadastrado quando esse método for chamado, irá ocorrer um +- Caso a SDK ainda não tenha `user` cadastrado quando esse método for chamado, irá ocorrer um erro no aplicativo. (utilize o método `identify()` para definir o usuário) ### removeMobileToken() @@ -154,7 +156,7 @@ Future removeMobileToken({ #### Exception -- Caso a SDK ainda não tenha `identify` cadastrado quando esse método for chamado, irá ocorrer um +- Caso a SDK ainda não tenha `user` cadastrado quando esse método for chamado, irá ocorrer um erro no aplicativo. (utilize o método `identify()` para definir o usuário) ## Exemplos From e9536db71f0d7291543165635227cf4c34dec4aa Mon Sep 17 00:00:00 2001 From: Ana Machado Date: Tue, 25 Jun 2024 14:12:05 -0300 Subject: [PATCH 28/36] en_us --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 97a9aa8..5f52802 100644 --- a/README.md +++ b/README.md @@ -161,15 +161,15 @@ import 'package:dito_sdk/dito_sdk.dart'; final dito = DitoSDK(); -// Inicializa a SDK com suas chaves de API +// Initialize the SDK with your API keys dito.initialize( apiKey: 'your_api_key', secretKey: 'your_secret_key'); -// Define ou atualiza informações do usuário na instância +// Sets or updates user information on the instance final user = UserEntity(userID: cpf, cpf: cpf, name: name, email: email); await dito.identify(user); -// Registra um evento na Dito +// Register an event at Dito await dito.trackEvent(eventName: 'login'); ``` From 9ae5ff842cc99b8b45a122a185c696d44df21490 Mon Sep 17 00:00:00 2001 From: Ana Machado Date: Tue, 25 Jun 2024 14:59:12 -0300 Subject: [PATCH 29/36] en_us --- README.es.md | 170 --------------------------------------------------- README.md | 1 - 2 files changed, 171 deletions(-) delete mode 100644 README.es.md diff --git a/README.es.md b/README.es.md deleted file mode 100644 index d101c83..0000000 --- a/README.es.md +++ /dev/null @@ -1,170 +0,0 @@ -[![es](https://img.shields.io/badge/lang-es-yellow.svg)](https://github.com/ditointernet/sdk_mobile_flutter/blob/main/README.es.md) - -```markdown -# Dito SDK (Flutter) - -DitoSDK es una biblioteca Dart que proporciona métodos para integrar aplicaciones con la plataforma de Dito. Permite identificar usuarios, registrar eventos y enviar datos personalizados. - -## Instalación - -Para instalar la biblioteca DitoSDK en tu aplicación Flutter, debes seguir las instrucciones proporcionadas [en este enlace](https://pub.dev/packages/dito_sdk/install). - -## Métodos - -### initialize() - -Este método debe ser llamado antes de cualquier otra operación con el SDK. Inicializa las claves de API y SECRET necesarias para la autenticación en la plataforma Dito. - -```dart -void initialize({required String apiKey, required String secretKey}); -``` - -#### Parámetros - -- **apiKey** _(String, obligatorio)_: La clave de API de la plataforma Dito. -- **secretKey** _(String, obligatorio)_: El secreto de la clave de API de la plataforma Dito. - -### initializePushNotificationService() - -Este método debe ser llamado después de la inicialización del SDK. Inicializa las configuraciones y servicios necesarios para el funcionamiento de notificaciones push de la plataforma Dito. - -```dart -void initializePushNotificationService(); -``` - -#### Parámetros - -- **apiKey** _(String, obligatorio)_: La clave de API de la plataforma Dito. -- **secretKey** _(String, obligatorio)_: El secreto de la clave de API de la plataforma Dito. - -### identify() - -Este método define el ID del usuario que se utilizará para todas las operaciones subsiguientes. - -```dart -void identify(String userId); -``` - -- **userID** _(String, obligatorio)_: ID para identificar al usuario en la plataforma Dito. -- **name** _(String)_: Parámetro para identificar al usuario en la plataforma Dito. -- **email** _(String)_: Parámetro para identificar al usuario en la plataforma Dito. -- **gender** _(String)_: Parámetro para identificar al usuario en la plataforma Dito. -- **birthday** _(String)_: Parámetro para identificar al usuario en la plataforma Dito. -- **location** _(String)_: Parámetro para identificar al usuario en la plataforma Dito. -- **customData** _(Map)_: Datos personalizados para identificar al usuario en la plataforma Dito. - -### trackEvent() - -Este método permite registrar eventos personalizados en la plataforma Dito. - -```dart -void trackEvent({ - required String eventName, - double? revenue, - Map? customData, -}); -``` - -- **eventName** _(String, obligatorio)_: El nombre del evento a ser registrado. -- **revenue** _(double, opcional)_: Ingreso generado por el evento. -- **customData** _(Map, opcional)_: Datos personalizados relacionados con el evento. - -### Ejemplo de Uso - -```dart -import 'package:dito_sdk/dito_sdk.dart'; - -final dito = DitoSDK(); - -dito.initialize( - apiKey: 'tu_api_key', - secretKey: 'tu_secret_key', -); - -dito.identify( - userId: 'id_del_usuario', - name: 'Nombre del Usuario', - email: 'email@ejemplo.com', - gender: 'género', - birthday: 'fecha_de_nacimiento', - location: 'ubicación', - customData: { - 'llave_personalizada': 'valor_personalizado', - }, -); - -dito.trackEvent( - eventName: 'nombre_del_evento', - revenue: 100.0, - customData: { - 'llave_personalizada': 'valor_personalizado', - }, -); -``` - -Puedes llamar al método `identify()` en cualquier momento para agregar o actualizar los detalles del usuario, y solo cuando sea necesario, enviarlos a través del método `identifyUser()`. - -#### archivoZ.dart - -```dart -import 'package:dito_sdk/dito_sdk.dart'; - -final dito = DitoSDK(); - -// Registra un evento en Dito -dito.trackEvent( - eventName: 'compró producto', - revenue: 99.90, - customData: { - 'producto': 'productoX', - 'sku_producto': '99999999', - 'método_pago': 'Visa', - }, -); -``` - -### Uso del SDK con notificaciones push: - -Para el funcionamiento es necesario configurar la biblioteca de Firebase Cloud Message (FCM), siguiendo los siguientes pasos: - -```shell -dart pub global activate flutterfire_cli -flutter pub add firebase_core firebase_messaging -``` - -```shell -flutterfire configure -``` - -Sigue los pasos que aparecerán en la CLI, así tendrás las claves de acceso de Firebase configuradas dentro de las aplicaciones Android e iOS. - -#### main.dart -```dart -import 'package:dito_sdk/dito_sdk.dart'; - -// Método para registrar un servicio que recibirá los push cuando la aplicación esté completamente cerrada -@pragma('vm:entry-point') -Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { - final notification = DataPayload.fromJson(jsonDecode(message.data["data"])); - - dito.notificationService().showLocalNotification(CustomNotification( - id: message.hashCode, - title: notification.details.title || "El nombre de la aplicación", - body: notification.details.message, - payload: notification)); -} - -void main() async { - WidgetsFlutterBinding.ensureInitialized(); - - await Firebase.initializeApp(); - FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); - - DitoSDK dito = DitoSDK(); - dito.initialize(apiKey: 'tu_api_key', secretKey: 'tu_secret_key'); - await dito.initializePushService(); -} -``` - -> Recuerda reemplazar 'tu_api_key', 'tu_secret_key' y 'id_del_usuario' por los valores correctos en tu entorno. -``` \ No newline at end of file diff --git a/README.md b/README.md index 5f52802..8284afe 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ [![en](https://img.shields.io/badge/lang-en-red.svg)](https://github.com/ditointernet/sdk_mobile_flutter/blob/main/README.md) [![pt-br](https://img.shields.io/badge/lang-pt--br-green.svg)](https://github.com/ditointernet/sdk_mobile_flutter/blob/main/README.pt-br.md) -[![es](https://img.shields.io/badge/lang-es-yellow.svg)](https://github.com/ditointernet/sdk_mobile_flutter/blob/main/README.es.md) # Dito SDK (Flutter) From 0b8cc6c56e0b84d9c5d90ec8feedc40d6e88ce6a Mon Sep 17 00:00:00 2001 From: Igor Gottschalg <119457595+igorgsduarte@users.noreply.github.com> Date: Tue, 30 Jul 2024 10:45:25 -0300 Subject: [PATCH 30/36] adding new events (#18) --- .gitignore | 4 + example/ios/Podfile | 2 +- example/lib/app.dart | 3 +- example/lib/app_form.dart | 38 +++-- example/lib/main.dart | 10 +- example/pubspec.lock | 30 ++-- example/pubspec.yaml | 6 +- package/lib/data/database.dart | 142 ++++++++++++++++++ package/lib/data/dito_api.dart | 31 ++++ package/lib/data/event_database.dart | 103 ++----------- package/lib/data/notification_database.dart | 85 +++++++++++ package/lib/dito_sdk.dart | 62 +------- package/lib/event/event_entity.dart | 24 +-- package/lib/event/event_interface.dart | 23 +-- package/lib/event/event_repository.dart | 27 ++-- .../notification/notification_controller.dart | 91 ++++++----- .../lib/notification/notification_entity.dart | 71 ++++++--- .../notification/notification_interface.dart | 124 ++++++++++----- .../notification/notification_repository.dart | 133 +++++++++------- package/lib/user/user_interface.dart | 17 ++- package/pubspec.lock | 28 ++-- package/pubspec.yaml | 6 +- package/test/dito_sdk_test.dart | 1 - package/test/event/database_test.dart | 53 +++---- package/test/event/event_test.dart | 26 ++-- pubspec.lock | 8 - pubspec.yaml | 2 - 27 files changed, 702 insertions(+), 448 deletions(-) create mode 100644 package/lib/data/database.dart create mode 100644 package/lib/data/notification_database.dart diff --git a/.gitignore b/.gitignore index d0bfcf6..03ba70f 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,7 @@ example/build/ package/build/ .flutter-plugins* package/test/.env-test.json +/example/ios/_GoogleService-Info.plist +/build/ +/example/lib/firebase_options.dart +/example/ios/Podfile.lock diff --git a/example/ios/Podfile b/example/ios/Podfile index d97f17e..9e2cb6e 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '12.0' +platform :ios, '16.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/example/lib/app.dart b/example/lib/app.dart index ba48a06..597c43c 100644 --- a/example/lib/app.dart +++ b/example/lib/app.dart @@ -37,7 +37,6 @@ class _AppState extends State { ), body: const Center( child: AppForm(), - )) - ); + ))); } } diff --git a/example/lib/app_form.dart b/example/lib/app_form.dart index b81b4e7..5384ce7 100644 --- a/example/lib/app_form.dart +++ b/example/lib/app_form.dart @@ -1,6 +1,6 @@ import 'package:dito_sdk/dito_sdk.dart'; +import 'package:dito_sdk/event/event_entity.dart'; import 'package:dito_sdk/user/user_entity.dart'; -import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -20,29 +20,34 @@ class AppFormState extends State { Widget build(BuildContext context) { final dito = Provider.of(context); - String cpf = "32190381209"; - String email = "teste.sdk2@dito.com.br"; + String cpf = "33333333333"; + String email = "teste.sdk-flutter@dito.com.br"; identify() async { final user = UserEntity( - userID: cpf, cpf: cpf, name: 'Teste SDK Flutter', email: email); + userID: "e400c65b1800bee5bf546c5b7bd37cd4f7452bb8", + cpf: cpf, + name: 'Teste SDK Flutter 33333333333', + email: email); - await dito.identify(user); + await dito.user.identify(user); + await dito.notification + .registryToken(await dito.notification.getFirebaseToken()); } handleIdentify() async { if (_formKey.currentState!.validate()) { - await identify(); - - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Usuário identificado')), - ); + await identify().then((response) => { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Usuário identificado')), + ) + }); } } handleNotification() async { if (_formKey.currentState!.validate()) { - await dito.trackEvent(eventName: 'action-test'); + await dito.event.trackEvent(EventEntity(eventName: 'action-test')); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Evento de notificação solicitado')), @@ -51,7 +56,8 @@ class AppFormState extends State { } handleDeleteToken() async { - await FirebaseMessaging.instance.deleteToken(); + await dito.notification + .removeToken(await dito.notification.getFirebaseToken()); } return Form( @@ -60,8 +66,8 @@ class AppFormState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ TextFormField( - onSaved: (value) { - cpf = value!; + onChanged: (value) { + cpf = value; }, initialValue: cpf, validator: (value) { @@ -72,8 +78,8 @@ class AppFormState extends State { }, ), TextFormField( - onSaved: (value) { - email = value!; + onChanged: (value) { + email = value; }, initialValue: email, validator: (value) { diff --git a/example/lib/main.dart b/example/lib/main.dart index 3849820..2869462 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,5 +1,6 @@ import 'package:dito_sdk/dito_sdk.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -21,9 +22,12 @@ void main() async { dito.initialize( apiKey: Constants.ditoApiKey, secretKey: Constants.ditoSecretKey); await dito.initializePushNotificationService(); - dito.setOnMessageClick((data) { - print(data.toJson()); - }); + + dito.notification.onMessageClick = (data) { + if (kDebugMode) { + print(data.toJson()); + } + }; runApp(MultiProvider(providers: [ Provider( diff --git a/example/pubspec.lock b/example/pubspec.lock index ba15da1..be2b324 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _flutterfire_internals - sha256: "37a42d06068e2fe3deddb2da079a8c4d105f241225ba27b7122b37e9865fd8f7" + sha256: b46f62516902afb04befa4b30eb6a12ac1f58ca8cb25fb9d632407259555dd3d url: "https://pub.dev" source: hosted - version: "1.3.35" + version: "1.3.39" args: dependency: transitive description: @@ -103,7 +103,7 @@ packages: path: "../package" relative: true source: path - version: "0.5.4" + version: "0.5.5" event_bus: dependency: transitive description: @@ -140,50 +140,50 @@ packages: dependency: "direct main" description: name: firebase_core - sha256: "26de145bb9688a90962faec6f838247377b0b0d32cc0abecd9a4e43525fc856c" + sha256: "5159984ce9b70727473eb388394650677c02c925aaa6c9439905e1f30966a4d5" url: "https://pub.dev" source: hosted - version: "2.32.0" + version: "3.2.0" firebase_core_platform_interface: dependency: transitive description: name: firebase_core_platform_interface - sha256: c437ae5d17e6b5cc7981cf6fd458a5db4d12979905f9aafd1fea930428a9fe63 + sha256: "1003a5a03a61fc9a22ef49f37cbcb9e46c86313a7b2e7029b9390cf8c6fc32cb" url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "5.1.0" firebase_core_web: dependency: transitive description: name: firebase_core_web - sha256: "43d9e951ac52b87ae9cc38ecdcca1e8fa7b52a1dd26a96085ba41ce5108db8e9" + sha256: "23509cb3cddfb3c910c143279ac3f07f06d3120f7d835e4a5d4b42558e978712" url: "https://pub.dev" source: hosted - version: "2.17.0" + version: "2.17.3" firebase_messaging: dependency: "direct main" description: name: firebase_messaging - sha256: a1662cc95d9750a324ad9df349b873360af6f11414902021f130c68ec02267c4 + sha256: "156c4292aa63a6a7d508c68ded984cb38730d2823c3265e573cb1e94983e2025" url: "https://pub.dev" source: hosted - version: "14.9.4" + version: "15.0.3" firebase_messaging_platform_interface: dependency: transitive description: name: firebase_messaging_platform_interface - sha256: "87c4a922cb6f811cfb7a889bdbb3622702443c52a0271636cbc90d813ceac147" + sha256: "10408c5ca242b7fc632dd5eab4caf8fdf18ebe88db6052980fa71a18d88bd200" url: "https://pub.dev" source: hosted - version: "4.5.37" + version: "4.5.41" firebase_messaging_web: dependency: transitive description: name: firebase_messaging_web - sha256: "0d34dca01a7b103ed7f20138bffbb28eb0e61a677bf9e78a028a932e2c7322d5" + sha256: c7a756e3750679407948de665735e69a368cb902940466e5d68a00ea7aba1aaa url: "https://pub.dev" source: hosted - version: "3.8.7" + version: "3.8.11" flutter: dependency: "direct main" description: flutter diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 8b5d9fd..3509fad 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -37,9 +37,9 @@ dependencies: cupertino_icons: ^1.0.6 provider: ^6.1.2 package_info_plus: ^4.2.0 - firebase_core: ^2.30.1 - firebase_messaging: ^14.9.1 - dito_sdk: ^0.5.1 + firebase_core: ^3.1.1 + firebase_messaging: ^15.0.2 + dito_sdk: ^0.5.4 dev_dependencies: flutter_test: diff --git a/package/lib/data/database.dart b/package/lib/data/database.dart new file mode 100644 index 0000000..0db1e2f --- /dev/null +++ b/package/lib/data/database.dart @@ -0,0 +1,142 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:sqflite_common_ffi/sqflite_ffi.dart'; + +/// EventDatabaseService is a singleton class that provides methods to interact with a SQLite database +/// for storing and managing events. +class LocalDatabase { + static const String _dbName = 'dito-offline.db'; + static Database? _database; + final tables = {"notification": "notification", "events": "events"}; + + static final LocalDatabase _instance = LocalDatabase._internal(); + + /// Factory constructor to return the singleton instance of EventDatabaseService. + factory LocalDatabase() { + return _instance; + } + + /// Private named constructor for internal initialization of singleton instance. + LocalDatabase._internal(); + + /// Getter for the database instance. + /// Initializes the database if it is not already initialized. + /// Returns a Future that completes with the database instance. + Future get database async { + if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) { + databaseFactoryOrNull = null; + sqfliteFfiInit(); + databaseFactory = databaseFactoryFfi; + } + + if (_database != null) return _database!; + + _database = await _initDatabase(); + return _database!; + } + + /// Private method to initialize the database. + /// Sets up the path and opens the database, creating it if it does not exist. + /// Returns a Future that completes with the database instance. + Future _initDatabase() async { + try { + final databasePath = await getDatabasesPath(); + final String path = '$databasePath/$_dbName'; + + return await openDatabase( + path, + version: 2, + onCreate: _createTable, + ); + } catch (e) { + if (kDebugMode) { + print('Error initializing database: $e'); + } + rethrow; + } + } + + /// Callback method to create the events table when the database is first created. + /// + /// [db] - The database instance. + /// [version] - The version of the database. + FutureOr _createTable(Database db, int version) async { + try { + await db.execute(''' + CREATE TABLE events ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + eventName TEXT, + eventMoment TEXT, + revenue REAL, + customData TEXT + ); + '''); + await db.execute(''' + CREATE TABLE notification ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + event TEXT, + token TEXT + ); + '''); + } catch (e) { + if (kDebugMode) { + print('Error creating table: $e'); + } + rethrow; + } + } + + Future insert(String table, Map values) async { + final db = await database; + return db.insert(table, values); + } + + Future delete( + String table, String where, List whereArgs) async { + try { + final db = await database; + return await db.delete( + table, + where: where, + whereArgs: whereArgs, + ); + } catch (e) { + if (kDebugMode) { + print('Error deleting event: $e'); + } + rethrow; + } + } + + /// Method to retrieve all events from the events table. + /// Returns a Future that completes with a list of Map objects. + Future>> fetchAll(String tableName) async { + final db = await database; + return db.query(tableName); + } + + /// Method to clear all events from the events table. + /// Returns a Future that completes with the number of rows deleted. + Future clearDatabase(String tableName) async { + try { + final db = await database; + await db.delete(tableName); + } catch (e) { + if (kDebugMode) { + print('Error clearing database: $e'); + } + rethrow; + } + } + + /// Method to close the database. + /// Should be called when the database is no longer needed. + Future closeDatabase() async { + if (_database != null) { + await _database!.close(); + _database = null; + } + } +} diff --git a/package/lib/data/dito_api.dart b/package/lib/data/dito_api.dart index f2327db..784ce36 100644 --- a/package/lib/data/dito_api.dart +++ b/package/lib/data/dito_api.dart @@ -53,6 +53,22 @@ class DitoApi { ); } + Future _put(String url, String path, + {Map? queryParameters, Map? body}) { + _checkConfiguration(); + + final uri = Uri.https(url, path, queryParameters); + + return http.put( + uri, + body: body, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'User-Agent': _platform, + }, + ); + } + Future identify(UserEntity user) async { final queryParameters = { 'user_data': jsonEncode(user.toJson()), @@ -66,6 +82,21 @@ class DitoApi { return await _post(url, path, queryParameters: queryParameters); } + Future updateUserData(UserEntity user) async { + final queryParameters = { + 'user_data': jsonEncode(user.toJson()), + }; + + queryParameters.addAll(_assign); + + const url = 'login.plataformasocial.com.br'; + final path = 'users/${user.id}'; + + return await _put(url, path, + queryParameters: queryParameters, + body: {'user_data': jsonEncode(user.toJson())}); + } + Future trackEvent(EventEntity event, UserEntity user) async { final body = { 'id_type': 'id', diff --git a/package/lib/data/event_database.dart b/package/lib/data/event_database.dart index b0bb8a0..f80dd83 100644 --- a/package/lib/data/event_database.dart +++ b/package/lib/data/event_database.dart @@ -1,17 +1,12 @@ -import 'dart:io'; - import 'package:flutter/foundation.dart'; -import 'package:sqflite_common_ffi/sqflite_ffi.dart'; import '../event/event_entity.dart'; +import 'database.dart'; /// EventDatabaseService is a singleton class that provides methods to interact with a SQLite database /// for storing and managing events. class EventDatabase { - static const String _dbName = 'ditoSDK.db'; - static const String _tableName = 'events'; - static Database? _database; - + static final LocalDatabase _database = LocalDatabase(); static final EventDatabase _instance = EventDatabase._internal(); /// Factory constructor to return the singleton instance of EventDatabaseService. @@ -22,74 +17,15 @@ class EventDatabase { /// Private named constructor for internal initialization of singleton instance. EventDatabase._internal(); - /// Getter for the database instance. - /// Initializes the database if it is not already initialized. - /// Returns a Future that completes with the database instance. - Future get database async { - if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) { - databaseFactoryOrNull = null; - sqfliteFfiInit(); - databaseFactory = databaseFactoryFfi; - } - - if (_database != null) return _database!; - - _database = await _initDatabase(); - return _database!; - } - - /// Private method to initialize the database. - /// Sets up the path and opens the database, creating it if it does not exist. - /// Returns a Future that completes with the database instance. - Future _initDatabase() async { - try { - final databasePath = await getDatabasesPath(); - final String path = '$databasePath/$_dbName'; - - return await openDatabase( - path, - version: 1, - onCreate: _createTable, - ); - } catch (e) { - if (kDebugMode) { - print('Error initializing database: $e'); - } - rethrow; - } - } - - /// Callback method to create the events table when the database is first created. - /// - /// [db] - The database instance. - /// [version] - The version of the database. - Future _createTable(Database db, int version) async { - try { - await db.execute(''' - CREATE TABLE $_tableName ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - eventName TEXT, - eventMoment TEXT, - revenue REAL, - customData TEXT - ) - '''); - } catch (e) { - if (kDebugMode) { - print('Error creating table: $e'); - } - rethrow; - } - } - /// Method to insert a new event into the events table. /// /// [event] - The EventEntity object to be inserted. /// Returns a Future that completes with the row id of the inserted event. Future create(EventEntity event) async { try { - final db = await database; - return await db.insert(_tableName, event.toMap()) > 0; + return await _database.insert( + _database.tables["events"]!, event.toMap()) > + 0; } catch (e) { if (kDebugMode) { print('Error inserting event: $e'); @@ -104,11 +40,10 @@ class EventDatabase { /// Returns a Future that completes with the number of rows deleted. Future delete(EventEntity event) async { try { - final db = await database; - return await db.delete( - _tableName, - where: 'eventName = ? AND eventMoment = ?', - whereArgs: [event.eventName, event.eventMoment], + return await _database.delete( + _database.tables["events"]!, + 'eventName = ? AND eventMoment = ?', + [event], ) > 0; } catch (e) { @@ -120,13 +55,11 @@ class EventDatabase { } /// Method to retrieve all events from the events table. - /// Returns a Future that completes with a list of EventEntity objects. - Future> fetchAll() async { + /// Returns a Future that completes with a list of Map objects. + Future> fetchAll() async { try { - final db = await database; - final List> maps = await db.query(_tableName); - - return maps.map((map) => EventEntity.fromMap(map)).toList(); + final maps = await _database.fetchAll(_database.tables["events"]!); + return maps.map((map) => EventEntity.fromMap(map)); } catch (e) { if (kDebugMode) { print('Error retrieving events: $e'); @@ -139,8 +72,7 @@ class EventDatabase { /// Returns a Future that completes with the number of rows deleted. Future clearDatabase() async { try { - final db = await database; - await db.delete(_tableName); + return _database.clearDatabase(_database.tables["events"]!); } catch (e) { if (kDebugMode) { print('Error clearing database: $e'); @@ -151,10 +83,5 @@ class EventDatabase { /// Method to close the database. /// Should be called when the database is no longer needed. - Future closeDatabase() async { - if (_database != null) { - await _database!.close(); - _database = null; - } - } + Future closeDatabase() => _database.closeDatabase(); } diff --git a/package/lib/data/notification_database.dart b/package/lib/data/notification_database.dart new file mode 100644 index 0000000..188dad3 --- /dev/null +++ b/package/lib/data/notification_database.dart @@ -0,0 +1,85 @@ +import 'package:flutter/foundation.dart'; + +import 'database.dart'; + +/// EventDatabaseService is a singleton class that provides methods to interact with a SQLite database +/// for storing and managing notification. +class NotificationEvent { + static final LocalDatabase _database = LocalDatabase(); + static final NotificationEvent _instance = NotificationEvent._internal(); + + /// Factory constructor to return the singleton instance of EventDatabaseService. + factory NotificationEvent() { + return _instance; + } + + /// Private named constructor for internal initialization of singleton instance. + NotificationEvent._internal(); + + /// Method to insert a new event into the notification table. + /// + /// [event] - The event name to be inserted. + /// Returns a Future that completes with the row id of the inserted event. + Future create(String event, String token) async { + try { + return await _database.insert(_database.tables["notification"]!, + {'event': event, 'token': token}) > + 0; + } catch (e) { + if (kDebugMode) { + print('Error inserting event: $e'); + } + rethrow; + } + } + + /// Method to delete an event from the notification table. + /// + /// [event] - The event name to be deleted. + /// Returns a Future that completes with the number of rows deleted. + Future delete(String event) async { + try { + return await _database.delete( + _database.tables["notification"]!, + 'event = ?', + [event], + ) > + 0; + } catch (e) { + if (kDebugMode) { + print('Error deleting event: $e'); + } + rethrow; + } + } + + /// Method to retrieve all notification from the notification table. + /// Returns a Future that completes with a list of Map objects. + Future>> fetchAll() async { + try { + return await _database.fetchAll(_database.tables["notification"]!); + } catch (e) { + if (kDebugMode) { + print('Error retrieving notification: $e'); + } + rethrow; + } + } + + /// Method to clear all notification from the notification table. + /// Returns a Future that completes with the number of rows deleted. + Future clearDatabase() async { + try { + return _database.clearDatabase(_database.tables["notification"]!); + } catch (e) { + if (kDebugMode) { + print('Error clearing database: $e'); + } + rethrow; + } + } + + /// Method to close the database. + /// Should be called when the database is no longer needed. + Future closeDatabase() => _database.closeDatabase(); +} diff --git a/package/lib/dito_sdk.dart b/package/lib/dito_sdk.dart index 8a96e56..2fff3b3 100644 --- a/package/lib/dito_sdk.dart +++ b/package/lib/dito_sdk.dart @@ -1,16 +1,11 @@ library dito_sdk; -import 'package:dito_sdk/notification/notification_entity.dart'; -import 'package:dito_sdk/notification/notification_events.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; -import 'package:http/http.dart' as http; import 'data/dito_api.dart'; -import 'event/event_entity.dart'; import 'event/event_interface.dart'; import 'notification/notification_interface.dart'; -import 'user/user_entity.dart'; import 'user/user_interface.dart'; /// DitoSDK is a singleton class that provides various methods to interact with Dito API @@ -20,7 +15,6 @@ class DitoSDK { final UserInterface _userInterface = UserInterface(); final EventInterface _eventInterface = EventInterface(); final NotificationInterface _notificationInterface = NotificationInterface(); - final NotificationEvents _notificationEvents = NotificationEvents(); static final DitoSDK _instance = DitoSDK._internal(); @@ -34,6 +28,14 @@ class DitoSDK { /// Returns an instance of UserInterface class. UserInterface get user => _userInterface; + /// This get method provides an interface for communication with a Notifications. + /// Returns an instance of NotificationInterface class. + NotificationInterface get notification => _notificationInterface; + + /// This get method provides an interface for communication with a Event. + /// Returns an instance of EventInterface class. + EventInterface get event => _eventInterface; + /// This method initializes the SDK with the provided API key and secret key. /// It also initializes the NotificationService and assigns API key and SHA1 signature. /// @@ -48,54 +50,6 @@ class DitoSDK { await _notificationInterface.initialize(); } - void setOnMessageClick(Function(DataPayload) onMessageClicked) { - _notificationEvents.stream.on().listen((event) { - onMessageClicked(event.data); - }); - } - - /// This method enables saving and sending user data to the Dito API. - /// - /// [user] - UserEntity object. - /// Returns a boolean indicating success. - Future identify(UserEntity user) async { - final result = await _userInterface.identify(user); - return result; - } - - /// This method tracks an event with optional revenue and custom data. - /// - /// [eventName] - The name of the event. - /// [revenue] - Optional revenue associated with the event. - /// [customData] - Optional custom data associated with the event. - /// Returns a bool. - Future trackEvent({ - required String eventName, - double? revenue, - Map? customData, - }) async { - final event = EventEntity( - eventName: eventName, customData: customData, revenue: revenue); - - return await _eventInterface.trackEvent(event); - } - - /// This method registers a mobile token for push notifications. - /// - /// [token] - The mobile token to be registered. - /// Returns an http.Response. - Future registryToken({String? token}) async { - return await _notificationInterface.registryToken(token); - } - - /// This method removes a mobile token from the push notification service. - /// - /// [token] - The mobile token to be removed. - /// Returns an http.Response. - Future removeToken({String? token}) async { - return await _notificationInterface.removeToken(token); - } - /// This method is a handler for manage messages in the background. /// It initializes Firebase and Dito, then push the message. Future onBackgroundMessageHandler(RemoteMessage message, diff --git a/package/lib/event/event_entity.dart b/package/lib/event/event_entity.dart index 8e83249..b646b53 100644 --- a/package/lib/event/event_entity.dart +++ b/package/lib/event/event_entity.dart @@ -18,10 +18,8 @@ class EventEntity { eventName: map['eventName'], revenue: map['revenue'], eventMoment: map['eventMoment'], - customData: map['customData'] != null - ? (json.decode(map['customData']) as Map) - .map((key, value) => MapEntry(key, value as String)) - : null, + customData: + map['customData'] != null ? json.decode(map['customData']) : null, ); } @@ -34,10 +32,16 @@ class EventEntity { }; } - Map toJson() => { - 'action': eventName, - 'revenue': revenue, - 'data': customData, - 'created_at': eventMoment - }; + Map toJson() { + final json = { + 'action': eventName, + 'revenue': revenue, + 'data': customData, + 'created_at': eventMoment + }; + + json.removeWhere((key, value) => value == null); + + return json; + } } diff --git a/package/lib/event/event_interface.dart b/package/lib/event/event_interface.dart index c41dd54..dcc307a 100644 --- a/package/lib/event/event_interface.dart +++ b/package/lib/event/event_interface.dart @@ -18,7 +18,7 @@ interface class EventInterface { DateTime utcDateTime = localDateTime.toUtc(); String eventMoment = utcDateTime.toIso8601String(); - event.eventMoment = eventMoment; + event.eventMoment ??= eventMoment; final version = await customDataVersion; if (event.customData == null) { @@ -35,25 +35,4 @@ interface class EventInterface { return false; } } - - /// Verifies and processes any pending events. - /// - /// Throws an exception if the user is not valid. - Future verifyPendingEvents() async { - try { - final events = await _repository.fetchPendingEvents(); - - if (events.isNotEmpty) { - for (final event in events) { - await trackEvent(event); - } - await _repository.clearEvents(); - } - } catch (e) { - if (kDebugMode) { - print('Error verifying pending events: $e'); - } - rethrow; - } - } } diff --git a/package/lib/event/event_repository.dart b/package/lib/event/event_repository.dart index cb4779f..b918fa9 100644 --- a/package/lib/event/event_repository.dart +++ b/package/lib/event/event_repository.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:dito_sdk/user/user_repository.dart'; +import 'package:flutter/foundation.dart'; import '../data/dito_api.dart'; import '../data/event_database.dart'; @@ -32,17 +33,23 @@ class EventRepository { .catchError((e) => false); } - /// Fetches all pending events from the local database. + /// Verifies and processes any pending events. /// - /// Returns a Future that completes with a list of EventEntity objects. - Future> fetchPendingEvents() async { - return await _database.fetchAll(); - } + /// Throws an exception if the user is not valid. + Future verifyPendingEvents() async { + try { + final events = await _database.fetchAll(); - /// Clears all events from the local database. - /// - /// Returns a Future that completes when the database has been cleared. - Future clearEvents() async { - return await _database.clearDatabase(); + for (final event in events) { + await trackEvent(event); + } + + await _database.clearDatabase(); + } catch (e) { + if (kDebugMode) { + print('Error verifying pending events: $e'); + } + rethrow; + } } } diff --git a/package/lib/notification/notification_controller.dart b/package/lib/notification/notification_controller.dart index 36a3d37..6c55209 100644 --- a/package/lib/notification/notification_controller.dart +++ b/package/lib/notification/notification_controller.dart @@ -2,11 +2,37 @@ import 'dart:async'; import 'dart:convert'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; + import 'notification_entity.dart'; +import 'notification_repository.dart'; class NotificationController { - late Function(DataPayload) onSelectNotification; late FlutterLocalNotificationsPlugin localNotificationsPlugin; + final NotificationRepository _repository = NotificationRepository(); + Function(DataPayload)? _selectNotification; + + /// Android-specific notification details. + AndroidNotificationDetails androidNotificationDetails = + const AndroidNotificationDetails( + 'dito_notifications', // Notification channel ID. + 'Notifications sent by Dito', // Notification channel name. + channelDescription: + 'Notifications sent by Dito', // Notification channel description. + importance: Importance.max, // Maximum importance level. + priority: Priority.max, // Maximum priority level. + enableVibration: true, // Enable vibration. + ); + + /// iOS-specific notification details. + DarwinNotificationDetails darwinNotificationDetails = + const DarwinNotificationDetails( + presentAlert: true, // Display alert. + presentBadge: true, // Display badge. + presentSound: true, // Play sound. + presentBanner: true, // Display banner. + ); + + NotificationController._internal(); static final NotificationController _instance = NotificationController._internal(); @@ -15,27 +41,13 @@ class NotificationController { return _instance; } - NotificationController._internal(); - - AndroidNotificationDetails androidDetails = const AndroidNotificationDetails( - 'dito_notifications', - 'Notifications sended by Dito', - channelDescription: 'Notifications sended by Dito', - importance: Importance.max, - priority: Priority.max, - enableVibration: true, - ); - - DarwinNotificationDetails iosDetails = const DarwinNotificationDetails( - presentAlert: true, - presentBadge: true, - presentSound: true, - presentBanner: true); - - /// This method initializes localNotificationsPlugin - initialize(Function(DataPayload) onSelectNotification) async { - _instance.onSelectNotification = onSelectNotification; + /// Initializes the local notifications plugin. + /// + /// [onSelectNotification] - Callback function to handle notification selection. + Future initialize(Function(DataPayload) selectNotification) async { + _selectNotification = selectNotification; localNotificationsPlugin = FlutterLocalNotificationsPlugin(); + const android = AndroidInitializationSettings('@mipmap/ic_launcher'); const ios = DarwinInitializationSettings(); @@ -48,11 +60,12 @@ class NotificationController { await _setupAndroidChannel(); } - _setupAndroidChannel() async { + /// Sets up the Android notification channel. + Future _setupAndroidChannel() async { const AndroidNotificationChannel channel = AndroidNotificationChannel( - 'dito_notifications', - 'Notifications sended by Dito', - importance: Importance.max, + 'dito_notifications', // Channel ID. + 'Notifications sent by Dito', // Channel name. + importance: Importance.max, // Maximum importance level. ); await localNotificationsPlugin @@ -61,29 +74,33 @@ class NotificationController { ?.createNotificationChannel(channel); } - /// This method uses local notifications plugin to show messages on the screen + /// Displays a notification. /// - /// [notification] - NotificationEntity object - showNotification(NotificationEntity notification) { + /// [notification] - NotificationEntity object containing notification details. + void showNotification(NotificationEntity notification) async { localNotificationsPlugin.show( - notification.id, - notification.title, - notification.body, - NotificationDetails(android: androidDetails, iOS: iosDetails), - payload: jsonEncode(notification.payload?.toJson()), + notification.id, // Notification ID. + notification.title, // Notification title. + notification.body, // Notification body. + NotificationDetails( + android: androidNotificationDetails, iOS: darwinNotificationDetails), + payload: + jsonEncode(notification.payload?.toJson()), // Notification payload. ); } - /// This method is called when user clicks on the notification + /// Handles notification selection by the user. /// - /// [response] - NotificationResponse object + /// [response] - NotificationResponse object containing response details. Future onTapNotification(NotificationResponse? response) async { final payload = response?.payload; if (payload != null && payload.isNotEmpty) { - final data = DataPayload.fromJson(jsonDecode(payload)); + final data = DataPayload.fromPayload(jsonDecode(payload)); + + await _repository.notifyOpenDeepLink(data.notification); - onSelectNotification(data); + if (_selectNotification != null) _selectNotification!(data); } } } diff --git a/package/lib/notification/notification_entity.dart b/package/lib/notification/notification_entity.dart index 69e4632..94dea79 100644 --- a/package/lib/notification/notification_entity.dart +++ b/package/lib/notification/notification_entity.dart @@ -1,22 +1,25 @@ +import 'dart:io'; + class Details { final String? link; - final String message; final String? title; + final String message; + String? image; - Details(this.link, this.message, this.title); + Details(this.title, this.message, this.link, this.image); factory Details.fromJson(dynamic json) { assert(json is Map); - return Details(json["link"], json["message"], json["title"]); + return Details(json["title"], json["message"], json["link"], json["image"]); } Map toJson() => - {'link': link, 'message': message, 'title': title}; + {'link': link, 'message': message, 'title': title, 'image': image}; } class DataPayload { - final String reference; - final String identifier; + final String? reference; + final String? identifier; final String? notification; final String? notification_log_id; final Details details; @@ -24,16 +27,43 @@ class DataPayload { DataPayload(this.reference, this.identifier, this.notification, this.notification_log_id, this.details); - factory DataPayload.fromJson(dynamic json) { + factory DataPayload.fromMap(dynamic json) { + final String? image; + assert(json is Map); + + if (Platform.isAndroid) { + image = json["notification"]?["android"]?["imageUrl"]; + } else { + image = json["notification"]?["apple"]?["imageUrl"]; + } + + final String title = + json["notification"]?["title"] ?? json["data"]["title"]; + final String message = + json["notification"]?["body"] ?? json["data"]["message"]; + final String link = json["data"]["link"]; + + final Details details = Details(title, message, link, image); + + return DataPayload( + json["data"]["reference"], + json["data"]["user_id"], + json["data"]["notification"], + json["data"]["notification_log_id"], + details, + ); + } + + factory DataPayload.fromPayload(dynamic json) { assert(json is Map); return DataPayload( - json["reference"], - // removendo json["identifier"] por causa do erro - json["notification_log_id"], - json["notification"], - json["notification_log_id"], - Details.fromJson(json["details"])); + json["reference"], + json["identifier"], + json["notification_log_id"], + json["notification"], + Details.fromJson(json["details"]), + ); } Map toJson() => { @@ -41,20 +71,25 @@ class DataPayload { 'identifier': identifier, 'notification': notification, 'notification_log_id': notification_log_id, - 'details': details.toJson(), + 'details': details.toJson() }; } class NotificationEntity { - final int id; - final String title; - final String body; - final DataPayload? payload; + int id; + String title; + String body; + String? notificationId; + String? image; + + DataPayload? payload; NotificationEntity({ required this.id, required this.title, required this.body, + this.notificationId, + this.image, this.payload, }); } diff --git a/package/lib/notification/notification_interface.dart b/package/lib/notification/notification_interface.dart index 39ce9ad..9d0e17c 100644 --- a/package/lib/notification/notification_interface.dart +++ b/package/lib/notification/notification_interface.dart @@ -1,62 +1,95 @@ import 'dart:async'; -import 'dart:convert'; +import 'dart:io'; -import 'package:dito_sdk/notification/notification_events.dart'; -import 'package:event_bus/event_bus.dart'; -import 'package:flutter/foundation.dart'; +import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter/foundation.dart'; import 'package:package_info_plus/package_info_plus.dart'; import '../data/dito_api.dart'; +import '../user/user_interface.dart'; +import 'notification_controller.dart'; import 'notification_entity.dart'; +import 'notification_events.dart'; import 'notification_repository.dart'; -import 'notification_controller.dart'; /// NotificationInterface is an interface for communication with the notification repository and notification controller class NotificationInterface { + late void Function(DataPayload payload) onMessageClick; final NotificationRepository _repository = NotificationRepository(); final NotificationController _controller = NotificationController(); final NotificationEvents _notificationEvents = NotificationEvents(); final DitoApi _api = DitoApi(); - - /// The broadcast stream for received notifications - final StreamController - _didReceiveLocalNotificationStream = - StreamController.broadcast(); - - /// The broadcast stream for selected notifications - final StreamController _selectNotificationStream = - StreamController.broadcast(); + final UserInterface _userInterface = UserInterface(); /// This method initializes notification controller and notification repository. /// Start listening to notifications Future initialize() async { - await _repository.initializeFirebaseMessaging(onMessage); + await Firebase.initializeApp(); + await FirebaseMessaging.instance.setAutoInitEnabled(true); + + if (Platform.isIOS) { + await FirebaseMessaging.instance + .setForegroundNotificationPresentationOptions( + badge: true, sound: true, alert: true); + } + + FirebaseMessaging.onMessage.listen(onMessage); + + _handleToken(); + await _controller.initialize(onSelectNotification); _listenStream(); } + void _handleToken() async { + _userInterface.data.token = await getFirebaseToken(); + FirebaseMessaging.instance.onTokenRefresh.listen((token) { + final lastToken = _userInterface.data.token; + if (lastToken != token) { + if (lastToken != null && lastToken.isNotEmpty) { + removeToken(lastToken); + } + registryToken(token); + _userInterface.data.token = token; + } + }).onError((err) { + if (kDebugMode) { + print('Error getting token: $err'); + } + }); + } + + /// Gets the current FCM token for the device. + /// + /// Returns the token as a String or null if not available. + Future getFirebaseToken() => FirebaseMessaging.instance.getToken(); + // This method turns off the streams when this class is unmounted void dispose() { - _didReceiveLocalNotificationStream.close(); - _selectNotificationStream.close(); + _repository.didReceiveLocalNotificationStream.close(); + _repository.selectNotificationStream.close(); } // This method initializes the listeners on streams _listenStream() { - _didReceiveLocalNotificationStream.stream - .listen((NotificationEntity receivedNotification) { + _repository.didReceiveLocalNotificationStream.stream + .listen((NotificationEntity receivedNotification) async { _controller.showNotification(receivedNotification); + await notifyReceivedNotification(receivedNotification.notificationId!); }); - _selectNotificationStream.stream.listen((DataPayload data) async { + _repository.selectNotificationStream.stream + .listen((DataPayload data) async { _notificationEvents.stream.fire(MessageClickedEvent(data)); - final notificationId = data.notification; - // Only sends the event if the message is linked to a notification - if (notificationId != null && notificationId.isNotEmpty) { + if (data.notification != null && + data.identifier != null && + data.reference != null) { await _api.openNotification( - notificationId, data.identifier, data.reference); + data.notification!, data.identifier!, data.reference!); + + onMessageClick(data); } }); } @@ -66,34 +99,57 @@ class NotificationInterface { /// /// [message] - RemoteMessage object. Future onMessage(RemoteMessage message) async { - if (message.data["data"] == null) { + if (message.data.isEmpty) { if (kDebugMode) { - print("Data is not defined: ${message.data}"); + print("Data is not defined: $message"); } } - final notification = DataPayload.fromJson(jsonDecode(message.data["data"])); - - final messagingAllowed = await _repository.checkPermissions(); + final notification = DataPayload.fromMap(message.toMap()); + final messagingAllowed = await _checkPermissions(); PackageInfo packageInfo = await PackageInfo.fromPlatform(); final appName = packageInfo.appName; if (messagingAllowed && notification.details.message.isNotEmpty) { - _didReceiveLocalNotificationStream.add((NotificationEntity( + _repository.didReceiveLocalNotificationStream.add((NotificationEntity( id: message.hashCode, + notificationId: notification.notification, title: notification.details.title ?? appName, body: notification.details.message, + image: notification.details.image, payload: notification))); } } + /// This method send a notify received push event to Dito + /// + /// [notificationId] - DataPayload object. + Future notifyReceivedNotification(String notificationId) => + _repository.notifyReceivedNotification(notificationId); + + /// This method send a open unsubscribe from notification event to Dito + Future unsubscribeFromNotifications() => + _repository.unsubscribeFromNotifications(); + + /// This method send a open deeplink event to Dito + Future notifyOpenDeepLink(String notificationId) => + _repository.notifyOpenDeepLink(notificationId); + + /// Requests permission to show notifications. + /// + /// Returns a boolean indicating if permission was granted. + Future _checkPermissions() async { + final settings = await FirebaseMessaging.instance.requestPermission(); + return settings.authorizationStatus == AuthorizationStatus.authorized; + } + /// This method adds a selected notification to stream /// /// [data] - DataPayload object. Future onSelectNotification(DataPayload? data) async { if (data != null) { - _selectNotificationStream.add(data); + _repository.selectNotificationStream.add(data); } } @@ -102,8 +158,7 @@ class NotificationInterface { /// [token] - The mobile token to be registered. /// Returns an http.Response. registryToken(String? token) async { - String? newToken = token; - newToken ??= await _repository.getFirebaseToken(); + String? newToken = token ?? await getFirebaseToken(); if (newToken != null) _repository.registryToken(newToken); } @@ -112,8 +167,7 @@ class NotificationInterface { /// [token] - The mobile token to be removed. /// Returns an http.Response. removeToken(String? token) async { - String? newToken = token; - newToken ??= await _repository.getFirebaseToken(); + String? newToken = token ?? await getFirebaseToken(); if (newToken != null) _repository.removeToken(newToken); } } diff --git a/package/lib/notification/notification_repository.dart b/package/lib/notification/notification_repository.dart index 65f82e2..57aa5a9 100644 --- a/package/lib/notification/notification_repository.dart +++ b/package/lib/notification/notification_repository.dart @@ -1,81 +1,108 @@ import 'dart:async'; -import 'dart:io'; -import 'package:firebase_core/firebase_core.dart'; -import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/foundation.dart'; -import 'package:http/http.dart' as http; import '../data/dito_api.dart'; -import '../user/user_interface.dart'; +import '../data/notification_database.dart'; +import '../event/event_entity.dart'; +import '../event/event_interface.dart'; +import '../user/user_repository.dart'; +import 'notification_entity.dart'; class NotificationRepository { final DitoApi _api = DitoApi(); - final UserInterface _userInterface = UserInterface(); + final _database = NotificationEvent(); + final UserRepository _userRepository = UserRepository(); + final EventInterface _eventInterface = EventInterface(); - /// This method initializes FirebaseMessaging - Future initializeFirebaseMessaging( - Function(RemoteMessage) onMessage) async { - await Firebase.initializeApp(); + /// The broadcast stream for received notifications + final StreamController didReceiveLocalNotificationStream = + StreamController.broadcast(); - await FirebaseMessaging.instance.setAutoInitEnabled(true); + /// The broadcast stream for selected notifications + final StreamController selectNotificationStream = + StreamController.broadcast(); - _handleToken(); - - if (Platform.isIOS) { - await FirebaseMessaging.instance - .setForegroundNotificationPresentationOptions( - badge: true, sound: true, alert: true); - } - - /// Shows the message when FCM payload is received - FirebaseMessaging.onMessage.listen(onMessage); - } + /// Verifies and processes any pending events. + /// + /// Throws an exception if the user is not valid. + Future verifyPendingEvents() async { + try { + final events = await _database.fetchAll(); - void _handleToken() async { - _userInterface.data.token = await getFirebaseToken(); - FirebaseMessaging.instance.onTokenRefresh.listen((token) { - final lastToken = _userInterface.data.token; - if (lastToken != token) { - if (lastToken != null && lastToken.isNotEmpty) { - removeToken(lastToken); + for (final event in events) { + final eventName = event["event"] as String; + final token = event["token"] as String; + switch (eventName) { + case "register-token": + registryToken(token); + break; + case "remove-token": + removeToken(token); + break; + default: + break; } - registryToken(token); - _userInterface.data.token = token; } - }).onError((err) { + + await _database.clearDatabase(); + } catch (e) { if (kDebugMode) { - print('Error getting token.: $err'); + print('Error verifying pending events on notification: $e'); } - }); + rethrow; + } } - /// This method asks for permission to show the notifications. + /// Registers the FCM token with the server. /// - /// Returns a bool. - Future checkPermissions() async { - var settings = await FirebaseMessaging.instance.requestPermission(); - return settings.authorizationStatus == AuthorizationStatus.authorized; + /// [token] - The FCM token to be registered. + /// Returns an http.Response from the server. + Future registryToken(String token) async { + if (_userRepository.data.isNotValid) { + return await _database.create('register-token', token); + } + + return await _api + .registryToken(token, _userRepository.data) + .then((result) => true) + .catchError((e) => false); } - /// This method get the mobile token for push notifications. + /// Removes the FCM token from the server. /// - /// Returns a String or null. - Future getFirebaseToken() => FirebaseMessaging.instance.getToken(); + /// [token] - The FCM token to be removed. + /// Returns an http.Response from the server. + Future removeToken(String token) async { + if (_userRepository.data.isNotValid) { + return await _database.create('remove-token', token); + } - /// This method registers a mobile token for push notifications. - /// - /// [token] - The mobile token to be registered. - /// Returns an http.Response. - Future registryToken(String token) async { - return await _api.registryToken(token, _userInterface.data); + return await _api + .removeToken(token, _userRepository.data) + .then((result) => true) + .catchError((e) => false); } - /// This method removes a mobile token for push notifications. + /// This method send a notify received push event to Dito /// - /// [token] - The mobile token to be removed. - /// Returns an http.Response. - Future removeToken(String token) async { - return await _api.removeToken(token, _userInterface.data); + /// [notification] - DataPayload object. + Future notifyReceivedNotification(String notificationId) async { + await _eventInterface.trackEvent(EventEntity( + eventName: 'received-mobile-push-notification', + customData: {'notification_id': notificationId})); + } + + /// This method send a open unsubscribe from notification event to Dito + Future unsubscribeFromNotifications() async { + await _eventInterface.trackEvent( + EventEntity(eventName: 'unsubscribed-mobile-push-notification')); + } + + /// This method send a open deeplink event to Dito + Future notifyOpenDeepLink(String? notificationId) async { + await _eventInterface.trackEvent(EventEntity( + eventName: 'open-deeplink-mobile-push-notification', + customData: {'notification_id': notificationId})); } } diff --git a/package/lib/user/user_interface.dart b/package/lib/user/user_interface.dart index be2b013..ade4d2c 100644 --- a/package/lib/user/user_interface.dart +++ b/package/lib/user/user_interface.dart @@ -1,16 +1,19 @@ import 'dart:async'; -import 'package:dito_sdk/event/event_interface.dart'; -import 'package:dito_sdk/user/user_entity.dart'; -import 'package:dito_sdk/user/user_repository.dart'; +import 'package:dito_sdk/notification/notification_repository.dart'; import 'package:flutter/foundation.dart'; +import '../event/event_repository.dart'; import '../utils/custom_data.dart'; +import 'user_entity.dart'; +import 'user_repository.dart'; /// UserInterface is an interface for communication with the user repository interface class UserInterface { final UserRepository _repository = UserRepository(); - final EventInterface _eventInterface = EventInterface(); + final EventRepository _eventRepository = EventRepository(); + final NotificationRepository _notificationRepository = + NotificationRepository(); /// Identifies the user by saving their data and sending it to DitoAPI. /// @@ -25,8 +28,10 @@ interface class UserInterface { user.customData?.addAll(version); } - final result = await _repository.identify(user); - await _eventInterface.verifyPendingEvents(); + final result = _repository.identify(user); + + _eventRepository.verifyPendingEvents(); + _notificationRepository.verifyPendingEvents(); return result; } catch (e) { diff --git a/package/pubspec.lock b/package/pubspec.lock index 68a840b..2e30b24 100644 --- a/package/pubspec.lock +++ b/package/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _flutterfire_internals - sha256: "13e611501ef36044655852215b4f30aed81123654a4f55193d0051a0e8705658" + sha256: b46f62516902afb04befa4b30eb6a12ac1f58ca8cb25fb9d632407259555dd3d url: "https://pub.dev" source: hosted - version: "1.3.36" + version: "1.3.39" args: dependency: transitive description: @@ -125,50 +125,50 @@ packages: dependency: "direct main" description: name: firebase_core - sha256: "0d436d29b16fd9844a098ece2a3ce75efc290e5fe0844d282c5e8987173b0d02" + sha256: "5159984ce9b70727473eb388394650677c02c925aaa6c9439905e1f30966a4d5" url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "3.2.0" firebase_core_platform_interface: dependency: transitive description: name: firebase_core_platform_interface - sha256: c437ae5d17e6b5cc7981cf6fd458a5db4d12979905f9aafd1fea930428a9fe63 + sha256: "1003a5a03a61fc9a22ef49f37cbcb9e46c86313a7b2e7029b9390cf8c6fc32cb" url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "5.1.0" firebase_core_web: dependency: transitive description: name: firebase_core_web - sha256: "22fcb352744908224fc7be3caae254836099786acfe5df6e9fe901e9c2575a41" + sha256: "23509cb3cddfb3c910c143279ac3f07f06d3120f7d835e4a5d4b42558e978712" url: "https://pub.dev" source: hosted - version: "2.17.1" + version: "2.17.3" firebase_messaging: dependency: "direct main" description: name: firebase_messaging - sha256: "62fb18daf69ee5f65c0ea5ef4c611481ec50584f54a9d7bcb2de511ad821b45d" + sha256: "156c4292aa63a6a7d508c68ded984cb38730d2823c3265e573cb1e94983e2025" url: "https://pub.dev" source: hosted - version: "15.0.0" + version: "15.0.3" firebase_messaging_platform_interface: dependency: transitive description: name: firebase_messaging_platform_interface - sha256: aec6972698a5f70557b44946923d6be2c0cda127b60b462a1b1f7f08a8a325a5 + sha256: "10408c5ca242b7fc632dd5eab4caf8fdf18ebe88db6052980fa71a18d88bd200" url: "https://pub.dev" source: hosted - version: "4.5.38" + version: "4.5.41" firebase_messaging_web: dependency: transitive description: name: firebase_messaging_web - sha256: "17979bda5f4474b76c069d547294955e775e3c4484ae80f621a06aa27f5ac5d8" + sha256: c7a756e3750679407948de665735e69a368cb902940466e5d68a00ea7aba1aaa url: "https://pub.dev" source: hosted - version: "3.8.8" + version: "3.8.11" flutter: dependency: "direct main" description: flutter diff --git a/package/pubspec.yaml b/package/pubspec.yaml index d3b801e..f5412cf 100644 --- a/package/pubspec.yaml +++ b/package/pubspec.yaml @@ -1,6 +1,6 @@ name: dito_sdk description: A Flutter package by Dito that enables user registration and user event tracking. -version: 0.5.4 +version: 0.5.5 homepage: https://github.com/ditointernet/sdk_mobile_flutter environment: @@ -14,9 +14,9 @@ dependencies: crypto: ">=3.0.1" device_info_plus: ">=9.0.3" sqflite: ">=2.3.3" - firebase_messaging: ">=14.9.1" + firebase_messaging: ">=15.0.1" + firebase_core: ">=3.1.0" flutter_local_notifications: ">=17.1.0" - firebase_core: ">=2.30.1" sqflite_common_ffi: ">=2.3.3" package_info_plus: ">=4.1.0" event_bus: ^2.0.0 diff --git a/package/test/dito_sdk_test.dart b/package/test/dito_sdk_test.dart index f61662c..ceed1df 100644 --- a/package/test/dito_sdk_test.dart +++ b/package/test/dito_sdk_test.dart @@ -1,5 +1,4 @@ import 'package:dito_sdk/dito_sdk.dart'; -import 'package:dito_sdk/user/user_entity.dart'; import 'package:flutter_test/flutter_test.dart'; import 'utils.dart'; diff --git a/package/test/event/database_test.dart b/package/test/event/database_test.dart index e8e4d53..5962266 100644 --- a/package/test/event/database_test.dart +++ b/package/test/event/database_test.dart @@ -1,4 +1,4 @@ -import 'package:dito_sdk/data/event_database.dart'; +import 'package:dito_sdk/data/database.dart'; import 'package:dito_sdk/event/event_entity.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart'; @@ -10,15 +10,14 @@ void main() { }); group('EventDatabaseService Tests', () { - late EventDatabase eventDatabaseService; + final LocalDatabase eventDatabaseService = LocalDatabase(); setUp(() async { - eventDatabaseService = EventDatabase(); await eventDatabaseService.database; }); tearDown(() async { - await eventDatabaseService.clearDatabase(); + await eventDatabaseService.clearDatabase("events"); await eventDatabaseService.closeDatabase(); }); @@ -28,15 +27,15 @@ void main() { eventMoment: '2024-06-01T12:34:56Z', revenue: 100.0, customData: {'key': 'value'}, - ); + ).toMap(); - final success = await eventDatabaseService.create(event); + final success = await eventDatabaseService.insert("events", event); expect(success, true); - final events = await eventDatabaseService.fetchAll(); + final events = await eventDatabaseService.fetchAll("events"); expect(events.length, 1); - expect(events.first.eventName, 'Test Event'); + expect(events.first["eventName"], 'Test Event'); }); test('should delete an event', () async { @@ -45,14 +44,12 @@ void main() { eventMoment: '2024-06-01T12:34:56Z', revenue: 100.0, customData: {'key': 'value'}, - ); + ).toMap(); - await eventDatabaseService.create(event); - final success = await eventDatabaseService.delete(event); + await eventDatabaseService.insert("events", event); + await eventDatabaseService.clearDatabase("events"); - expect(success, true); - - final events = await eventDatabaseService.fetchAll(); + final events = await eventDatabaseService.fetchAll("events"); expect(events.isEmpty, true); }); @@ -62,38 +59,24 @@ void main() { eventMoment: '2024-06-01T12:34:56Z', revenue: 100.0, customData: {'key': 'value1'}, - ); + ).toMap(); final event2 = EventEntity( eventName: 'Test Event 2', eventMoment: '2024-06-02T12:34:56Z', revenue: 200.0, customData: {'key': 'value2'}, - ); + ).toMap(); - await eventDatabaseService.create(event1); - await eventDatabaseService.create(event2); + await eventDatabaseService.insert("events", event1); + await eventDatabaseService.insert("events", event2); - final events = await eventDatabaseService.fetchAll(); + final events = await eventDatabaseService.fetchAll("events"); expect(events.length, 2); - expect(events[0].eventName, 'Test Event 1'); - expect(events[1].eventName, 'Test Event 2'); + expect(events.first["eventName"], 'Test Event 1'); + expect(events.last["eventName"], 'Test Event 2'); }); - test('should clear the database', () async { - final event = EventEntity( - eventName: 'Test Event', - eventMoment: '2024-06-01T12:34:56Z', - revenue: 100.0, - customData: {'key': 'value'}, - ); - - await eventDatabaseService.create(event); - await eventDatabaseService.clearDatabase(); - - final events = await eventDatabaseService.fetchAll(); - expect(events.isEmpty, true); - }); }); } diff --git a/package/test/event/event_test.dart b/package/test/event/event_test.dart index 00dde03..35fd7d1 100644 --- a/package/test/event/event_test.dart +++ b/package/test/event/event_test.dart @@ -1,5 +1,6 @@ -import 'package:dito_sdk/data/event_database.dart'; +import 'package:dito_sdk/data/database.dart'; import 'package:dito_sdk/dito_sdk.dart'; +import 'package:dito_sdk/event/event_entity.dart'; import 'package:dito_sdk/user/user_entity.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart'; @@ -20,29 +21,30 @@ void main() async { }); group('Events: ', () { - late EventDatabase database; + final LocalDatabase database = LocalDatabase(); setUp(() async { - database = EventDatabase(); await database.database; }); tearDown(() async { - await database.clearDatabase(); + await database.clearDatabase("events"); await database.closeDatabase(); }); test('Send event without identify', () async { - await dito.trackEvent(eventName: 'event-test-sdk-flutter'); - final events = await database.fetchAll(); + await dito.event + .trackEvent(EventEntity(eventName: 'event-test-sdk-flutter')); + final events = await database.fetchAll("events"); expect(events.length, 1); - expect(events.first.eventName, 'event-test-sdk-flutter'); + expect(events.first["eventName"], 'event-test-sdk-flutter'); }); test('Send event with identify', () async { dito.user.identify(UserEntity(userID: id, email: "teste@teste.com")); - final result = await dito.trackEvent(eventName: 'event-test-sdk-flutter'); - final events = await database.fetchAll(); + final result = await dito.event + .trackEvent(EventEntity(eventName: 'event-test-sdk-flutter')); + final events = await database.fetchAll("events"); expect(events.length, 0); expect(result, true); @@ -50,13 +52,13 @@ void main() async { test('Send event with custom data', () async { dito.user.identify(UserEntity(userID: id, email: "teste@teste.com")); - final result = await dito.trackEvent( + final result = await dito.event.trackEvent(EventEntity( eventName: 'event-test-sdk-flutter', customData: { "data do ultimo teste": DateTime.now().toIso8601String() }, - revenue: 10); - final events = await database.fetchAll(); + revenue: 10)); + final events = await database.fetchAll("events"); expect(events.length, 0); expect(result, true); diff --git a/pubspec.lock b/pubspec.lock index f2c1697..2048d15 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -73,14 +73,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.0+1" - event_bus: - dependency: "direct main" - description: - name: event_bus - sha256: "44baa799834f4c803921873e7446a2add0f3efa45e101a054b1f0ab9b95f8edc" - url: "https://pub.dev" - source: hosted - version: "2.0.0" file: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index d8cb633..436f1ce 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,5 +5,3 @@ environment: dev_dependencies: melos: ^6.0.0 -dependencies: - event_bus: 2.0.0 From 640594ee42083e1e802fe47051916e90e46a3512 Mon Sep 17 00:00:00 2001 From: Ana Machado Date: Mon, 12 Aug 2024 10:40:03 -0300 Subject: [PATCH 31/36] =?UTF-8?q?[NOT-3216]=20Removendo=20listener=20na=20?= =?UTF-8?q?inicializa=C3=A7=C3=A3o=20em=20background?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package/lib/dito_sdk.dart | 6 ++---- package/lib/notification/notification_interface.dart | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/package/lib/dito_sdk.dart b/package/lib/dito_sdk.dart index 2fff3b3..7fa0671 100644 --- a/package/lib/dito_sdk.dart +++ b/package/lib/dito_sdk.dart @@ -1,6 +1,5 @@ library dito_sdk; -import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'data/dito_api.dart'; @@ -47,7 +46,7 @@ class DitoSDK { /// This method initializes the push notification service using Firebase. Future initializePushNotificationService() async { - await _notificationInterface.initialize(); + await _notificationInterface.initialize(false); } /// This method is a handler for manage messages in the background. @@ -55,8 +54,7 @@ class DitoSDK { Future onBackgroundMessageHandler(RemoteMessage message, {required String apiKey, required String secretKey}) async { _api.setKeys(apiKey, secretKey); - await Firebase.initializeApp(); - await _notificationInterface.initialize(); + await _notificationInterface.initialize(true); return await _notificationInterface.onMessage(message); } } diff --git a/package/lib/notification/notification_interface.dart b/package/lib/notification/notification_interface.dart index 9d0e17c..7393f09 100644 --- a/package/lib/notification/notification_interface.dart +++ b/package/lib/notification/notification_interface.dart @@ -24,7 +24,7 @@ class NotificationInterface { /// This method initializes notification controller and notification repository. /// Start listening to notifications - Future initialize() async { + Future initialize(bool background) async { await Firebase.initializeApp(); await FirebaseMessaging.instance.setAutoInitEnabled(true); @@ -34,7 +34,7 @@ class NotificationInterface { badge: true, sound: true, alert: true); } - FirebaseMessaging.onMessage.listen(onMessage); + if (!background) FirebaseMessaging.onMessage.listen(onMessage); _handleToken(); From 20b6a5dc61792b9fde33583a1277218093a047f0 Mon Sep 17 00:00:00 2001 From: Igor Duarte Date: Mon, 12 Aug 2024 17:12:13 -0300 Subject: [PATCH 32/36] prevent generate double notification Signed-off-by: Igor Duarte --- example/lib/main.dart | 6 ++---- package/lib/dito_sdk.dart | 12 +++++------- .../lib/notification/notification_interface.dart | 16 ++++++++++++++-- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 2869462..e3001c5 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,5 +1,4 @@ import 'package:dito_sdk/dito_sdk.dart'; -import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -8,15 +7,14 @@ import 'app.dart'; import 'constants.dart'; @pragma('vm:entry-point') -Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { +Future _firebaseMessagingBackgroundHandler() async { DitoSDK dito = DitoSDK(); - dito.onBackgroundMessageHandler(message, + dito.onBackgroundPushNotificationHandler( apiKey: Constants.ditoApiKey, secretKey: Constants.ditoSecretKey); } void main() async { WidgetsFlutterBinding.ensureInitialized(); - FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); DitoSDK dito = DitoSDK(); dito.initialize( diff --git a/package/lib/dito_sdk.dart b/package/lib/dito_sdk.dart index 7fa0671..307533a 100644 --- a/package/lib/dito_sdk.dart +++ b/package/lib/dito_sdk.dart @@ -1,7 +1,5 @@ library dito_sdk; -import 'package:firebase_messaging/firebase_messaging.dart'; - import 'data/dito_api.dart'; import 'event/event_interface.dart'; import 'notification/notification_interface.dart'; @@ -46,15 +44,15 @@ class DitoSDK { /// This method initializes the push notification service using Firebase. Future initializePushNotificationService() async { - await _notificationInterface.initialize(false); + await _notificationInterface.initialize(); } /// This method is a handler for manage messages in the background. /// It initializes Firebase and Dito, then push the message. - Future onBackgroundMessageHandler(RemoteMessage message, - {required String apiKey, required String secretKey}) async { + void onBackgroundPushNotificationHandler( + {required String apiKey, required String secretKey}) { _api.setKeys(apiKey, secretKey); - await _notificationInterface.initialize(true); - return await _notificationInterface.onMessage(message); + initializePushNotificationService(); + _notificationInterface.registerBackgroundMessageHandle(); } } diff --git a/package/lib/notification/notification_interface.dart b/package/lib/notification/notification_interface.dart index 7393f09..d806081 100644 --- a/package/lib/notification/notification_interface.dart +++ b/package/lib/notification/notification_interface.dart @@ -21,10 +21,14 @@ class NotificationInterface { final NotificationEvents _notificationEvents = NotificationEvents(); final DitoApi _api = DitoApi(); final UserInterface _userInterface = UserInterface(); + bool initialized = false; + bool backgroundMessageRegistered = false; /// This method initializes notification controller and notification repository. /// Start listening to notifications - Future initialize(bool background) async { + Future initialize() async { + if (initialized) return; + await Firebase.initializeApp(); await FirebaseMessaging.instance.setAutoInitEnabled(true); @@ -34,12 +38,20 @@ class NotificationInterface { badge: true, sound: true, alert: true); } - if (!background) FirebaseMessaging.onMessage.listen(onMessage); + FirebaseMessaging.onMessage.listen(onMessage); _handleToken(); await _controller.initialize(onSelectNotification); _listenStream(); + initialized = true; + } + + void registerBackgroundMessageHandle() { + if (backgroundMessageRegistered) return; + + FirebaseMessaging.onBackgroundMessage(onMessage); + backgroundMessageRegistered = true; } void _handleToken() async { From 6b26c7c991a313a835614105c1c85dc159e1d187 Mon Sep 17 00:00:00 2001 From: Igor Duarte Date: Thu, 15 Aug 2024 10:30:08 -0300 Subject: [PATCH 33/36] prevent generate double notification Signed-off-by: Igor Duarte --- example/lib/main.dart | 11 +++++++++-- .../lib/notification/notification_interface.dart | 15 +++++---------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index e3001c5..efd8640 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,4 +1,6 @@ import 'package:dito_sdk/dito_sdk.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -7,20 +9,25 @@ import 'app.dart'; import 'constants.dart'; @pragma('vm:entry-point') -Future _firebaseMessagingBackgroundHandler() async { +Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { + await Firebase.initializeApp(); DitoSDK dito = DitoSDK(); - dito.onBackgroundPushNotificationHandler( + dito.initialize( apiKey: Constants.ditoApiKey, secretKey: Constants.ditoSecretKey); + dito.onBackgroundPushNotificationHandler(message: message); } void main() async { WidgetsFlutterBinding.ensureInitialized(); + await Firebase.initializeApp(); DitoSDK dito = DitoSDK(); dito.initialize( apiKey: Constants.ditoApiKey, secretKey: Constants.ditoSecretKey); await dito.initializePushNotificationService(); + FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); + dito.notification.onMessageClick = (data) { if (kDebugMode) { print(data.toJson()); diff --git a/package/lib/notification/notification_interface.dart b/package/lib/notification/notification_interface.dart index d806081..ed8bdc1 100644 --- a/package/lib/notification/notification_interface.dart +++ b/package/lib/notification/notification_interface.dart @@ -22,14 +22,16 @@ class NotificationInterface { final DitoApi _api = DitoApi(); final UserInterface _userInterface = UserInterface(); bool initialized = false; - bool backgroundMessageRegistered = false; /// This method initializes notification controller and notification repository. /// Start listening to notifications Future initialize() async { + if (Firebase.apps.isEmpty) { + throw 'Firebase not initialized'; + } + if (initialized) return; - await Firebase.initializeApp(); await FirebaseMessaging.instance.setAutoInitEnabled(true); if (Platform.isIOS) { @@ -47,13 +49,6 @@ class NotificationInterface { initialized = true; } - void registerBackgroundMessageHandle() { - if (backgroundMessageRegistered) return; - - FirebaseMessaging.onBackgroundMessage(onMessage); - backgroundMessageRegistered = true; - } - void _handleToken() async { _userInterface.data.token = await getFirebaseToken(); FirebaseMessaging.instance.onTokenRefresh.listen((token) { @@ -128,7 +123,7 @@ class NotificationInterface { id: message.hashCode, notificationId: notification.notification, title: notification.details.title ?? appName, - body: notification.details.message, + body: "${notification.details.message} local", image: notification.details.image, payload: notification))); } From b8c92efd01d4baf08febf366edbe060010a1005e Mon Sep 17 00:00:00 2001 From: Igor Duarte Date: Thu, 15 Aug 2024 10:30:22 -0300 Subject: [PATCH 34/36] prevent generate double notification Signed-off-by: Igor Duarte --- package/lib/dito_sdk.dart | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/package/lib/dito_sdk.dart b/package/lib/dito_sdk.dart index 307533a..60972b8 100644 --- a/package/lib/dito_sdk.dart +++ b/package/lib/dito_sdk.dart @@ -1,5 +1,7 @@ library dito_sdk; +import 'package:firebase_messaging/firebase_messaging.dart'; + import 'data/dito_api.dart'; import 'event/event_interface.dart'; import 'notification/notification_interface.dart'; @@ -50,9 +52,8 @@ class DitoSDK { /// This method is a handler for manage messages in the background. /// It initializes Firebase and Dito, then push the message. void onBackgroundPushNotificationHandler( - {required String apiKey, required String secretKey}) { - _api.setKeys(apiKey, secretKey); - initializePushNotificationService(); - _notificationInterface.registerBackgroundMessageHandle(); + {required RemoteMessage message}) async { + await initializePushNotificationService(); + await _notificationInterface.onMessage(message); } } From 16c059630fa1705ffab4493936054317cff4fd95 Mon Sep 17 00:00:00 2001 From: anamachadoldo <118830921+anamachadoldo@users.noreply.github.com> Date: Mon, 19 Aug 2024 08:08:22 -0300 Subject: [PATCH 35/36] Update notification_interface.dart --- package/lib/notification/notification_interface.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/lib/notification/notification_interface.dart b/package/lib/notification/notification_interface.dart index ed8bdc1..6b4ffe0 100644 --- a/package/lib/notification/notification_interface.dart +++ b/package/lib/notification/notification_interface.dart @@ -123,7 +123,7 @@ class NotificationInterface { id: message.hashCode, notificationId: notification.notification, title: notification.details.title ?? appName, - body: "${notification.details.message} local", + body: notification.details.message, image: notification.details.image, payload: notification))); } From ca1025145c4006636e6819696c583b79ed254898 Mon Sep 17 00:00:00 2001 From: Igor Gottschalg <119457595+igorgsduarte@users.noreply.github.com> Date: Thu, 10 Oct 2024 09:39:04 -0300 Subject: [PATCH 36/36] WIP: Adding Protobuf API (#20) * feat: adding api consuming protobuf files * feat: changing architecture from api methods * feat: changing architecture from api methods * feat: changing architecture from api methods * fixing sql sintaxe * adding validation if no address was set by user * adjusting click activity * adjusting click payload * adjusting and adding to example received activity * adjusting step create temporary data * fixing event dao * fixing code * removing unncessary line * Ajustes SDK (#22) * fix: merging adjustment * fix: adjustment on unit tests Co-Authored-By: Gabriel Miranda Co-Authored-By: Mariana Machado <91987581+marimloliveira@users.noreply.github.com> --- .gitignore | 7 + example/.env.example | 2 - example/lib/app_form.dart | 199 ++++++- example/lib/constants.dart | 41 -- example/lib/main.dart | 19 +- example/pubspec.lock | 529 ------------------ example/pubspec.yaml | 5 +- package/Makefile | 10 + package/buf.gen.yaml | 8 + package/buf.lock | 2 + package/buf.yaml | 11 + package/lib/api/dito_api_interface.dart | 381 +++++++++++++ package/lib/data/database.dart | 44 +- package/lib/data/dito_api.dart | 173 ------ package/lib/data/event_database.dart | 87 --- package/lib/dito_sdk.dart | 16 +- package/lib/event/event_dao.dart | 122 ++++ package/lib/event/event_entity.dart | 31 +- package/lib/event/event_interface.dart | 90 ++- package/lib/event/event_repository.dart | 114 +++- package/lib/event/navigation_entity.dart | 30 + .../notification/notification_controller.dart | 34 +- .../lib/notification/notification_entity.dart | 94 ++-- .../lib/notification/notification_events.dart | 6 +- .../notification/notification_interface.dart | 208 +++---- .../notification/notification_repository.dart | 114 ++-- package/lib/user/address_entity.dart | 29 + package/lib/user/token_repository.dart | 162 ++++++ .../user_dao.dart} | 62 +- package/lib/user/user_entity.dart | 24 +- package/lib/user/user_interface.dart | 108 +++- package/lib/user/user_repository.dart | 81 ++- package/lib/utils/custom_data.dart | 9 +- package/lib/utils/logger.dart | 7 + package/pubspec.lock | 498 ----------------- package/pubspec.yaml | 7 +- package/test/event/database_test.dart | 69 +-- package/test/event/event_test.dart | 33 +- package/test/user/user_test.dart | 28 +- package/test/utils.dart | 1 + pubspec.lock | 325 ----------- 41 files changed, 1701 insertions(+), 2119 deletions(-) delete mode 100644 example/.env.example delete mode 100644 example/lib/constants.dart delete mode 100644 example/pubspec.lock create mode 100644 package/Makefile create mode 100644 package/buf.gen.yaml create mode 100644 package/buf.lock create mode 100644 package/buf.yaml create mode 100644 package/lib/api/dito_api_interface.dart delete mode 100644 package/lib/data/dito_api.dart delete mode 100644 package/lib/data/event_database.dart create mode 100644 package/lib/event/event_dao.dart create mode 100644 package/lib/event/navigation_entity.dart create mode 100644 package/lib/user/address_entity.dart create mode 100644 package/lib/user/token_repository.dart rename package/lib/{data/notification_database.dart => user/user_dao.dart} (57%) create mode 100644 package/lib/utils/logger.dart delete mode 100644 package/pubspec.lock delete mode 100644 pubspec.lock diff --git a/.gitignore b/.gitignore index 03ba70f..c179e32 100644 --- a/.gitignore +++ b/.gitignore @@ -28,9 +28,16 @@ node_modules **/.dart_tool/ example/build/ package/build/ +package/proto/ +package/lib/proto/ .flutter-plugins* package/test/.env-test.json /example/ios/_GoogleService-Info.plist /build/ /example/lib/firebase_options.dart /example/ios/Podfile.lock + +# Lockfiles +pubspec.lock +example/pubspec.lock +package/pubspec.lock diff --git a/example/.env.example b/example/.env.example deleted file mode 100644 index 97200d5..0000000 --- a/example/.env.example +++ /dev/null @@ -1,2 +0,0 @@ -API_KEY= -SECRET_KEY= diff --git a/example/lib/app_form.dart b/example/lib/app_form.dart index 5384ce7..825d236 100644 --- a/example/lib/app_form.dart +++ b/example/lib/app_form.dart @@ -1,6 +1,4 @@ import 'package:dito_sdk/dito_sdk.dart'; -import 'package:dito_sdk/event/event_entity.dart'; -import 'package:dito_sdk/user/user_entity.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -23,41 +21,174 @@ class AppFormState extends State { String cpf = "33333333333"; String email = "teste.sdk-flutter@dito.com.br"; - identify() async { - final user = UserEntity( - userID: "e400c65b1800bee5bf546c5b7bd37cd4f7452bb8", + identify() { + return dito.user.identify( + userID: "1575213826e164f73d28c4ed1b5fabaad4bd4a13", cpf: cpf, - name: 'Teste SDK Flutter 33333333333', + name: 'Usuário SDK FLutter - Teste', email: email); + } - await dito.user.identify(user); - await dito.notification - .registryToken(await dito.notification.getFirebaseToken()); + login() { + return dito.user + .login(userID: '1575213826e164f73d28c4ed1b5fabaad4bd4a13'); } handleIdentify() async { if (_formKey.currentState!.validate()) { - await identify().then((response) => { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Usuário identificado')), - ) - }); + final bool response = await identify(); + + if (response) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Usuário identificado')), + ); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Ocorreu um erro!'), + backgroundColor: Colors.redAccent), + ); + } + } + } + + handleLogin() async { + if (_formKey.currentState!.validate()) { + final bool response = await login(); + + if (response) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Usuário logado')), + ); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Ocorreu um erro!'), + backgroundColor: Colors.redAccent), + ); + } } } - handleNotification() async { + handleGenericTrack() async { if (_formKey.currentState!.validate()) { - await dito.event.trackEvent(EventEntity(eventName: 'action-test')); + final bool response = await dito.event.track(action: 'action-test'); + + if (response) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Evento de notificação solicitado')), + ); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Ocorreu um erro!'), + backgroundColor: Colors.redAccent), + ); + } + } + } + + handleNavigation() async { + if (_formKey.currentState!.validate()) { + final bool response = await dito.event.navigate(name: 'home'); + + if (response) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Evento de notificação solicitado')), + ); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Ocorreu um erro!'), + backgroundColor: Colors.redAccent), + ); + } + } + } + handleClickNotification() async { + if (_formKey.currentState!.validate()) { + final bool response = await dito.notification.click( + notification: 'notification-sdk-test', + ); + + if (response) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Evento de notificação solicitado')), + ); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Ocorreu um erro!'), + backgroundColor: Colors.redAccent), + ); + } + } + } + + handleReceivedNotification() async { + if (_formKey.currentState!.validate()) { + final bool response = await dito.notification.received( + notification: 'notification-sdk-test', + ); + + if (response) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Evento de notificação solicitado')), + ); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Ocorreu um erro!'), + backgroundColor: Colors.redAccent), + ); + } + } + } + + handleDeleteToken() async { + final bool response = await dito.user.token.removeToken(dito.user.data.token); + if (response) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Evento de notificação solicitado')), ); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Ocorreu um erro!'), + backgroundColor: Colors.redAccent), + ); } } - handleDeleteToken() async { - await dito.notification - .removeToken(await dito.notification.getFirebaseToken()); + handleRegistryToken() async { + final bool response = await dito.user.token.registryToken(dito.user.data.token); + if (response) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Evento de notificação solicitado')), + ); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Ocorreu um erro!'), + backgroundColor: Colors.redAccent), + ); + } + } + + handlePingToken() async { + final bool response = await dito.user.token.pingToken(dito.user.data.token); + if (response) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Evento de notificação solicitado')), + ); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Ocorreu um erro!'), + backgroundColor: Colors.redAccent), + ); + } } return Form( @@ -95,16 +226,40 @@ class AppFormState extends State { child: Column(children: [ FilledButton( onPressed: handleIdentify, - child: const Text('Registrar Identify'), + child: const Text('Alterar cadastro'), + ), + FilledButton( + onPressed: handleLogin, + child: const Text('Logar usuário'), + ), + OutlinedButton( + onPressed: handleGenericTrack, + child: const Text('Registrar evento genérico'), + ), + OutlinedButton( + onPressed: handleNavigation, + child: const Text('Registrar evento de navegação'), ), OutlinedButton( - onPressed: handleNotification, - child: const Text('Receber Notification'), + onPressed: handleClickNotification, + child: const Text('Registrar evento de click'), ), OutlinedButton( + onPressed: handleReceivedNotification, + child: const Text('Registrar evento de Entrega'), + ), + FilledButton( + onPressed: handleRegistryToken, + child: const Text('Registrar token'), + ), + FilledButton( onPressed: handleDeleteToken, child: const Text('Deletar token'), ), + FilledButton( + onPressed: handlePingToken, + child: const Text('Validar token'), + ), ]))) ], ), diff --git a/example/lib/constants.dart b/example/lib/constants.dart deleted file mode 100644 index 8871074..0000000 --- a/example/lib/constants.dart +++ /dev/null @@ -1,41 +0,0 @@ -abstract class Constants { - static const String ditoApiKey = String.fromEnvironment( - 'API_KEY', - defaultValue: '', - ); - - static const String ditoSecretKey = String.fromEnvironment( - 'SECRET_KEY', - defaultValue: '', - ); - - static const String firebaseAndroidApKey = String.fromEnvironment( - 'ANDROID_FIREBASE_APP_KEY', - defaultValue: '', - ); - - static const String firebaseAndroidAppID = String.fromEnvironment( - 'FIREBASE_MESSAGE_SENDER_ID', - defaultValue: '', - ); - - static const String firebaseMessageID = String.fromEnvironment( - 'ANDROID_FIREBASE_APP_ID', - defaultValue: '', - ); - - static const String firebaseProjectID = String.fromEnvironment( - 'FIREBASE_PROJECT_ID', - defaultValue: '', - ); - - static const String firebaseIosAppKey = String.fromEnvironment( - 'IOS_FIREBASE_APP_KEY', - defaultValue: '', - ); - - static const String firebaseIosAppID = String.fromEnvironment( - 'IOS_FIREBASE_APP_ID', - defaultValue: '', - ); -} diff --git a/example/lib/main.dart b/example/lib/main.dart index efd8640..629b5dd 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -6,14 +6,22 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'app.dart'; -import 'constants.dart'; + +const apiKey = String.fromEnvironment( + 'API_KEY', + defaultValue: '', +); + +const secretKey = String.fromEnvironment( + 'SECRET_KEY', + defaultValue: '', +); @pragma('vm:entry-point') Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { await Firebase.initializeApp(); DitoSDK dito = DitoSDK(); - dito.initialize( - apiKey: Constants.ditoApiKey, secretKey: Constants.ditoSecretKey); + dito.initialize(apiKey: apiKey, secretKey: secretKey); dito.onBackgroundPushNotificationHandler(message: message); } @@ -22,15 +30,14 @@ void main() async { await Firebase.initializeApp(); DitoSDK dito = DitoSDK(); - dito.initialize( - apiKey: Constants.ditoApiKey, secretKey: Constants.ditoSecretKey); + dito.initialize(apiKey: apiKey, secretKey: secretKey); await dito.initializePushNotificationService(); FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); dito.notification.onMessageClick = (data) { if (kDebugMode) { - print(data.toJson()); + print(data); } }; diff --git a/example/pubspec.lock b/example/pubspec.lock deleted file mode 100644 index be2b324..0000000 --- a/example/pubspec.lock +++ /dev/null @@ -1,529 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - _flutterfire_internals: - dependency: transitive - description: - name: _flutterfire_internals - sha256: b46f62516902afb04befa4b30eb6a12ac1f58ca8cb25fb9d632407259555dd3d - url: "https://pub.dev" - source: hosted - version: "1.3.39" - args: - dependency: transitive - description: - name: args - sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" - url: "https://pub.dev" - source: hosted - version: "2.5.0" - async: - dependency: transitive - description: - name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" - url: "https://pub.dev" - source: hosted - version: "2.11.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - characters: - dependency: transitive - description: - name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" - url: "https://pub.dev" - source: hosted - version: "1.3.0" - clock: - dependency: transitive - description: - name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf - url: "https://pub.dev" - source: hosted - version: "1.1.1" - collection: - dependency: transitive - description: - name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a - url: "https://pub.dev" - source: hosted - version: "1.18.0" - crypto: - dependency: transitive - description: - name: crypto - sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab - url: "https://pub.dev" - source: hosted - version: "3.0.3" - cupertino_icons: - dependency: "direct main" - description: - name: cupertino_icons - sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 - url: "https://pub.dev" - source: hosted - version: "1.0.8" - dbus: - dependency: transitive - description: - name: dbus - sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" - url: "https://pub.dev" - source: hosted - version: "0.7.10" - device_info_plus: - dependency: transitive - description: - name: device_info_plus - sha256: eead12d1a1ed83d8283ab4c2f3fca23ac4082f29f25f29dff0f758f57d06ec91 - url: "https://pub.dev" - source: hosted - version: "10.1.0" - device_info_plus_platform_interface: - dependency: transitive - description: - name: device_info_plus_platform_interface - sha256: d3b01d5868b50ae571cd1dc6e502fc94d956b665756180f7b16ead09e836fd64 - url: "https://pub.dev" - source: hosted - version: "7.0.0" - dito_sdk: - dependency: "direct main" - description: - path: "../package" - relative: true - source: path - version: "0.5.5" - event_bus: - dependency: transitive - description: - name: event_bus - sha256: "44baa799834f4c803921873e7446a2add0f3efa45e101a054b1f0ab9b95f8edc" - url: "https://pub.dev" - source: hosted - version: "2.0.0" - fake_async: - dependency: transitive - description: - name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" - url: "https://pub.dev" - source: hosted - version: "1.3.1" - ffi: - dependency: transitive - description: - name: ffi - sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - file: - dependency: transitive - description: - name: file - sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" - url: "https://pub.dev" - source: hosted - version: "7.0.0" - firebase_core: - dependency: "direct main" - description: - name: firebase_core - sha256: "5159984ce9b70727473eb388394650677c02c925aaa6c9439905e1f30966a4d5" - url: "https://pub.dev" - source: hosted - version: "3.2.0" - firebase_core_platform_interface: - dependency: transitive - description: - name: firebase_core_platform_interface - sha256: "1003a5a03a61fc9a22ef49f37cbcb9e46c86313a7b2e7029b9390cf8c6fc32cb" - url: "https://pub.dev" - source: hosted - version: "5.1.0" - firebase_core_web: - dependency: transitive - description: - name: firebase_core_web - sha256: "23509cb3cddfb3c910c143279ac3f07f06d3120f7d835e4a5d4b42558e978712" - url: "https://pub.dev" - source: hosted - version: "2.17.3" - firebase_messaging: - dependency: "direct main" - description: - name: firebase_messaging - sha256: "156c4292aa63a6a7d508c68ded984cb38730d2823c3265e573cb1e94983e2025" - url: "https://pub.dev" - source: hosted - version: "15.0.3" - firebase_messaging_platform_interface: - dependency: transitive - description: - name: firebase_messaging_platform_interface - sha256: "10408c5ca242b7fc632dd5eab4caf8fdf18ebe88db6052980fa71a18d88bd200" - url: "https://pub.dev" - source: hosted - version: "4.5.41" - firebase_messaging_web: - dependency: transitive - description: - name: firebase_messaging_web - sha256: c7a756e3750679407948de665735e69a368cb902940466e5d68a00ea7aba1aaa - url: "https://pub.dev" - source: hosted - version: "3.8.11" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" - url: "https://pub.dev" - source: hosted - version: "3.0.2" - flutter_local_notifications: - dependency: transitive - description: - name: flutter_local_notifications - sha256: "40e6fbd2da7dcc7ed78432c5cdab1559674b4af035fddbfb2f9a8f9c2112fcef" - url: "https://pub.dev" - source: hosted - version: "17.1.2" - flutter_local_notifications_linux: - dependency: transitive - description: - name: flutter_local_notifications_linux - sha256: "33f741ef47b5f63cc7f78fe75eeeac7e19f171ff3c3df054d84c1e38bedb6a03" - url: "https://pub.dev" - source: hosted - version: "4.0.0+1" - flutter_local_notifications_platform_interface: - dependency: transitive - description: - name: flutter_local_notifications_platform_interface - sha256: "340abf67df238f7f0ef58f4a26d2a83e1ab74c77ab03cd2b2d5018ac64db30b7" - url: "https://pub.dev" - source: hosted - version: "7.1.0" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - flutter_web_plugins: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - http: - dependency: transitive - description: - name: http - sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" - url: "https://pub.dev" - source: hosted - version: "1.2.1" - http_parser: - dependency: transitive - description: - name: http_parser - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" - url: "https://pub.dev" - source: hosted - version: "4.0.2" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" - url: "https://pub.dev" - source: hosted - version: "10.0.4" - leak_tracker_flutter_testing: - dependency: transitive - description: - name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" - url: "https://pub.dev" - source: hosted - version: "3.0.3" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" - url: "https://pub.dev" - source: hosted - version: "3.0.1" - lints: - dependency: transitive - description: - name: lints - sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 - url: "https://pub.dev" - source: hosted - version: "3.0.0" - matcher: - dependency: transitive - description: - name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb - url: "https://pub.dev" - source: hosted - version: "0.12.16+1" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" - url: "https://pub.dev" - source: hosted - version: "0.8.0" - meta: - dependency: transitive - description: - name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" - url: "https://pub.dev" - source: hosted - version: "1.12.0" - nested: - dependency: transitive - description: - name: nested - sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" - url: "https://pub.dev" - source: hosted - version: "1.0.0" - package_info_plus: - dependency: "direct main" - description: - name: package_info_plus - sha256: "7e76fad405b3e4016cd39d08f455a4eb5199723cf594cd1b8916d47140d93017" - url: "https://pub.dev" - source: hosted - version: "4.2.0" - package_info_plus_platform_interface: - dependency: transitive - description: - name: package_info_plus_platform_interface - sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6" - url: "https://pub.dev" - source: hosted - version: "2.0.1" - path: - dependency: transitive - description: - name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" - url: "https://pub.dev" - source: hosted - version: "1.9.0" - petitparser: - dependency: transitive - description: - name: petitparser - sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 - url: "https://pub.dev" - source: hosted - version: "6.0.2" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" - url: "https://pub.dev" - source: hosted - version: "2.1.8" - provider: - dependency: "direct main" - description: - name: provider - sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c - url: "https://pub.dev" - source: hosted - version: "6.1.2" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_span: - dependency: transitive - description: - name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" - url: "https://pub.dev" - source: hosted - version: "1.10.0" - sqflite: - dependency: transitive - description: - name: sqflite - sha256: a43e5a27235518c03ca238e7b4732cf35eabe863a369ceba6cbefa537a66f16d - url: "https://pub.dev" - source: hosted - version: "2.3.3+1" - sqflite_common: - dependency: transitive - description: - name: sqflite_common - sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4" - url: "https://pub.dev" - source: hosted - version: "2.5.4" - sqflite_common_ffi: - dependency: transitive - description: - name: sqflite_common_ffi - sha256: "4d6137c29e930d6e4a8ff373989dd9de7bac12e3bc87bce950f6e844e8ad3bb5" - url: "https://pub.dev" - source: hosted - version: "2.3.3" - sqlite3: - dependency: transitive - description: - name: sqlite3 - sha256: b384f598b813b347c5a7e5ffad82cbaff1bec3d1561af267041e66f6f0899295 - url: "https://pub.dev" - source: hosted - version: "2.4.3" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" - url: "https://pub.dev" - source: hosted - version: "1.11.1" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 - url: "https://pub.dev" - source: hosted - version: "2.1.2" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - synchronized: - dependency: transitive - description: - name: synchronized - sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558" - url: "https://pub.dev" - source: hosted - version: "3.1.0+1" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - test_api: - dependency: transitive - description: - name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" - url: "https://pub.dev" - source: hosted - version: "0.7.0" - timezone: - dependency: transitive - description: - name: timezone - sha256: a6ccda4a69a442098b602c44e61a1e2b4bf6f5516e875bbf0f427d5df14745d5 - url: "https://pub.dev" - source: hosted - version: "0.9.3" - typed_data: - dependency: transitive - description: - name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c - url: "https://pub.dev" - source: hosted - version: "1.3.2" - vector_math: - dependency: transitive - description: - name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" - url: "https://pub.dev" - source: hosted - version: "14.2.1" - web: - dependency: transitive - description: - name: web - sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" - url: "https://pub.dev" - source: hosted - version: "0.5.1" - win32: - dependency: transitive - description: - name: win32 - sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4 - url: "https://pub.dev" - source: hosted - version: "5.5.1" - win32_registry: - dependency: transitive - description: - name: win32_registry - sha256: "10589e0d7f4e053f2c61023a31c9ce01146656a70b7b7f0828c0b46d7da2a9bb" - url: "https://pub.dev" - source: hosted - version: "1.1.3" - xdg_directories: - dependency: transitive - description: - name: xdg_directories - sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d - url: "https://pub.dev" - source: hosted - version: "1.0.4" - xml: - dependency: transitive - description: - name: xml - sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 - url: "https://pub.dev" - source: hosted - version: "6.5.0" -sdks: - dart: ">=3.4.0 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 3509fad..7b6df2e 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -39,7 +39,7 @@ dependencies: package_info_plus: ^4.2.0 firebase_core: ^3.1.1 firebase_messaging: ^15.0.2 - dito_sdk: ^0.5.4 + dito_sdk: ^2.0.0 dev_dependencies: flutter_test: @@ -57,9 +57,6 @@ dev_dependencies: # The following section is specific to Flutter packages. flutter: - assets: - - .env - # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class. diff --git a/package/Makefile b/package/Makefile new file mode 100644 index 0000000..b34f41c --- /dev/null +++ b/package/Makefile @@ -0,0 +1,10 @@ +login: + buf registry login + +pull: + buf export buf.build/dito/sdk-service -o proto + buf export buf.build/protocolbuffers/wellknowntypes -o proto + +build: + buf build + buf generate proto --include-imports --clean --debug diff --git a/package/buf.gen.yaml b/package/buf.gen.yaml new file mode 100644 index 0000000..f2cdc48 --- /dev/null +++ b/package/buf.gen.yaml @@ -0,0 +1,8 @@ +version: v2 +managed: + enabled: true +plugins: + - remote: buf.build/protocolbuffers/dart:v21.1.2 + out: lib/proto +inputs: + - directory: proto diff --git a/package/buf.lock b/package/buf.lock new file mode 100644 index 0000000..4f98143 --- /dev/null +++ b/package/buf.lock @@ -0,0 +1,2 @@ +# Generated by buf. DO NOT EDIT. +version: v2 diff --git a/package/buf.yaml b/package/buf.yaml new file mode 100644 index 0000000..61ae974 --- /dev/null +++ b/package/buf.yaml @@ -0,0 +1,11 @@ +# For details on buf.yaml configuration, visit https://buf.build/docs/configuration/v2/buf-yaml +version: v2 +modules: + - path: proto + name: buf.build/dito/sdk-service +lint: + use: + - STANDARD +breaking: + use: + - FILE diff --git a/package/lib/api/dito_api_interface.dart b/package/lib/api/dito_api_interface.dart new file mode 100644 index 0000000..cbccd9e --- /dev/null +++ b/package/lib/api/dito_api_interface.dart @@ -0,0 +1,381 @@ +import 'dart:io'; + +import 'package:http/http.dart' as http; +import 'package:uuid/uuid.dart'; + +import '../event/event_entity.dart'; +import '../event/navigation_entity.dart'; +import '../notification/notification_entity.dart'; +import '../proto/google/protobuf/timestamp.pb.dart'; +import '../proto/sdkapi/v1/api.pb.dart' as rpcAPI; +import '../user/user_interface.dart'; +import '../utils/sha1.dart'; + +const url = 'https://sdk.dito.com.br/connect.sdk_api.v1.SDKService/Activity'; + +class AppInfoEntity { + String? build; + String? version; + String? id; + String? platform; + String? sdkVersion; + String? sdkBuild; + String? sdkLang; +} + +final class AppInfo extends AppInfoEntity { + AppInfo._internal(); + + static final appInfo = AppInfo._internal(); + + factory AppInfo() => appInfo; +} + +class ApiActivities { + final UserInterface _userInterface = UserInterface(); + final AppInfo _appInfo = AppInfo(); + + Timestamp _parseToTimestamp(String? time) { + return time != null + ? Timestamp.fromDateTime(DateTime.parse(time)) + : Timestamp.fromDateTime(DateTime.now()); + } + + rpcAPI.DeviceOs _getPlatform() { + if (Platform.isIOS) { + return rpcAPI.DeviceOs.DEVICE_OS_IOS; + } else { + return rpcAPI.DeviceOs.DEVICE_OS_ANDROID; + } + } + + rpcAPI.DeviceInfo get deviceToken => rpcAPI.DeviceInfo() + ..os = _getPlatform() + ..token = _userInterface.data.token!; + + rpcAPI.SDKInfo get sdkInfo => rpcAPI.SDKInfo() + ..version = _appInfo.sdkVersion! + ..build = _appInfo.build! + ..lang = _appInfo.sdkLang!; + + rpcAPI.AppInfo get appInfo => rpcAPI.AppInfo() + ..id = _appInfo.id! + ..build = _appInfo.build! + ..platform = _appInfo.platform! + ..version = _appInfo.version!; + + rpcAPI.UserInfo get userInfo { + final user = rpcAPI.UserInfo(); + + if (_userInterface.data.email != null && + _userInterface.data.email!.isNotEmpty) { + user.email = _userInterface.data.email!; + } + + if (_userInterface.data.id != null && _userInterface.data.id!.isNotEmpty) { + user.ditoId = _userInterface.data.id!; + } + + if (_userInterface.data.name != null && + _userInterface.data.name!.isNotEmpty) { + user.name = _userInterface.data.name!; + } + + if (_userInterface.data.birthday != null && + _userInterface.data.birthday!.isNotEmpty) { + user.birthday = _userInterface.data.birthday!; + } + + if (_userInterface.data.phone != null && + _userInterface.data.phone!.isNotEmpty) { + user.phone = _userInterface.data.phone!; + } + + if (_userInterface.data.gender != null && + _userInterface.data.gender!.isNotEmpty) { + user.gender = _userInterface.data.gender!; + } + + if (_userInterface.data.cpf != null && + _userInterface.data.cpf!.isNotEmpty) { + user.customData['cpf'] = rpcAPI.UserInfo_CustomData( + format: 'string', value: _userInterface.data.cpf); + } + + if (_userInterface.data.customData != null && + _userInterface.data.customData!.isNotEmpty) { + user.customData.addAll(_userInterface.data.customData!.map((key, value) { + final customDataValue = + rpcAPI.UserInfo_CustomData(format: 'string', value: value); + return MapEntry(key, customDataValue); + })); + } + + final addressData = _userInterface.data.address; + if (addressData != null) { + final hasAddress = [ + addressData.city, + addressData.country, + addressData.postalCode, + addressData.state, + addressData.street + ].any((field) => field != null && field.isNotEmpty); + + if (hasAddress) { + user.address = rpcAPI.UserInfo_Address(); + + if (_userInterface.data.address?.city != null && + _userInterface.data.address!.city!.isNotEmpty) { + user.address.city = _userInterface.data.address!.city!; + } + + if (_userInterface.data.address?.country != null && + _userInterface.data.address!.country!.isNotEmpty) { + user.address.country = _userInterface.data.address!.country!; + } + + if (_userInterface.data.address?.postalCode != null && + _userInterface.data.address!.postalCode!.isNotEmpty) { + user.address.postalCode = _userInterface.data.address!.postalCode!; + } + + if (_userInterface.data.address?.state != null && + _userInterface.data.address!.state!.isNotEmpty) { + user.address.state = _userInterface.data.address!.state!; + } + + if (_userInterface.data.address?.street != null && + _userInterface.data.address!.street!.isNotEmpty) { + user.address.street = _userInterface.data.address!.street!; + } + } + } + return user; + } + + rpcAPI.Activity identify({String? uuid, String? time}) { + final generatedUuid = uuid ?? Uuid().v4(); + final generatedTime = _parseToTimestamp(time); + + return rpcAPI.Activity() + ..timestamp = generatedTime + ..id = generatedUuid + ..type = rpcAPI.ActivityType.ACTIVITY_IDENTIFY + ..userData = rpcAPI.Activity_UserDataActivity(); + } + + rpcAPI.Activity login({String? uuid, String? time}) { + final generatedUuid = uuid ?? Uuid().v4(); + final generatedTime = _parseToTimestamp(time); + + return rpcAPI.Activity() + ..timestamp = generatedTime + ..id = generatedUuid + ..type = rpcAPI.ActivityType.ACTIVITY_TRACK + ..userLogin = (rpcAPI.Activity_UserLoginActivity()..utmSource = 'source'); + } + + rpcAPI.Activity trackEvent(EventEntity event, {String? uuid, String? time}) { + final generatedUuid = uuid ?? Uuid().v4(); + final generatedTime = _parseToTimestamp(time); + + return rpcAPI.Activity() + ..timestamp = generatedTime + ..id = generatedUuid + ..type = rpcAPI.ActivityType.ACTIVITY_TRACK + ..track = (rpcAPI.Activity_TrackActivity() + ..event = event.action + ..revenue = event.revenue ?? 0 + ..currency = event.currency ?? 'BRL' + ..utmSource = 'source' + ..data.addAll(event.customData + ?.map((key, value) => MapEntry(key, value.toString())) ?? + {})); + } + + rpcAPI.Activity trackNavigation(NavigationEntity navigation, + {String? uuid, String? time}) { + final generatedUuid = uuid ?? Uuid().v4(); + final generatedTime = _parseToTimestamp(time); + + return rpcAPI.Activity() + ..timestamp = generatedTime + ..id = generatedUuid + ..type = rpcAPI.ActivityType.ACTIVITY_TRACK + ..trackNavigation = (rpcAPI.Activity_TrackNavigationActivity() + ..pageIdentifier = navigation.pageName + ..data.addAll(navigation.customData + ?.map((key, value) => MapEntry(key, value.toString())) ?? + {})); + } + + rpcAPI.Activity notificationClick(NotificationEntity notification, + {String? uuid, String? time}) { + final generatedUuid = uuid ?? Uuid().v4(); + final generatedTime = _parseToTimestamp(time); + + return rpcAPI.Activity() + ..timestamp = generatedTime + ..id = generatedUuid + ..type = rpcAPI.ActivityType.ACTIVITY_TRACK + ..trackPushClick = (rpcAPI.Activity_TrackPushClickActivity() + ..notification = (rpcAPI.NotificationInfo() + ..notificationId = notification.notification + ..dispatchId = notification.notificationLogId ?? "" + ..contactId = notification.contactId ?? "" + ..name = notification.name ?? "" + ..channel = 'mobile') + ..utmSource = 'source'); + } + + rpcAPI.Activity notificationReceived(NotificationEntity notification, + {String? uuid, String? time}) { + final generatedUuid = uuid ?? Uuid().v4(); + final generatedTime = _parseToTimestamp(time); + + return rpcAPI.Activity() + ..timestamp = generatedTime + ..id = generatedUuid + ..type = rpcAPI.ActivityType.ACTIVITY_TRACK + ..trackPushReceipt = (rpcAPI.Activity_TrackPushReceiptActivity() + ..notification = (rpcAPI.NotificationInfo() + ..notificationId = notification.notification + ..dispatchId = notification.notificationLogId ?? "" + ..contactId = notification.contactId ?? "" + ..name = notification.name ?? "" + ..channel = 'mobile') + ..utmSource = 'source'); + } + + rpcAPI.Activity registryToken(String token, {String? uuid, String? time}) { + final generatedUuid = uuid ?? Uuid().v4(); + final generatedTime = _parseToTimestamp(time); + + return rpcAPI.Activity() + ..timestamp = generatedTime + ..id = generatedUuid + ..type = rpcAPI.ActivityType.ACTIVITY_REGISTER + ..tokenRegister = (rpcAPI.Activity_TokenRegisterActivity() + ..token = token + ..provider = rpcAPI.PushProvider.PROVIDER_FCM); + } + + rpcAPI.Activity pingToken(String token, {String? uuid, String? time}) { + final generatedUuid = uuid ?? Uuid().v4(); + final generatedTime = _parseToTimestamp(time); + + return rpcAPI.Activity() + ..timestamp = generatedTime + ..id = generatedUuid + ..type = rpcAPI.ActivityType.ACTIVITY_REGISTER + ..tokenPing = (rpcAPI.Activity_TokenPingActivity() + ..token = token + ..provider = rpcAPI.PushProvider.PROVIDER_FCM); + } + + rpcAPI.Activity removeToken(String token, {String? uuid, String? time}) { + final generatedUuid = uuid ?? Uuid().v4(); + final generatedTime = _parseToTimestamp(time); + + return rpcAPI.Activity() + ..timestamp = generatedTime + ..id = generatedUuid + ..type = rpcAPI.ActivityType.ACTIVITY_REGISTER + ..tokenUnregister = (rpcAPI.Activity_TokenUnregisterActivity() + ..token = token + ..provider = rpcAPI.PushProvider.PROVIDER_FCM); + } +} + +class ApiInterface { + String? _apiKey; + String? _secretKey; + + static final ApiInterface _instance = ApiInterface._internal(); + + factory ApiInterface() { + return _instance; + } + + ApiInterface._internal(); + + void setKeys(String apiKey, String secretKey) { + _instance._apiKey = apiKey; + _instance._secretKey = convertToSHA1(secretKey); + } + + ApiRequest createRequest(List activities) { + ApiActivities apiActivities = ApiActivities(); + + final device = apiActivities.deviceToken; + final sdk = apiActivities.sdkInfo; + final app = apiActivities.appInfo; + final user = apiActivities.userInfo; + + final request = rpcAPI.Request() + ..user = user + ..device = device + ..sdk = sdk + ..app = app + ..activities.addAll(activities); + + return ApiRequest(request, _apiKey, _secretKey); + } +} + +class ApiRequest { + final rpcAPI.Request _request; + final String? _apiKey; + final String? _secretKey; + + ApiRequest(this._request, this._apiKey, this._secretKey); + + void _checkConfiguration() { + if (_apiKey == null || _secretKey == null) { + throw Exception( + 'API key and Secret Key must be initialized before using. Please call the initialize() method first.'); + } + } + + Future call() async { + _checkConfiguration(); + + final response = await http.post( + Uri.parse(url), + headers: { + 'Content-Type': 'application/proto', + 'platform_api_key': _apiKey!, + 'sha1_signature': _secretKey!, + }, + body: _request.writeToBuffer(), + ); + + if (response.statusCode == 200) { + final responseProto = rpcAPI.Response.fromBuffer(response.bodyBytes); + for (var responseData in responseProto.response) { + if (responseData.hasError()) { + final error = responseData.error; + + switch (error.code) { + case rpcAPI.ErrorCode.ERROR_INVALID_REQUEST: + throw ('Invalid request format.'); + case rpcAPI.ErrorCode.ERROR_UNAUTHORIZED: + throw ('Unauthorized access.'); + case rpcAPI.ErrorCode.ERROR_NOT_FOUND: + throw ('Resource not found.'); + case rpcAPI.ErrorCode.ERROR_INTERNAL: + throw ('Internal server error.'); + case rpcAPI.ErrorCode.ERROR_NOT_IMPLEMENTED: + throw ('Feature not implemented.'); + default: + throw ('Unknown error occurred.'); + } + } + } + + return response.statusCode; + } + + throw ('Unknown error occurred.'); + } +} diff --git a/package/lib/data/database.dart b/package/lib/data/database.dart index 0db1e2f..3e1f959 100644 --- a/package/lib/data/database.dart +++ b/package/lib/data/database.dart @@ -1,15 +1,20 @@ import 'dart:async'; import 'dart:io'; -import 'package:flutter/foundation.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart'; +import '../utils/logger.dart'; + /// EventDatabaseService is a singleton class that provides methods to interact with a SQLite database /// for storing and managing events. class LocalDatabase { static const String _dbName = 'dito-offline.db'; static Database? _database; - final tables = {"notification": "notification", "events": "events"}; + final tables = { + "notification": "notification", + "events": "events", + "user": "user" + }; static final LocalDatabase _instance = LocalDatabase._internal(); @@ -51,9 +56,7 @@ class LocalDatabase { onCreate: _createTable, ); } catch (e) { - if (kDebugMode) { - print('Error initializing database: $e'); - } + loggerError('Error initializing database: $e'); rethrow; } } @@ -67,23 +70,24 @@ class LocalDatabase { await db.execute(''' CREATE TABLE events ( id INTEGER PRIMARY KEY AUTOINCREMENT, - eventName TEXT, - eventMoment TEXT, - revenue REAL, - customData TEXT + name TEXT, + event TEXT, + uuid TEXT, + createdAt TEXT ); '''); await db.execute(''' - CREATE TABLE notification ( + CREATE TABLE user ( id INTEGER PRIMARY KEY AUTOINCREMENT, - event TEXT, - token TEXT + name TEXT, + user TEXT, + uuid TEXT, + createdAt TEXT ); '''); } catch (e) { - if (kDebugMode) { - print('Error creating table: $e'); - } + loggerError('Error creating table: $e'); + rethrow; } } @@ -103,9 +107,8 @@ class LocalDatabase { whereArgs: whereArgs, ); } catch (e) { - if (kDebugMode) { - print('Error deleting event: $e'); - } + loggerError('Error deleting event: $e'); + rethrow; } } @@ -124,9 +127,8 @@ class LocalDatabase { final db = await database; await db.delete(tableName); } catch (e) { - if (kDebugMode) { - print('Error clearing database: $e'); - } + loggerError('Error clearing event: $e'); + rethrow; } } diff --git a/package/lib/data/dito_api.dart b/package/lib/data/dito_api.dart deleted file mode 100644 index 784ce36..0000000 --- a/package/lib/data/dito_api.dart +++ /dev/null @@ -1,173 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:http/http.dart' as http; - -import '../event/event_entity.dart'; -import '../user/user_entity.dart'; -import '../utils/sha1.dart'; - -class DitoApi { - final String _platform = Platform.isIOS ? 'iPhone' : 'Android'; - String? _apiKey; - String? _secretKey; - late Map _assign; - - static final DitoApi _instance = DitoApi._internal(); - - factory DitoApi() { - return _instance; - } - - DitoApi._internal(); - - void setKeys(String apiKey, String secretKey) { - _instance._apiKey = apiKey; - _instance._secretKey = secretKey; - _instance._assign = { - 'platform_api_key': apiKey, - 'sha1_signature': convertToSHA1(secretKey), - }; - } - - void _checkConfiguration() { - if (_apiKey == null || _secretKey == null) { - throw Exception( - 'API key and Secret Key must be initialized before using. Please call the initialize() method first.'); - } - } - - Future _post(String url, String path, - {Map? queryParameters, Map? body}) { - _checkConfiguration(); - - final uri = Uri.https(url, path, queryParameters); - - return http.post( - uri, - body: body, - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'User-Agent': _platform, - }, - ); - } - - Future _put(String url, String path, - {Map? queryParameters, Map? body}) { - _checkConfiguration(); - - final uri = Uri.https(url, path, queryParameters); - - return http.put( - uri, - body: body, - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'User-Agent': _platform, - }, - ); - } - - Future identify(UserEntity user) async { - final queryParameters = { - 'user_data': jsonEncode(user.toJson()), - }; - - queryParameters.addAll(_assign); - - const url = 'login.plataformasocial.com.br'; - final path = 'users/portal/${user.id}/signup'; - - return await _post(url, path, queryParameters: queryParameters); - } - - Future updateUserData(UserEntity user) async { - final queryParameters = { - 'user_data': jsonEncode(user.toJson()), - }; - - queryParameters.addAll(_assign); - - const url = 'login.plataformasocial.com.br'; - final path = 'users/${user.id}'; - - return await _put(url, path, - queryParameters: queryParameters, - body: {'user_data': jsonEncode(user.toJson())}); - } - - Future trackEvent(EventEntity event, UserEntity user) async { - final body = { - 'id_type': 'id', - 'network_name': 'pt', - 'event': jsonEncode(event.toJson()) - }; - - const url = 'events.plataformasocial.com.br'; - final path = 'users/${user.id}'; - - body.addAll(_assign); - - return await _post(url, path, body: body); - } - - Future openNotification( - String notificationId, String identifier, String reference) async { - final queryParameters = { - 'channel_type': 'mobile', - }; - - final body = { - 'identifier': identifier, - 'reference': reference, - }; - - queryParameters.addAll(_assign); - - const url = 'notification.plataformasocial.com.br'; - final path = 'notifications/$notificationId/open'; - - return await _post(url, path, queryParameters: queryParameters, body: body); - } - - Future registryToken(String token, UserEntity user) async { - if (user.isNotValid) { - throw Exception( - 'User registration is required. Please call the identify() method first.'); - } - - final queryParameters = { - 'id_type': 'id', - 'token': token, - 'platform': _platform, - }; - - queryParameters.addAll(_assign); - - const url = 'notification.plataformasocial.com.br'; - final path = 'users/${user.id}/mobile-tokens/'; - - return await _post(url, path, queryParameters: queryParameters); - } - - Future removeToken(String token, UserEntity user) async { - if (user.isNotValid) { - throw Exception( - 'User registration is required. Please call the identify() method first.'); - } - - final queryParameters = { - 'id_type': 'id', - 'token': token, - 'platform': _platform, - }; - - queryParameters.addAll(_assign); - - const url = 'notification.plataformasocial.com.br'; - final path = 'users/${user.id}/mobile-tokens/disable'; - - return await _post(url, path, queryParameters: queryParameters); - } -} diff --git a/package/lib/data/event_database.dart b/package/lib/data/event_database.dart deleted file mode 100644 index f80dd83..0000000 --- a/package/lib/data/event_database.dart +++ /dev/null @@ -1,87 +0,0 @@ -import 'package:flutter/foundation.dart'; - -import '../event/event_entity.dart'; -import 'database.dart'; - -/// EventDatabaseService is a singleton class that provides methods to interact with a SQLite database -/// for storing and managing events. -class EventDatabase { - static final LocalDatabase _database = LocalDatabase(); - static final EventDatabase _instance = EventDatabase._internal(); - - /// Factory constructor to return the singleton instance of EventDatabaseService. - factory EventDatabase() { - return _instance; - } - - /// Private named constructor for internal initialization of singleton instance. - EventDatabase._internal(); - - /// Method to insert a new event into the events table. - /// - /// [event] - The EventEntity object to be inserted. - /// Returns a Future that completes with the row id of the inserted event. - Future create(EventEntity event) async { - try { - return await _database.insert( - _database.tables["events"]!, event.toMap()) > - 0; - } catch (e) { - if (kDebugMode) { - print('Error inserting event: $e'); - } - rethrow; - } - } - - /// Method to delete an event from the events table. - /// - /// [event] - The EventEntity object to be deleted. - /// Returns a Future that completes with the number of rows deleted. - Future delete(EventEntity event) async { - try { - return await _database.delete( - _database.tables["events"]!, - 'eventName = ? AND eventMoment = ?', - [event], - ) > - 0; - } catch (e) { - if (kDebugMode) { - print('Error deleting event: $e'); - } - rethrow; - } - } - - /// Method to retrieve all events from the events table. - /// Returns a Future that completes with a list of Map objects. - Future> fetchAll() async { - try { - final maps = await _database.fetchAll(_database.tables["events"]!); - return maps.map((map) => EventEntity.fromMap(map)); - } catch (e) { - if (kDebugMode) { - print('Error retrieving events: $e'); - } - rethrow; - } - } - - /// Method to clear all events from the events table. - /// Returns a Future that completes with the number of rows deleted. - Future clearDatabase() async { - try { - return _database.clearDatabase(_database.tables["events"]!); - } catch (e) { - if (kDebugMode) { - print('Error clearing database: $e'); - } - rethrow; - } - } - - /// Method to close the database. - /// Should be called when the database is no longer needed. - Future closeDatabase() => _database.closeDatabase(); -} diff --git a/package/lib/dito_sdk.dart b/package/lib/dito_sdk.dart index 60972b8..38c4f1d 100644 --- a/package/lib/dito_sdk.dart +++ b/package/lib/dito_sdk.dart @@ -1,8 +1,11 @@ library dito_sdk; +import 'dart:io'; + import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:package_info_plus/package_info_plus.dart'; -import 'data/dito_api.dart'; +import 'api/dito_api_interface.dart'; import 'event/event_interface.dart'; import 'notification/notification_interface.dart'; import 'user/user_interface.dart'; @@ -10,10 +13,11 @@ import 'user/user_interface.dart'; /// DitoSDK is a singleton class that provides various methods to interact with Dito API /// and manage user data, events, and push notifications. class DitoSDK { - final DitoApi _api = DitoApi(); + final ApiInterface _api = ApiInterface(); final UserInterface _userInterface = UserInterface(); final EventInterface _eventInterface = EventInterface(); final NotificationInterface _notificationInterface = NotificationInterface(); + final AppInfo _appInfo = AppInfo(); static final DitoSDK _instance = DitoSDK._internal(); @@ -41,7 +45,15 @@ class DitoSDK { /// [apiKey] - The API key for the Dito platform. /// [secretKey] - The secret key for the Dito platform. void initialize({required String apiKey, required String secretKey}) async { + final packageInfo = !Platform.environment.containsKey('FLUTTER_TEST') ? await PackageInfo.fromPlatform(): null; _api.setKeys(apiKey, secretKey); + _appInfo.platform = Platform.isAndroid ? 'Android' : 'Apple iPhone'; + _appInfo.sdkLang = "Flutter"; + _appInfo.sdkVersion = '2.0.0'; + _appInfo.sdkBuild = '1'; + _appInfo.build = packageInfo?.buildNumber ?? '1'; + _appInfo.version = packageInfo?.version ?? '1.0.0'; + _appInfo.id = packageInfo?.packageName ?? ''; } /// This method initializes the push notification service using Firebase. diff --git a/package/lib/event/event_dao.dart b/package/lib/event/event_dao.dart new file mode 100644 index 0000000..6120613 --- /dev/null +++ b/package/lib/event/event_dao.dart @@ -0,0 +1,122 @@ +import 'dart:convert'; + +import 'package:dito_sdk/notification/notification_entity.dart'; + +import '../data/database.dart'; +import '../utils/logger.dart'; +import 'event_entity.dart'; +import 'navigation_entity.dart'; + +enum EventsNames { click, received, navigate, track } + +/// EventDatabaseService is a singleton class that provides methods to interact with a SQLite database +/// for storing and managing events. +class EventDAO { + static final LocalDatabase _database = LocalDatabase(); + static final EventDAO _instance = EventDAO._internal(); + + get _table => _database.tables["events"]; + + /// Factory constructor to return the singleton instance of EventDatabaseService. + factory EventDAO() { + return _instance; + } + + /// Private named constructor for internal initialization of singleton instance. + EventDAO._internal(); + + /// Method to insert a new event into the events table. + /// + /// [event] - The EventEntity object to be inserted. + /// [navigation] - The NavigationEntity object to be inserted. + /// [notification] - The NotificationEntity object to be inserted. + /// [uuid] - Event Identifier. + /// Returns a Future that completes with the row id of the inserted event. + Future create(EventsNames eventType, String uuid, + {EventEntity? event, + NavigationEntity? navigation, + NotificationEntity? notification}) async { + try { + if (event != null) { + return await _database.insert(_table, { + "name": eventType.name, + "event": jsonEncode(event.toJson()), + "uuid": uuid, + "createdAt": DateTime.now().toIso8601String() + }) > + 0; + } + + if (navigation != null) { + return await _database.insert(_table, { + "name": eventType.name, + "event": jsonEncode(navigation.toJson()), + "uuid": uuid, + "createdAt": DateTime.now().toIso8601String() + }) > + 0; + } + + if (notification != null) { + return await _database.insert(_table, { + "name": eventType.name, + "event": jsonEncode(notification.toJson()), + "uuid": uuid, + "createdAt": DateTime.now().toIso8601String() + }) > + 0; + } + + return false; + } catch (e) { + loggerError('Error inserting event: $e'); + rethrow; + } + } + + /// Method to delete an event from the events table. + /// + /// [event] - The EventEntity object to be deleted. + /// Returns a Future that completes with the number of rows deleted. + Future delete(EventEntity event) async { + try { + return await _database.delete( + _table, + 'name = ? AND createdAt = ?', + [event], + ) > + 0; + } catch (e) { + loggerError('Error deleting event: $e'); + + rethrow; + } + } + + /// Method to retrieve all events from the events table. + /// Returns a Future that completes with a list of Map objects. + Future fetchAll() async { + try { + return await _database.fetchAll(_table); + } catch (e) { + loggerError('Error retrieving events: $e'); + + rethrow; + } + } + + /// Method to clear all events from the events table. + /// Returns a Future that completes with the number of rows deleted. + Future clearDatabase() async { + try { + return _database.clearDatabase(_table); + } catch (e) { + loggerError('Error clearing database: $e'); + rethrow; + } + } + + /// Method to close the database. + /// Should be called when the database is no longer needed. + Future closeDatabase() => _database.closeDatabase(); +} diff --git a/package/lib/event/event_entity.dart b/package/lib/event/event_entity.dart index b646b53..b9a0fcb 100644 --- a/package/lib/event/event_entity.dart +++ b/package/lib/event/event_entity.dart @@ -1,43 +1,38 @@ import 'dart:convert'; class EventEntity { - String eventName; - String? eventMoment; + String action; + String? createdAt; double? revenue; + String? currency; Map? customData; EventEntity({ - required this.eventName, + required this.action, this.revenue, - this.eventMoment, + this.createdAt, + this.currency, this.customData, }); factory EventEntity.fromMap(Map map) { return EventEntity( - eventName: map['eventName'], + action: map['action'], revenue: map['revenue'], - eventMoment: map['eventMoment'], + createdAt: map['createdAt'], + currency: map['currency'], customData: - map['customData'] != null ? json.decode(map['customData']) : null, + map['customData'] != null ? jsonDecode(map['customData']) : null, ); } - Map toMap() { - return { - 'eventName': eventName, - 'eventMoment': eventMoment, - 'revenue': revenue, - 'customData': customData != null ? jsonEncode(customData) : null, - }; - } - Map toJson() { final json = { - 'action': eventName, + 'action': action, 'revenue': revenue, + 'currency': currency, 'data': customData, - 'created_at': eventMoment + 'created_at': createdAt }; json.removeWhere((key, value) => value == null); diff --git a/package/lib/event/event_interface.dart b/package/lib/event/event_interface.dart index dcc307a..2a885d1 100644 --- a/package/lib/event/event_interface.dart +++ b/package/lib/event/event_interface.dart @@ -1,38 +1,86 @@ -import 'package:flutter/foundation.dart'; - -import '../utils/custom_data.dart'; +import '../utils/logger.dart'; import 'event_entity.dart'; import 'event_repository.dart'; +import 'navigation_entity.dart'; -/// EventInterface is an interface for managing events and communicating with the event repository +/// `EventInterface` provides an interface for tracking user events and navigation actions. +/// It interacts with the `EventRepository` to save these events in the backend. interface class EventInterface { + /// Repository that handles the communication for event tracking. final EventRepository _repository = EventRepository(); - /// Tracks an event by saving and sending it to the event repository. + /// Tracks a user event. + /// + /// [action] - The action name (e.g., a button click or form submission). + /// [createdAt] - The event creation time, defaults to the current UTC time if not provided. + /// [revenue] - The revenue amount associated with the event, optional. + /// [currency] - The currency for the revenue, optional. + /// [customData] - A map of additional custom data related to the event, optional. /// - /// [event] - The EventEntity object containing event data. - /// Returns a Future that completes with true if the event was successfully tracked. - Future trackEvent(EventEntity event) async { + /// Returns a `Future` that completes with `true` if the event was tracked successfully, + /// or `false` if there was an error. + Future track( + {required String action, + String? createdAt, + double? revenue, + String? currency, + Map? customData}) async { try { + // Get the current local time and convert it to UTC for accurate event logging. DateTime localDateTime = DateTime.now(); DateTime utcDateTime = localDateTime.toUtc(); - String eventMoment = utcDateTime.toIso8601String(); - event.eventMoment ??= eventMoment; + // Create an event entity using the provided data. + final event = EventEntity( + action: action, + createdAt: createdAt ?? + utcDateTime + .toIso8601String(), // Default to current UTC time if not provided. + revenue: revenue, + currency: currency, + customData: customData); + + // Track the event using the repository and return the result. + return _repository.track(event); + } catch (e) { + loggerError('Error tracking event: $e'); // Log the error in debug mode. + + return false; // Return false if there was an error. + } + } + + /// Tracks a navigation event when the user navigates to a new page or screen. + /// + /// [name] - The name of the page the user navigated to. + /// [createdAt] - The navigation event creation time, defaults to the current UTC time if not provided. + /// [customData] - A map of additional custom data related to the navigation event, optional. + /// + /// Returns a `Future` that completes with `true` if the navigation event was tracked successfully, + /// or `false` if there was an error. + Future navigate( + {required String name, + String? createdAt, + Map? customData}) async { + try { + // Get the current local time and convert it to UTC for accurate navigation logging. + DateTime localDateTime = DateTime.now(); + DateTime utcDateTime = localDateTime.toUtc(); - final version = await customDataVersion; - if (event.customData == null) { - event.customData = version; - } else { - event.customData?.addAll(version); - } + // Create a navigation entity with the provided data. + final navigation = NavigationEntity( + pageName: name, + createdAt: createdAt ?? + utcDateTime + .toIso8601String(), // Default to current UTC time if not provided. + customData: customData); - return await _repository.trackEvent(event); + // Track the navigation event using the repository and return the result. + return _repository.navigate(navigation); } catch (e) { - if (kDebugMode) { - print('Error tracking event: $e'); - } - return false; + loggerError( + 'Error tracking navigation event: $e'); // Log the error in debug mode. + + return false; // Return false if there was an error. } } } diff --git a/package/lib/event/event_repository.dart b/package/lib/event/event_repository.dart index b918fa9..facdf33 100644 --- a/package/lib/event/event_repository.dart +++ b/package/lib/event/event_repository.dart @@ -1,18 +1,47 @@ import 'dart:async'; +import 'dart:convert'; -import 'package:dito_sdk/user/user_repository.dart'; -import 'package:flutter/foundation.dart'; - -import '../data/dito_api.dart'; -import '../data/event_database.dart'; +import '../api/dito_api_interface.dart'; +import '../notification/notification_entity.dart'; +import '../proto/sdkapi/v1/api.pb.dart'; +import '../user/user_repository.dart'; +import '../utils/logger.dart'; +import 'event_dao.dart'; import 'event_entity.dart'; +import 'navigation_entity.dart'; /// EventRepository is responsible for managing events by interacting with /// the local database and the Dito API. class EventRepository { - final DitoApi _api = DitoApi(); + final ApiInterface _api = ApiInterface(); final UserRepository _userRepository = UserRepository(); - final _database = EventDatabase(); + final _database = EventDAO(); + + /// Tracks an event by saving it to the local database if the user is not registered, + /// or by sending it to the Dito API if the user is registered. + /// + /// [event] - The EventEntity object containing event data. + /// Returns a Future that completes with true if the event was successfully tracked, + /// or false if an error occurred. + Future track(EventEntity event) async { + final activity = ApiActivities().trackEvent(event); + + if (_userRepository.data.isNotValid) { + return await _database.create(EventsNames.track, activity.id, + event: event); + } + + final result = await _api.createRequest([activity]).call(); + + print(result); + + if (result >= 400 && result < 500) { + await _database.create(EventsNames.track, activity.id, event: event); + return false; + } + + return true; + } /// Tracks an event by saving it to the local database if the user is not registered, /// or by sending it to the Dito API if the user is registered. @@ -20,17 +49,23 @@ class EventRepository { /// [event] - The EventEntity object containing event data. /// Returns a Future that completes with true if the event was successfully tracked, /// or false if an error occurred. - Future trackEvent(EventEntity event) async { - // If the user is not registered, save the event to the local database + Future navigate(NavigationEntity navigation) async { + final activity = ApiActivities().trackNavigation(navigation); + if (_userRepository.data.isNotValid) { - return await _database.create(event); + return await _database.create(EventsNames.navigate, activity.id, + navigation: navigation); } - // Otherwise, send the event to the Dito API - return await _api - .trackEvent(event, _userRepository.data) - .then((response) => true) - .catchError((e) => false); + final result = await _api.createRequest([activity]).call(); + + if (result >= 400 && result < 500) { + await _database.create(EventsNames.navigate, activity.id, + navigation: navigation); + return false; + } + + return true; } /// Verifies and processes any pending events. @@ -38,17 +73,52 @@ class EventRepository { /// Throws an exception if the user is not valid. Future verifyPendingEvents() async { try { - final events = await _database.fetchAll(); + final rows = await _database.fetchAll(); + List activities = []; + + for (final row in rows) { + final eventName = row["name"]; + final uuid = row["uuid"] as String? ?? null; + final time = row["createdAt"] as String? ?? null; - for (final event in events) { - await trackEvent(event); + switch (eventName) { + case 'track': + final event = + EventEntity.fromMap(jsonDecode(row["event"] as String)); + activities + .add(ApiActivities().trackEvent(event, uuid: uuid, time: time)); + break; + case 'received': + final event = + NotificationEntity.fromMap(jsonDecode(row["event"] as String)); + activities.add(ApiActivities() + .notificationReceived(event, uuid: uuid, time: time)); + break; + case 'click': + final event = + NotificationEntity.fromMap(jsonDecode(row["event"] as String)); + activities.add(ApiActivities() + .notificationClick(event, uuid: uuid, time: time)); + break; + case 'navigate': + final event = + NavigationEntity.fromMap(jsonDecode(row["event"] as String)); + activities.add( + ApiActivities().trackNavigation(event, uuid: uuid, time: time)); + break; + default: + break; + } } - await _database.clearDatabase(); - } catch (e) { - if (kDebugMode) { - print('Error verifying pending events: $e'); + if (activities.isNotEmpty) { + await _api.createRequest(activities).call(); } + + return await _database.clearDatabase(); + } catch (e) { + loggerError('Error verifying pending events on notification: $e'); + rethrow; } } diff --git a/package/lib/event/navigation_entity.dart b/package/lib/event/navigation_entity.dart new file mode 100644 index 0000000..df2c007 --- /dev/null +++ b/package/lib/event/navigation_entity.dart @@ -0,0 +1,30 @@ +import 'dart:convert'; + +class NavigationEntity { + String pageName; + String? createdAt; + Map? customData; + + NavigationEntity({ + required this.pageName, + this.createdAt, + this.customData, + }); + + factory NavigationEntity.fromMap(Map map) { + return NavigationEntity( + pageName: map['pageName'], + createdAt: map['createdAt'], + customData: + map['customData'] != null ? jsonDecode(map['customData']) : null, + ); + } + + Map toJson() { + return { + 'pageName': pageName, + 'createdAt': createdAt, + 'data': customData, + }; + } +} diff --git a/package/lib/notification/notification_controller.dart b/package/lib/notification/notification_controller.dart index 6c55209..8b71cc5 100644 --- a/package/lib/notification/notification_controller.dart +++ b/package/lib/notification/notification_controller.dart @@ -1,15 +1,15 @@ import 'dart:async'; import 'dart:convert'; +import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +import 'package:package_info_plus/package_info_plus.dart'; import 'notification_entity.dart'; -import 'notification_repository.dart'; class NotificationController { late FlutterLocalNotificationsPlugin localNotificationsPlugin; - final NotificationRepository _repository = NotificationRepository(); - Function(DataPayload)? _selectNotification; + Function(RemoteMessage)? _selectNotification; /// Android-specific notification details. AndroidNotificationDetails androidNotificationDetails = @@ -44,7 +44,7 @@ class NotificationController { /// Initializes the local notifications plugin. /// /// [onSelectNotification] - Callback function to handle notification selection. - Future initialize(Function(DataPayload) selectNotification) async { + Future initialize(Function(RemoteMessage) selectNotification) async { _selectNotification = selectNotification; localNotificationsPlugin = FlutterLocalNotificationsPlugin(); @@ -78,14 +78,25 @@ class NotificationController { /// /// [notification] - NotificationEntity object containing notification details. void showNotification(NotificationEntity notification) async { + PackageInfo packageInfo = await PackageInfo.fromPlatform(); + final appName = packageInfo.appName; + + final displayInfo = NotificationDisplayEntity( + id: notification.hashCode, + notificationId: notification.notification, + title: notification.details?.title ?? appName, + body: notification.details?.message ?? "", + image: notification.details?.image, + data: notification.details?.toJson()); + localNotificationsPlugin.show( - notification.id, // Notification ID. - notification.title, // Notification title. - notification.body, // Notification body. + displayInfo.id, // Notification ID. + displayInfo.title, // Notification title. + displayInfo.body, // Notification body. NotificationDetails( android: androidNotificationDetails, iOS: darwinNotificationDetails), payload: - jsonEncode(notification.payload?.toJson()), // Notification payload. + jsonEncode(notification.details?.toJson()), // Notification payload. ); } @@ -96,11 +107,8 @@ class NotificationController { final payload = response?.payload; if (payload != null && payload.isNotEmpty) { - final data = DataPayload.fromPayload(jsonDecode(payload)); - - await _repository.notifyOpenDeepLink(data.notification); - - if (_selectNotification != null) _selectNotification!(data); + if (_selectNotification != null) + _selectNotification!(jsonDecode(payload) as RemoteMessage); } } } diff --git a/package/lib/notification/notification_entity.dart b/package/lib/notification/notification_entity.dart index 94dea79..04ebb6d 100644 --- a/package/lib/notification/notification_entity.dart +++ b/package/lib/notification/notification_entity.dart @@ -1,33 +1,45 @@ import 'dart:io'; -class Details { +class DetailsEntity { final String? link; final String? title; final String message; String? image; - Details(this.title, this.message, this.link, this.image); + DetailsEntity(this.title, this.message, this.link, this.image); - factory Details.fromJson(dynamic json) { + factory DetailsEntity.fromJson(dynamic json) { assert(json is Map); - return Details(json["title"], json["message"], json["link"], json["image"]); + return DetailsEntity( + json["title"], json["message"], json["link"], json["image"]); } Map toJson() => {'link': link, 'message': message, 'title': title, 'image': image}; } -class DataPayload { +class NotificationEntity { + final String notification; + final String? notificationLogId; + final String? contactId; final String? reference; - final String? identifier; - final String? notification; - final String? notification_log_id; - final Details details; + final String? userId; + final String? name; + final DetailsEntity? details; + final String? createdAt; - DataPayload(this.reference, this.identifier, this.notification, - this.notification_log_id, this.details); + NotificationEntity({ + required this.notification, + this.notificationLogId, + this.contactId, + this.reference, + this.userId, + this.name, + this.details, + this.createdAt, + }); - factory DataPayload.fromMap(dynamic json) { + factory NotificationEntity.fromMap(dynamic json) { final String? image; assert(json is Map); @@ -43,53 +55,63 @@ class DataPayload { json["notification"]?["body"] ?? json["data"]["message"]; final String link = json["data"]["link"]; - final Details details = Details(title, message, link, image); + final DetailsEntity details = DetailsEntity(title, message, link, image); - return DataPayload( - json["data"]["reference"], - json["data"]["user_id"], - json["data"]["notification"], - json["data"]["notification_log_id"], - details, - ); + DateTime localDateTime = DateTime.now(); + DateTime utcDateTime = localDateTime.toUtc(); + + return NotificationEntity( + notification: json["data"]["notification"], + notificationLogId: json["data"]["log_id"], + contactId: json["messageId"], + reference: json["data"]["reference"], + userId: json["data"]["user_id"], + name: json["data"]["notification_name"], + createdAt: utcDateTime.toIso8601String(), + details: details); } - factory DataPayload.fromPayload(dynamic json) { + factory NotificationEntity.fromPayload(dynamic json) { assert(json is Map); - return DataPayload( - json["reference"], - json["identifier"], - json["notification_log_id"], - json["notification"], - Details.fromJson(json["details"]), + return NotificationEntity( + notification: json["notification"], + notificationLogId: json["notificationLogId"], + contactId: json["contactId"], + reference: json["reference"], + userId: json["userId"], + name: json["name"], + createdAt: json["createdAt"], + details: DetailsEntity.fromJson(json["details"]), ); } Map toJson() => { - 'reference': reference, - 'identifier': identifier, 'notification': notification, - 'notification_log_id': notification_log_id, - 'details': details.toJson() + 'notificationLogId': notificationLogId, + 'contactId': contactId, + 'reference': reference, + 'userId': userId, + 'name': name, + 'createdAt': createdAt, + 'details': details, }; } -class NotificationEntity { +class NotificationDisplayEntity { int id; String title; String body; String? notificationId; String? image; + Map? data; - DataPayload? payload; - - NotificationEntity({ + NotificationDisplayEntity({ required this.id, required this.title, required this.body, this.notificationId, this.image, - this.payload, + this.data, }); } diff --git a/package/lib/notification/notification_events.dart b/package/lib/notification/notification_events.dart index d985b0b..d55fd9f 100644 --- a/package/lib/notification/notification_events.dart +++ b/package/lib/notification/notification_events.dart @@ -1,10 +1,10 @@ -import 'package:dito_sdk/notification/notification_entity.dart'; import 'package:event_bus/event_bus.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; class MessageClickedEvent { - DataPayload data; + RemoteMessage message; - MessageClickedEvent(this.data); + MessageClickedEvent(this.message); } class NotificationEvents { diff --git a/package/lib/notification/notification_interface.dart b/package/lib/notification/notification_interface.dart index 6b4ffe0..52116fb 100644 --- a/package/lib/notification/notification_interface.dart +++ b/package/lib/notification/notification_interface.dart @@ -3,37 +3,37 @@ import 'dart:io'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; -import 'package:flutter/foundation.dart'; -import 'package:package_info_plus/package_info_plus.dart'; -import '../data/dito_api.dart'; import '../user/user_interface.dart'; +import '../utils/logger.dart'; import 'notification_controller.dart'; import 'notification_entity.dart'; import 'notification_events.dart'; import 'notification_repository.dart'; -/// NotificationInterface is an interface for communication with the notification repository and notification controller +/// `NotificationInterface` manages notifications, handling initialization, token management, +/// and listening for notification events. It integrates with Firebase Messaging and custom notification flows. class NotificationInterface { - late void Function(DataPayload payload) onMessageClick; + late void Function(RemoteMessage message) onMessageClick; final NotificationRepository _repository = NotificationRepository(); final NotificationController _controller = NotificationController(); final NotificationEvents _notificationEvents = NotificationEvents(); - final DitoApi _api = DitoApi(); final UserInterface _userInterface = UserInterface(); bool initialized = false; + Future get token async => + await FirebaseMessaging.instance.getToken(); - /// This method initializes notification controller and notification repository. - /// Start listening to notifications + /// Initializes the notification interface, including Firebase Messaging, + /// setting up token management, and listening for notification events. Future initialize() async { if (Firebase.apps.isEmpty) { throw 'Firebase not initialized'; } if (initialized) return; - await FirebaseMessaging.instance.setAutoInitEnabled(true); + // For iOS, set notification presentation options. if (Platform.isIOS) { await FirebaseMessaging.instance .setForegroundNotificationPresentationOptions( @@ -41,140 +41,154 @@ class NotificationInterface { } FirebaseMessaging.onMessage.listen(onMessage); - _handleToken(); - await _controller.initialize(onSelectNotification); + _listenStream(); + initialized = true; } void _handleToken() async { - _userInterface.data.token = await getFirebaseToken(); + _userInterface.data.token = await token; + FirebaseMessaging.instance.onTokenRefresh.listen((token) { - final lastToken = _userInterface.data.token; - if (lastToken != token) { - if (lastToken != null && lastToken.isNotEmpty) { - removeToken(lastToken); - } - registryToken(token); - _userInterface.data.token = token; - } + _userInterface.data.token = token; + _userInterface.token.pingToken(token); }).onError((err) { - if (kDebugMode) { - print('Error getting token: $err'); - } + loggerError(err); }); } - /// Gets the current FCM token for the device. - /// - /// Returns the token as a String or null if not available. - Future getFirebaseToken() => FirebaseMessaging.instance.getToken(); - - // This method turns off the streams when this class is unmounted + /// Disposes of notification streams, ensuring that all resources are released. void dispose() { _repository.didReceiveLocalNotificationStream.close(); _repository.selectNotificationStream.close(); } - // This method initializes the listeners on streams + /// Listens for events in the notification streams and triggers appropriate actions. _listenStream() { _repository.didReceiveLocalNotificationStream.stream .listen((NotificationEntity receivedNotification) async { _controller.showNotification(receivedNotification); - await notifyReceivedNotification(receivedNotification.notificationId!); + + await _repository.received(NotificationEntity( + notification: receivedNotification.notification, + notificationLogId: receivedNotification.notificationLogId, + contactId: receivedNotification.contactId, + name: receivedNotification.name)); }); + _repository.selectNotificationStream.stream - .listen((DataPayload data) async { - _notificationEvents.stream.fire(MessageClickedEvent(data)); - - // Only sends the event if the message is linked to a notification - if (data.notification != null && - data.identifier != null && - data.reference != null) { - await _api.openNotification( - data.notification!, data.identifier!, data.reference!); - - onMessageClick(data); - } + .listen((RemoteMessage message) async { + _notificationEvents.stream.fire(MessageClickedEvent(message)); + + final data = message.data; + final notification = NotificationEntity( + notification: data["notification"], + notificationLogId: data["notificationLogId"]!, + contactId: data["contactId"], + name: data["name"]); + + await _repository.click(notification); + onMessageClick(message); }); } - /// This method is a handler for new remote messages. - /// Check permissions and add notification to the stream. + /// Handles incoming messages from Firebase and triggers appropriate actions based on the content. /// - /// [message] - RemoteMessage object. + /// [message] - The incoming [RemoteMessage] from Firebase. Future onMessage(RemoteMessage message) async { if (message.data.isEmpty) { - if (kDebugMode) { - print("Data is not defined: $message"); - } + loggerError("Data is not defined: $message"); } - final notification = DataPayload.fromMap(message.toMap()); + final notification = NotificationEntity.fromMap(message.toMap()); + + _repository.received(notification); + final messagingAllowed = await _checkPermissions(); - PackageInfo packageInfo = await PackageInfo.fromPlatform(); - final appName = packageInfo.appName; - - if (messagingAllowed && notification.details.message.isNotEmpty) { - _repository.didReceiveLocalNotificationStream.add((NotificationEntity( - id: message.hashCode, - notificationId: notification.notification, - title: notification.details.title ?? appName, - body: notification.details.message, - image: notification.details.image, - payload: notification))); + if (messagingAllowed && notification.details?.message != null) { + _repository.didReceiveLocalNotificationStream.add(notification); } } - /// This method send a notify received push event to Dito + /// Marks a notification as received in the repository. /// - /// [notificationId] - DataPayload object. - Future notifyReceivedNotification(String notificationId) => - _repository.notifyReceivedNotification(notificationId); - - /// This method send a open unsubscribe from notification event to Dito - Future unsubscribeFromNotifications() => - _repository.unsubscribeFromNotifications(); - - /// This method send a open deeplink event to Dito - Future notifyOpenDeepLink(String notificationId) => - _repository.notifyOpenDeepLink(notificationId); - - /// Requests permission to show notifications. - /// - /// Returns a boolean indicating if permission was granted. - Future _checkPermissions() async { - final settings = await FirebaseMessaging.instance.requestPermission(); - return settings.authorizationStatus == AuthorizationStatus.authorized; + /// [notification] - The notification identifier. + /// [notificationLogId] - The dispatch identifier. + /// [contactId] - The contact identifier. + /// [name] - The name of notification. + /// Returns a `Future` that completes with `true` if the event was tracked successfully, + /// or `false` if there was an error. + Future received( + {required String notification, + String? notificationLogId, + String? contactId, + String? name}) async { + try { + return _repository.received(NotificationEntity( + notification: notification, + notificationLogId: notificationLogId, + contactId: contactId, + name: name)); + } catch (e) { + loggerError( + 'Error tracking click event: $e'); // Log the error in debug mode. + + return false; // Return false if there was an error. + } } - /// This method adds a selected notification to stream + /// Marks a notification as clicked in the repository. /// - /// [data] - DataPayload object. - Future onSelectNotification(DataPayload? data) async { - if (data != null) { - _repository.selectNotificationStream.add(data); + /// [notification] - The notification identifier. + /// [notificationLogId] - The dispatch identifier. + /// [contactId] - The contact identifier. + /// [name] - The name of notification. + /// [createdAt] - The navigation event creation time, defaults to the current UTC time if not provided. + /// Returns a `Future` that completes with `true` if the event was tracked successfully, + /// or `false` if there was an error. + Future click( + {required String notification, + String? notificationLogId, + String? contactId, + String? name, + String? createdAt}) async { + try { + DateTime localDateTime = DateTime.now(); + DateTime utcDateTime = localDateTime.toUtc(); + + return await _repository.click(NotificationEntity( + notification: notification, + notificationLogId: notificationLogId, + contactId: contactId, + name: name, + createdAt: createdAt ?? + utcDateTime + .toIso8601String(), // Default to current UTC time if not provided. + )); + } catch (e) { + loggerError( + 'Error tracking click event: $e'); // Log the error in debug mode. + + return false; // Return false if there was an error. } } - /// This method registers a mobile token for push notifications. + /// Checks if the user has granted permissions for receiving notifications. /// - /// [token] - The mobile token to be registered. - /// Returns an http.Response. - registryToken(String? token) async { - String? newToken = token ?? await getFirebaseToken(); - if (newToken != null) _repository.registryToken(newToken); + /// Returns `true` if notifications are authorized, `false` otherwise. + Future _checkPermissions() async { + final settings = await FirebaseMessaging.instance.requestPermission(); + return settings.authorizationStatus == AuthorizationStatus.authorized; } - /// This method removes a mobile token for push notifications. + /// Handles notification selection events and triggers appropriate actions. /// - /// [token] - The mobile token to be removed. - /// Returns an http.Response. - removeToken(String? token) async { - String? newToken = token ?? await getFirebaseToken(); - if (newToken != null) _repository.removeToken(newToken); + /// [message] - The selected [RemoteMessage] from Firebase. + void onSelectNotification(RemoteMessage message) { + _repository.selectNotificationStream.add(message); } } diff --git a/package/lib/notification/notification_repository.dart b/package/lib/notification/notification_repository.dart index 57aa5a9..ccec4b6 100644 --- a/package/lib/notification/notification_repository.dart +++ b/package/lib/notification/notification_repository.dart @@ -1,108 +1,68 @@ import 'dart:async'; -import 'package:flutter/foundation.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; -import '../data/dito_api.dart'; -import '../data/notification_database.dart'; -import '../event/event_entity.dart'; -import '../event/event_interface.dart'; +import '../api/dito_api_interface.dart'; +import '../event/event_dao.dart'; import '../user/user_repository.dart'; import 'notification_entity.dart'; class NotificationRepository { - final DitoApi _api = DitoApi(); - final _database = NotificationEvent(); + final ApiInterface _api = ApiInterface(); final UserRepository _userRepository = UserRepository(); - final EventInterface _eventInterface = EventInterface(); + final _database = EventDAO(); /// The broadcast stream for received notifications final StreamController didReceiveLocalNotificationStream = StreamController.broadcast(); /// The broadcast stream for selected notifications - final StreamController selectNotificationStream = - StreamController.broadcast(); + final StreamController selectNotificationStream = + StreamController.broadcast(); - /// Verifies and processes any pending events. + /// This method send a notify click on push event to Dito /// - /// Throws an exception if the user is not valid. - Future verifyPendingEvents() async { - try { - final events = await _database.fetchAll(); - - for (final event in events) { - final eventName = event["event"] as String; - final token = event["token"] as String; - switch (eventName) { - case "register-token": - registryToken(token); - break; - case "remove-token": - removeToken(token); - break; - default: - break; - } - } - - await _database.clearDatabase(); - } catch (e) { - if (kDebugMode) { - print('Error verifying pending events on notification: $e'); - } - rethrow; - } - } + /// [notification] - Notification object. + Future click(NotificationEntity notification) async { + final activity = ApiActivities().notificationClick(notification); - /// Registers the FCM token with the server. - /// - /// [token] - The FCM token to be registered. - /// Returns an http.Response from the server. - Future registryToken(String token) async { if (_userRepository.data.isNotValid) { - return await _database.create('register-token', token); + return await _database.create(EventsNames.click, activity.id, + notification: notification); } - return await _api - .registryToken(token, _userRepository.data) - .then((result) => true) - .catchError((e) => false); - } + final result = await _api.createRequest([activity]).call(); - /// Removes the FCM token from the server. - /// - /// [token] - The FCM token to be removed. - /// Returns an http.Response from the server. - Future removeToken(String token) async { - if (_userRepository.data.isNotValid) { - return await _database.create('remove-token', token); + if (result >= 400 && result < 500) { + await _database.create(EventsNames.click, activity.id, + notification: notification); + + return false; } - return await _api - .removeToken(token, _userRepository.data) - .then((result) => true) - .catchError((e) => false); + return true; } /// This method send a notify received push event to Dito /// - /// [notification] - DataPayload object. - Future notifyReceivedNotification(String notificationId) async { - await _eventInterface.trackEvent(EventEntity( - eventName: 'received-mobile-push-notification', - customData: {'notification_id': notificationId})); - } + /// [notification] - Notification object. + Future received(NotificationEntity notification) async { + final activity = ApiActivities().notificationReceived(notification); - /// This method send a open unsubscribe from notification event to Dito - Future unsubscribeFromNotifications() async { - await _eventInterface.trackEvent( - EventEntity(eventName: 'unsubscribed-mobile-push-notification')); - } + if (_userRepository.data.isNotValid) { + return await _database.create(EventsNames.received, activity.id, + notification: notification); + } + + final result = await _api.createRequest([activity]).call(); + + if (result >= 400 && result < 500) { + await _database.create(EventsNames.received, activity.id, + notification: notification); + + return false; + } - /// This method send a open deeplink event to Dito - Future notifyOpenDeepLink(String? notificationId) async { - await _eventInterface.trackEvent(EventEntity( - eventName: 'open-deeplink-mobile-push-notification', - customData: {'notification_id': notificationId})); + return true; } } diff --git a/package/lib/user/address_entity.dart b/package/lib/user/address_entity.dart new file mode 100644 index 0000000..08dfcb2 --- /dev/null +++ b/package/lib/user/address_entity.dart @@ -0,0 +1,29 @@ +class AddressEntity { + String? city; + String? street; + String? state; + String? postalCode; + String? country; + + AddressEntity( + {this.city, this.street, this.state, this.postalCode, this.country}); + + Map toJson() { + return { + 'city': city, + 'street': street, + 'state': state, + 'postalCode': postalCode, + 'country': country + }; + } + + factory AddressEntity.fromMap(Map map) { + return AddressEntity( + city: map['city'], + street: map['street'], + state: map['state'], + postalCode: map['postalCode'], + country: map['country']); + } +} diff --git a/package/lib/user/token_repository.dart b/package/lib/user/token_repository.dart new file mode 100644 index 0000000..7f9c1c6 --- /dev/null +++ b/package/lib/user/token_repository.dart @@ -0,0 +1,162 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:dito_sdk/user/user_repository.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; + +import '../api/dito_api_interface.dart'; +import '../notification/notification_interface.dart'; +import '../proto/sdkapi/v1/api.pb.dart'; +import '../utils/logger.dart'; +import 'user_dao.dart'; +import 'user_entity.dart'; + +class TokenRepository { + final ApiInterface _api = ApiInterface(); + final UserRepository _userRepository = UserRepository(); + final UserDAO _userDAO = UserDAO(); + final NotificationInterface _notification = NotificationInterface(); + + Future get data async => + !Platform.environment.containsKey('FLUTTER_TEST') + ? await FirebaseMessaging.instance.getToken() + : ""; + UserEntity get _userData => _userRepository.data; + + /// Registers the FCM token with the server. + /// + /// [token] - The FCM token to be registered. + /// Returns an http.Response from the server. + Future registryToken([String? token]) async { + if (token == null || token.isEmpty) { + token = await _notification.token; + } + + if (token!.isEmpty && _userData.token != null && _userData.token!.isEmpty) { + throw Exception('User registration token is required'); + } + + if (token.isNotEmpty) _userData.token = token; + + final activity = ApiActivities().registryToken(_userData.token!); + + if (_userData.isNotValid) { + return await _userDAO.create( + UserEventsNames.registryToken, _userData, activity.id); + } + + final result = await _api.createRequest([activity]).call(); + + if (result >= 400 && result < 500) { + await _userDAO.create( + UserEventsNames.registryToken, _userData, activity.id); + return false; + } + + return true; + } + + /// Registers the FCM token with the server. + /// + /// [token] - The FCM token to be registered. + /// Returns an http.Response from the server. + Future pingToken([String? token]) async { + if (token == null || token.isEmpty) { + token = await _notification.token; + } + + if (token!.isEmpty && _userData.token != null && _userData.token!.isEmpty) { + throw Exception('User registration token is required'); + } + + if (token.isNotEmpty) _userData.token = token; + + final activity = ApiActivities().pingToken(_userData.token!); + + if (_userData.isNotValid) { + return await _userDAO.create( + UserEventsNames.pingToken, _userData, activity.id); + } + + final result = await _api.createRequest([activity]).call(); + + if (result >= 400 && result < 500) { + await _userDAO.create(UserEventsNames.pingToken, _userData, activity.id); + return false; + } + + return true; + } + + /// Removes the FCM token from the server. + /// + /// [token] - The FCM token to be removed. + /// Returns an http.Response from the server. + Future removeToken([String? token]) async { + if (token == null || token.isEmpty) { + token = await _notification.token; + } + + if (token!.isEmpty && _userData.token != null && _userData.token!.isEmpty) { + throw Exception('User registration token is required'); + } + + if (_userData.isNotValid) { + throw Exception('User is required'); + } + + if (token.isNotEmpty) _userData.token = token; + + final activity = ApiActivities().removeToken(_userData.token!); + final result = await _api.createRequest([activity]).call(); + + if (result >= 400 && result < 500) { + await _userDAO.create( + UserEventsNames.pingToken, _userData, activity.id); + return false; + } + + return true; + } + + /// Verifies and processes any pending events. + /// + /// Throws an exception if the user is not valid. + Future verifyPendingEvents() async { + try { + final events = await _userDAO.fetchAll(); + List activities = []; + + for (final event in events) { + final eventName = event["name"] as String; + final user = UserEntity.fromMap(jsonDecode(event["user"] as String)); + final uuid = event["uuid"] as String? ?? null; + final time = event["createdAt"] as String? ?? null; + + switch (eventName) { + case 'registryToken': + activities.add(ApiActivities() + .registryToken(user.token!, uuid: uuid, time: time)); + break; + case 'pingToken': + activities.add( + ApiActivities().pingToken(user.token!, uuid: uuid, time: time)); + break; + default: + break; + } + } + + if (activities.isNotEmpty) { + await _api.createRequest(activities).call(); + } + + return await _userDAO.clearDatabase(); + } catch (e) { + loggerError('Error verifying pending events on notification: $e'); + + rethrow; + } + } +} diff --git a/package/lib/data/notification_database.dart b/package/lib/user/user_dao.dart similarity index 57% rename from package/lib/data/notification_database.dart rename to package/lib/user/user_dao.dart index 188dad3..12526ba 100644 --- a/package/lib/data/notification_database.dart +++ b/package/lib/user/user_dao.dart @@ -1,34 +1,45 @@ -import 'package:flutter/foundation.dart'; +import 'dart:convert'; -import 'database.dart'; +import '../data/database.dart'; +import '../utils/logger.dart'; +import 'user_entity.dart'; + +enum UserEventsNames { login, identify, registryToken, pingToken, removeToken } /// EventDatabaseService is a singleton class that provides methods to interact with a SQLite database /// for storing and managing notification. -class NotificationEvent { +class UserDAO { static final LocalDatabase _database = LocalDatabase(); - static final NotificationEvent _instance = NotificationEvent._internal(); + static final UserDAO _instance = UserDAO._internal(); + get _dataTable => _database.tables["user"]; /// Factory constructor to return the singleton instance of EventDatabaseService. - factory NotificationEvent() { + factory UserDAO() { return _instance; } /// Private named constructor for internal initialization of singleton instance. - NotificationEvent._internal(); + UserDAO._internal(); /// Method to insert a new event into the notification table. /// /// [event] - The event name to be inserted. + /// [user] - The User entity to be inserted. + /// [uuid] - Event Identifier. /// Returns a Future that completes with the row id of the inserted event. - Future create(String event, String token) async { + Future create( + UserEventsNames event, UserEntity user, String uuid) async { try { - return await _database.insert(_database.tables["notification"]!, - {'event': event, 'token': token}) > + return await _database.insert(_dataTable!, { + "name": event.name, + "user": jsonEncode(user.toJson()), + "uuid": uuid, + "createdAt": DateTime.now().toIso8601String() + }) > 0; } catch (e) { - if (kDebugMode) { - print('Error inserting event: $e'); - } + loggerError('Error inserting event: $e'); + rethrow; } } @@ -37,18 +48,17 @@ class NotificationEvent { /// /// [event] - The event name to be deleted. /// Returns a Future that completes with the number of rows deleted. - Future delete(String event) async { + Future delete(String userID) async { try { return await _database.delete( - _database.tables["notification"]!, - 'event = ?', - [event], + _dataTable!, + 'userID = ?', + [userID], ) > 0; } catch (e) { - if (kDebugMode) { - print('Error deleting event: $e'); - } + loggerError('Error deleting event: $e'); + rethrow; } } @@ -57,11 +67,10 @@ class NotificationEvent { /// Returns a Future that completes with a list of Map objects. Future>> fetchAll() async { try { - return await _database.fetchAll(_database.tables["notification"]!); + return await _database.fetchAll(_dataTable!); } catch (e) { - if (kDebugMode) { - print('Error retrieving notification: $e'); - } + loggerError('Error retrieving notification: $e'); + rethrow; } } @@ -70,11 +79,10 @@ class NotificationEvent { /// Returns a Future that completes with the number of rows deleted. Future clearDatabase() async { try { - return _database.clearDatabase(_database.tables["notification"]!); + return _database.clearDatabase(_dataTable!); } catch (e) { - if (kDebugMode) { - print('Error clearing database: $e'); - } + loggerError('Error clearing database: $e'); + rethrow; } } diff --git a/package/lib/user/user_entity.dart b/package/lib/user/user_entity.dart index 4726a59..86b2479 100644 --- a/package/lib/user/user_entity.dart +++ b/package/lib/user/user_entity.dart @@ -1,5 +1,7 @@ import 'dart:convert'; +import 'address_entity.dart'; + class UserEntity { String? userID; String? name; @@ -7,7 +9,8 @@ class UserEntity { String? email; String? gender; String? birthday; - String? location; + String? phone; + AddressEntity? address; String? token; Map? customData; @@ -18,7 +21,8 @@ class UserEntity { this.email, this.gender, this.birthday, - this.location, + this.phone, + this.address, this.token, this.customData}); @@ -31,6 +35,9 @@ class UserEntity { // Factory method to instance a user from a JSON object factory UserEntity.fromMap(Map map) { + final address = map['address'] != null + ? AddressEntity.fromMap(map['address'] as Map) + : null; return UserEntity( userID: map['userID'], name: map['name'], @@ -38,7 +45,9 @@ class UserEntity { email: map['email'], gender: map['gender'], birthday: map['birthday'], - location: map['location'], + phone: map['phone'], + token: map['token'], + address: address, customData: map['customData'] != null ? (json.decode(map['customData']) as Map) .map((key, value) => MapEntry(key, value as String)) @@ -53,8 +62,15 @@ class UserEntity { 'email': email, 'gender': gender, 'birthday': birthday, - 'location': location, + 'phone': phone, + 'token': token, + 'address': address?.toJson() ?? {}, 'data': customData != null ? jsonEncode(customData) : null, }; } + + // Factory method to convert a user to Map object + Map toMap() { + return toJson(); + } } diff --git a/package/lib/user/user_interface.dart b/package/lib/user/user_interface.dart index ade4d2c..36ba306 100644 --- a/package/lib/user/user_interface.dart +++ b/package/lib/user/user_interface.dart @@ -1,26 +1,76 @@ import 'dart:async'; -import 'package:dito_sdk/notification/notification_repository.dart'; -import 'package:flutter/foundation.dart'; - import '../event/event_repository.dart'; import '../utils/custom_data.dart'; +import '../utils/logger.dart'; +import 'address_entity.dart'; +import 'token_repository.dart'; import 'user_entity.dart'; import 'user_repository.dart'; -/// UserInterface is an interface for communication with the user repository +/// `UserInterface` defines methods for interacting with the user repository, +/// handling user identification and login flows, and managing related tokens. interface class UserInterface { final UserRepository _repository = UserRepository(); final EventRepository _eventRepository = EventRepository(); - final NotificationRepository _notificationRepository = - NotificationRepository(); - /// Identifies the user by saving their data and sending it to DitoAPI. + /// Provides access to the current user's data by retrieving it from the repository. + /// + /// Returns a [UserEntity] object representing the user's information. + UserEntity get data => _repository.data; + + /// Provides access to the `TokenRepository` for handling token-related operations. + TokenRepository get token => TokenRepository(); + + /// Identifies the user by saving their data and sending it to Dito. + /// + /// - [userID] is the required identifier of the user. + /// - Optional parameters like [name], [cpf], [email], [gender], [birthday], etc., + /// allow the specification of additional user details. + /// - [mobileToken] can be passed, or it will be fetched using FirebaseMessaging if not provided. + /// - [customData] allows sending extra information related to the user. /// - /// [user] - The UserEntity object containing user data. - /// Returns a Future that completes with true if the identification was successful. - Future identify(UserEntity user) async { + /// Returns a [Future] that completes with `true` if the user identification is successful. + Future identify({ + required String userID, + String? name, + String? cpf, + String? email, + String? gender, + String? birthday, + String? phone, + String? city, + String? street, + String? state, + String? postalCode, + String? country, + String? mobileToken, + Map? customData, + }) async { try { + final String userCurrentToken = mobileToken ?? await token.data ?? ""; + + final address = AddressEntity( + city: city, + street: street, + state: state, + postalCode: postalCode, + country: country, + ); + + final user = UserEntity( + userID: userID, + name: name, + cpf: cpf, + email: email, + gender: gender, + birthday: birthday, + phone: phone, + address: address, + token: userCurrentToken, + customData: customData, + ); + final version = await customDataVersion; if (user.customData == null) { user.customData = version; @@ -28,22 +78,38 @@ interface class UserInterface { user.customData?.addAll(version); } - final result = _repository.identify(user); + final resultIdentify = await _repository.identify(user); + await _eventRepository.verifyPendingEvents(); + await token.verifyPendingEvents(); - _eventRepository.verifyPendingEvents(); - _notificationRepository.verifyPendingEvents(); - - return result; + return resultIdentify; } catch (e) { - if (kDebugMode) { - print('Error identifying user: $e'); - } + loggerError('Error identifying user: $e'); + return false; } } - /// Gets the user data from the repository. + /// Logs the user into the system by sending a login event to Dito. /// - /// Returns the UserEntity object containing user data. - UserEntity get data => _repository.data; + /// - [userID] is the required identifier of the user. + /// - [mobileToken] is optional and, if not provided, it will be fetched using FirebaseMessaging. + /// + /// Returns a [Future] that completes with `true` if the login was successful. + Future login({required String userID, String? mobileToken}) async { + try { + final String userCurrentToken = mobileToken ?? await token.data ?? ""; + final user = UserEntity(userID: userID, token: userCurrentToken); + + final result = await _repository.login(user); + await _eventRepository.verifyPendingEvents(); + await token.verifyPendingEvents(); + + return result; + } catch (e) { + loggerError('Error identifying user: $e'); + + return false; + } + } } diff --git a/package/lib/user/user_repository.dart b/package/lib/user/user_repository.dart index 74bb2db..1007a81 100644 --- a/package/lib/user/user_repository.dart +++ b/package/lib/user/user_repository.dart @@ -1,6 +1,9 @@ import 'dart:async'; -import '../data/dito_api.dart'; +import '../api/dito_api_interface.dart'; +import '../proto/sdkapi/v1/api.pb.dart'; +import '../utils/logger.dart'; +import 'user_dao.dart'; import 'user_entity.dart'; final class UserData extends UserEntity { @@ -13,7 +16,8 @@ final class UserData extends UserEntity { class UserRepository { final _userData = UserData(); - final DitoApi api = DitoApi(); + final ApiInterface _api = ApiInterface(); + final UserDAO _userDAO = UserDAO(); /// This method get a user data on Static Data Object UserData /// Return a UserEntity Class @@ -24,16 +28,18 @@ class UserRepository { /// This method set a user data on Static Data Object UserData void _set(UserEntity user) { _userData.userID = user.userID; + if (user.phone != null) _userData.phone = user.phone; if (user.cpf != null) _userData.cpf = user.cpf; if (user.name != null) _userData.name = user.name; if (user.email != null) _userData.email = user.email; if (user.gender != null) _userData.gender = user.gender; if (user.birthday != null) _userData.birthday = user.birthday; - if (user.location != null) _userData.location = user.location; + if (user.address != null) _userData.address = user.address; if (user.customData != null) _userData.customData = user.customData; + if (user.token != null) _userData.token = user.token; } - /// This method enable user data save and send to DitoAPI + /// This method enable user data save and send to Dito /// Return bool with true when the identify was successes Future identify(UserEntity? user) async { if (user != null) _set(user); @@ -42,9 +48,68 @@ class UserRepository { throw Exception('User registration id (userID) is required'); } - return await api - .identify(user!) - .then((response) => true) - .catchError((error) => false); + final activity = ApiActivities().identify(); + final result = await _api.createRequest([activity]).call(); + + if (result >= 400 && result < 500) { + await _userDAO.create(UserEventsNames.login, _userData, activity.id); + return false; + } + + return true; + } + + /// This method enable user data save and send to Dito + /// Return bool with true when the identify was successes + Future login(UserEntity? user) async { + if (user != null) _set(user); + + if (_userData.isNotValid) { + throw Exception('User id (userID) is required'); + } + + final activity = ApiActivities().login(); + final result = await _api.createRequest([activity]).call(); + + if (result >= 400 && result < 500) { + await _userDAO.create(UserEventsNames.login, _userData, activity.id); + return false; + } + + return true; + } + + Future verifyPendingEvents() async { + try { + final events = await _userDAO.fetchAll(); + List activities = []; + + for (final event in events) { + final eventName = event["name"] as String; + final uuid = event["uuid"] as String? ?? null; + final time = event["createdAt"] as String? ?? null; + + switch (eventName) { + case 'identify': + activities.add(ApiActivities().identify(uuid: uuid, time: time)); + break; + case 'login': + activities.add(ApiActivities().login(uuid: uuid, time: time)); + break; + default: + break; + } + } + + if (activities.isNotEmpty) { + await _api.createRequest(activities).call(); + } + + return await _userDAO.clearDatabase(); + } catch (e) { + loggerError('Error verifying pending events on notification: $e'); + + rethrow; + } } } diff --git a/package/lib/utils/custom_data.dart b/package/lib/utils/custom_data.dart index 725b822..dffaf85 100644 --- a/package/lib/utils/custom_data.dart +++ b/package/lib/utils/custom_data.dart @@ -1,15 +1,14 @@ -import 'package:flutter/foundation.dart'; +import 'logger.dart'; /// Retrieves the custom data version including the SDK version information. /// /// Returns a Future that completes with a Map containing the version information. Future> get customDataVersion async { try { - return {"dito_sdk_version": "Flutter SDK - 0.5.3"}; + return {"dito_sdk_version": "Flutter SDK - 2.0.0"}; } catch (e) { - if (kDebugMode) { - print('Error retrieving package info: $e'); - } + loggerError('Error retrieving package info: $e'); + return {"dito_sdk_version": "Unknown version"}; } } diff --git a/package/lib/utils/logger.dart b/package/lib/utils/logger.dart new file mode 100644 index 0000000..9d695fe --- /dev/null +++ b/package/lib/utils/logger.dart @@ -0,0 +1,7 @@ +import 'package:flutter/foundation.dart'; + +void loggerError(dynamic err) { + if (kDebugMode) { + print("INFO: $err"); + } +} diff --git a/package/pubspec.lock b/package/pubspec.lock deleted file mode 100644 index 2e30b24..0000000 --- a/package/pubspec.lock +++ /dev/null @@ -1,498 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - _flutterfire_internals: - dependency: transitive - description: - name: _flutterfire_internals - sha256: b46f62516902afb04befa4b30eb6a12ac1f58ca8cb25fb9d632407259555dd3d - url: "https://pub.dev" - source: hosted - version: "1.3.39" - args: - dependency: transitive - description: - name: args - sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" - url: "https://pub.dev" - source: hosted - version: "2.5.0" - async: - dependency: transitive - description: - name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" - url: "https://pub.dev" - source: hosted - version: "2.11.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - characters: - dependency: transitive - description: - name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" - url: "https://pub.dev" - source: hosted - version: "1.3.0" - clock: - dependency: transitive - description: - name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf - url: "https://pub.dev" - source: hosted - version: "1.1.1" - collection: - dependency: transitive - description: - name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a - url: "https://pub.dev" - source: hosted - version: "1.18.0" - crypto: - dependency: "direct main" - description: - name: crypto - sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab - url: "https://pub.dev" - source: hosted - version: "3.0.3" - dbus: - dependency: transitive - description: - name: dbus - sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" - url: "https://pub.dev" - source: hosted - version: "0.7.10" - device_info_plus: - dependency: "direct main" - description: - name: device_info_plus - sha256: eead12d1a1ed83d8283ab4c2f3fca23ac4082f29f25f29dff0f758f57d06ec91 - url: "https://pub.dev" - source: hosted - version: "10.1.0" - device_info_plus_platform_interface: - dependency: transitive - description: - name: device_info_plus_platform_interface - sha256: d3b01d5868b50ae571cd1dc6e502fc94d956b665756180f7b16ead09e836fd64 - url: "https://pub.dev" - source: hosted - version: "7.0.0" - event_bus: - dependency: "direct main" - description: - name: event_bus - sha256: "44baa799834f4c803921873e7446a2add0f3efa45e101a054b1f0ab9b95f8edc" - url: "https://pub.dev" - source: hosted - version: "2.0.0" - fake_async: - dependency: transitive - description: - name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" - url: "https://pub.dev" - source: hosted - version: "1.3.1" - ffi: - dependency: transitive - description: - name: ffi - sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - file: - dependency: transitive - description: - name: file - sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" - url: "https://pub.dev" - source: hosted - version: "7.0.0" - firebase_core: - dependency: "direct main" - description: - name: firebase_core - sha256: "5159984ce9b70727473eb388394650677c02c925aaa6c9439905e1f30966a4d5" - url: "https://pub.dev" - source: hosted - version: "3.2.0" - firebase_core_platform_interface: - dependency: transitive - description: - name: firebase_core_platform_interface - sha256: "1003a5a03a61fc9a22ef49f37cbcb9e46c86313a7b2e7029b9390cf8c6fc32cb" - url: "https://pub.dev" - source: hosted - version: "5.1.0" - firebase_core_web: - dependency: transitive - description: - name: firebase_core_web - sha256: "23509cb3cddfb3c910c143279ac3f07f06d3120f7d835e4a5d4b42558e978712" - url: "https://pub.dev" - source: hosted - version: "2.17.3" - firebase_messaging: - dependency: "direct main" - description: - name: firebase_messaging - sha256: "156c4292aa63a6a7d508c68ded984cb38730d2823c3265e573cb1e94983e2025" - url: "https://pub.dev" - source: hosted - version: "15.0.3" - firebase_messaging_platform_interface: - dependency: transitive - description: - name: firebase_messaging_platform_interface - sha256: "10408c5ca242b7fc632dd5eab4caf8fdf18ebe88db6052980fa71a18d88bd200" - url: "https://pub.dev" - source: hosted - version: "4.5.41" - firebase_messaging_web: - dependency: transitive - description: - name: firebase_messaging_web - sha256: c7a756e3750679407948de665735e69a368cb902940466e5d68a00ea7aba1aaa - url: "https://pub.dev" - source: hosted - version: "3.8.11" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" - url: "https://pub.dev" - source: hosted - version: "4.0.0" - flutter_local_notifications: - dependency: "direct main" - description: - name: flutter_local_notifications - sha256: "40e6fbd2da7dcc7ed78432c5cdab1559674b4af035fddbfb2f9a8f9c2112fcef" - url: "https://pub.dev" - source: hosted - version: "17.1.2" - flutter_local_notifications_linux: - dependency: transitive - description: - name: flutter_local_notifications_linux - sha256: "33f741ef47b5f63cc7f78fe75eeeac7e19f171ff3c3df054d84c1e38bedb6a03" - url: "https://pub.dev" - source: hosted - version: "4.0.0+1" - flutter_local_notifications_platform_interface: - dependency: transitive - description: - name: flutter_local_notifications_platform_interface - sha256: "340abf67df238f7f0ef58f4a26d2a83e1ab74c77ab03cd2b2d5018ac64db30b7" - url: "https://pub.dev" - source: hosted - version: "7.1.0" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - flutter_web_plugins: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - http: - dependency: "direct main" - description: - name: http - sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" - url: "https://pub.dev" - source: hosted - version: "1.2.1" - http_parser: - dependency: transitive - description: - name: http_parser - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" - url: "https://pub.dev" - source: hosted - version: "4.0.2" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" - url: "https://pub.dev" - source: hosted - version: "10.0.4" - leak_tracker_flutter_testing: - dependency: transitive - description: - name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" - url: "https://pub.dev" - source: hosted - version: "3.0.3" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" - url: "https://pub.dev" - source: hosted - version: "3.0.1" - lints: - dependency: transitive - description: - name: lints - sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" - url: "https://pub.dev" - source: hosted - version: "4.0.0" - matcher: - dependency: transitive - description: - name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb - url: "https://pub.dev" - source: hosted - version: "0.12.16+1" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" - url: "https://pub.dev" - source: hosted - version: "0.8.0" - meta: - dependency: transitive - description: - name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" - url: "https://pub.dev" - source: hosted - version: "1.12.0" - package_info_plus: - dependency: "direct main" - description: - name: package_info_plus - sha256: b93d8b4d624b4ea19b0a5a208b2d6eff06004bc3ce74c06040b120eeadd00ce0 - url: "https://pub.dev" - source: hosted - version: "8.0.0" - package_info_plus_platform_interface: - dependency: transitive - description: - name: package_info_plus_platform_interface - sha256: f49918f3433a3146047372f9d4f1f847511f2acd5cd030e1f44fe5a50036b70e - url: "https://pub.dev" - source: hosted - version: "3.0.0" - path: - dependency: transitive - description: - name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" - url: "https://pub.dev" - source: hosted - version: "1.9.0" - petitparser: - dependency: transitive - description: - name: petitparser - sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 - url: "https://pub.dev" - source: hosted - version: "6.0.2" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" - url: "https://pub.dev" - source: hosted - version: "2.1.8" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_span: - dependency: transitive - description: - name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" - url: "https://pub.dev" - source: hosted - version: "1.10.0" - sqflite: - dependency: "direct main" - description: - name: sqflite - sha256: a43e5a27235518c03ca238e7b4732cf35eabe863a369ceba6cbefa537a66f16d - url: "https://pub.dev" - source: hosted - version: "2.3.3+1" - sqflite_common: - dependency: transitive - description: - name: sqflite_common - sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4" - url: "https://pub.dev" - source: hosted - version: "2.5.4" - sqflite_common_ffi: - dependency: "direct main" - description: - name: sqflite_common_ffi - sha256: "4d6137c29e930d6e4a8ff373989dd9de7bac12e3bc87bce950f6e844e8ad3bb5" - url: "https://pub.dev" - source: hosted - version: "2.3.3" - sqlite3: - dependency: transitive - description: - name: sqlite3 - sha256: b384f598b813b347c5a7e5ffad82cbaff1bec3d1561af267041e66f6f0899295 - url: "https://pub.dev" - source: hosted - version: "2.4.3" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" - url: "https://pub.dev" - source: hosted - version: "1.11.1" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 - url: "https://pub.dev" - source: hosted - version: "2.1.2" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - synchronized: - dependency: transitive - description: - name: synchronized - sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558" - url: "https://pub.dev" - source: hosted - version: "3.1.0+1" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - test_api: - dependency: transitive - description: - name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" - url: "https://pub.dev" - source: hosted - version: "0.7.0" - timezone: - dependency: transitive - description: - name: timezone - sha256: a6ccda4a69a442098b602c44e61a1e2b4bf6f5516e875bbf0f427d5df14745d5 - url: "https://pub.dev" - source: hosted - version: "0.9.3" - typed_data: - dependency: transitive - description: - name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c - url: "https://pub.dev" - source: hosted - version: "1.3.2" - vector_math: - dependency: transitive - description: - name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" - url: "https://pub.dev" - source: hosted - version: "14.2.1" - web: - dependency: transitive - description: - name: web - sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" - url: "https://pub.dev" - source: hosted - version: "0.5.1" - win32: - dependency: transitive - description: - name: win32 - sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4 - url: "https://pub.dev" - source: hosted - version: "5.5.1" - win32_registry: - dependency: transitive - description: - name: win32_registry - sha256: "10589e0d7f4e053f2c61023a31c9ce01146656a70b7b7f0828c0b46d7da2a9bb" - url: "https://pub.dev" - source: hosted - version: "1.1.3" - xdg_directories: - dependency: transitive - description: - name: xdg_directories - sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d - url: "https://pub.dev" - source: hosted - version: "1.0.4" - xml: - dependency: transitive - description: - name: xml - sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 - url: "https://pub.dev" - source: hosted - version: "6.5.0" -sdks: - dart: ">=3.4.0 <4.0.0" - flutter: ">=3.19.0" diff --git a/package/pubspec.yaml b/package/pubspec.yaml index f5412cf..bad0acb 100644 --- a/package/pubspec.yaml +++ b/package/pubspec.yaml @@ -1,6 +1,6 @@ name: dito_sdk description: A Flutter package by Dito that enables user registration and user event tracking. -version: 0.5.5 +version: 2.0.0 homepage: https://github.com/ditointernet/sdk_mobile_flutter environment: @@ -20,8 +20,13 @@ dependencies: sqflite_common_ffi: ">=2.3.3" package_info_plus: ">=4.1.0" event_bus: ^2.0.0 + uuid: ^4.4.2 + protobuf: ^3.1.0 + mockito: ^5.4.4 + dev_dependencies: flutter_test: sdk: flutter flutter_lints: ">=2.0.0" + pub_updater: ">=0.5.0" diff --git a/package/test/event/database_test.dart b/package/test/event/database_test.dart index 5962266..cbd119a 100644 --- a/package/test/event/database_test.dart +++ b/package/test/event/database_test.dart @@ -1,7 +1,10 @@ -import 'package:dito_sdk/data/database.dart'; +import 'dart:convert'; + +import 'package:dito_sdk/event/event_dao.dart'; import 'package:dito_sdk/event/event_entity.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart'; +import 'package:uuid/uuid.dart'; void main() { setUpAll(() { @@ -10,73 +13,75 @@ void main() { }); group('EventDatabaseService Tests', () { - final LocalDatabase eventDatabaseService = LocalDatabase(); - - setUp(() async { - await eventDatabaseService.database; - }); + final EventDAO eventDAO = EventDAO(); tearDown(() async { - await eventDatabaseService.clearDatabase("events"); - await eventDatabaseService.closeDatabase(); + await eventDAO.clearDatabase(); + await eventDAO.closeDatabase(); }); test('should insert an event', () async { final event = EventEntity( - eventName: 'Test Event', - eventMoment: '2024-06-01T12:34:56Z', + action: 'Test Event', + createdAt: '2024-06-01T12:34:56Z', revenue: 100.0, customData: {'key': 'value'}, - ).toMap(); + ); + + final uuid = Uuid().v4(); - final success = await eventDatabaseService.insert("events", event); + final success = await eventDAO.create(EventsNames.track, uuid, event: event); expect(success, true); - final events = await eventDatabaseService.fetchAll("events"); + final events = await eventDAO.fetchAll(); expect(events.length, 1); - expect(events.first["eventName"], 'Test Event'); + expect(jsonDecode(events.first["event"])["action"], 'Test Event'); }); test('should delete an event', () async { final event = EventEntity( - eventName: 'Test Event', - eventMoment: '2024-06-01T12:34:56Z', + action: 'Test Event', + createdAt: '2024-06-01T12:34:56Z', revenue: 100.0, customData: {'key': 'value'}, - ).toMap(); + ); - await eventDatabaseService.insert("events", event); - await eventDatabaseService.clearDatabase("events"); + final uuid = Uuid().v4(); - final events = await eventDatabaseService.fetchAll("events"); + await eventDAO.create(EventsNames.track, uuid, event: event); + await eventDAO.clearDatabase(); + + final events = await eventDAO.fetchAll(); expect(events.isEmpty, true); }); test('should fetch all events', () async { final event1 = EventEntity( - eventName: 'Test Event 1', - eventMoment: '2024-06-01T12:34:56Z', + action: 'Test Event 1', + createdAt: '2024-06-01T12:34:56Z', revenue: 100.0, customData: {'key': 'value1'}, - ).toMap(); + ); final event2 = EventEntity( - eventName: 'Test Event 2', - eventMoment: '2024-06-02T12:34:56Z', + action: 'Test Event 2', + createdAt: '2024-06-02T12:34:56Z', revenue: 200.0, customData: {'key': 'value2'}, - ).toMap(); + ); + + final uuid = Uuid().v4(); + final uuid2 = Uuid().v4(); - await eventDatabaseService.insert("events", event1); - await eventDatabaseService.insert("events", event2); + await eventDAO.create(EventsNames.track, uuid, event: event1); + await eventDAO.create(EventsNames.track, uuid2, event: event2); - final events = await eventDatabaseService.fetchAll("events"); + final events = await eventDAO.fetchAll(); expect(events.length, 2); - expect(events.first["eventName"], 'Test Event 1'); - expect(events.last["eventName"], 'Test Event 2'); + expect(jsonDecode(events.first["event"])["action"], 'Test Event 1'); + expect(jsonDecode(events.last["event"])["action"], 'Test Event 2'); }); - }); } diff --git a/package/test/event/event_test.dart b/package/test/event/event_test.dart index 35fd7d1..49dc5e2 100644 --- a/package/test/event/event_test.dart +++ b/package/test/event/event_test.dart @@ -1,7 +1,6 @@ import 'package:dito_sdk/data/database.dart'; import 'package:dito_sdk/dito_sdk.dart'; -import 'package:dito_sdk/event/event_entity.dart'; -import 'package:dito_sdk/user/user_entity.dart'; +import 'package:dito_sdk/utils/sha1.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart'; @@ -14,10 +13,11 @@ void main() async { TestWidgetsFlutterBinding.ensureInitialized(); dynamic env = await testEnv(); + setUpAll(() { sqfliteFfiInit(); databaseFactory = databaseFactoryFfi; - dito.initialize(apiKey: env["apiKey"], secretKey: env["secret"]); + dito.initialize(apiKey: env["apiKey"], secretKey: convertToSHA1(env["secret"])); }); group('Events: ', () { @@ -33,17 +33,24 @@ void main() async { }); test('Send event without identify', () async { - await dito.event - .trackEvent(EventEntity(eventName: 'event-test-sdk-flutter')); + await dito.event.track(action: 'event-test-sdk-flutter'); final events = await database.fetchAll("events"); expect(events.length, 1); expect(events.first["eventName"], 'event-test-sdk-flutter'); }); test('Send event with identify', () async { - dito.user.identify(UserEntity(userID: id, email: "teste@teste.com")); - final result = await dito.event - .trackEvent(EventEntity(eventName: 'event-test-sdk-flutter')); + await dito.user.identify(userID: id, email: "teste@teste.com"); + final result = await dito.event.track(action: 'event-test-sdk-flutter'); + final events = await database.fetchAll("events"); + + expect(events.length, 0); + expect(result, true); + }); + + test('Send navigation event', () async { + await dito.user.identify(userID: id, email: "teste@teste.com"); + final result = await dito.event.navigate(name: 'home'); final events = await database.fetchAll("events"); expect(events.length, 0); @@ -51,16 +58,14 @@ void main() async { }); test('Send event with custom data', () async { - dito.user.identify(UserEntity(userID: id, email: "teste@teste.com")); - final result = await dito.event.trackEvent(EventEntity( - eventName: 'event-test-sdk-flutter', + await dito.user.identify(userID: id, email: "teste@teste.com"); + final result = await dito.event.track( + action: 'event-test-sdk-flutter', customData: { "data do ultimo teste": DateTime.now().toIso8601String() }, - revenue: 10)); - final events = await database.fetchAll("events"); + revenue: 10); - expect(events.length, 0); expect(result, true); }); }); diff --git a/package/test/user/user_test.dart b/package/test/user/user_test.dart index b051765..fc81b7d 100644 --- a/package/test/user/user_test.dart +++ b/package/test/user/user_test.dart @@ -1,5 +1,5 @@ import 'package:dito_sdk/dito_sdk.dart'; -import 'package:dito_sdk/user/user_entity.dart'; +import 'package:dito_sdk/utils/sha1.dart'; import 'package:flutter_test/flutter_test.dart'; import '../utils.dart'; @@ -7,10 +7,13 @@ import '../utils.dart'; final DitoSDK dito = DitoSDK(); const id = '22222222222'; -void main() { - setUp(() async { - dynamic env = await testEnv(); - dito.initialize(apiKey: env["apiKey"], secretKey: env["secret"]); +void main() async { + TestWidgetsFlutterBinding.ensureInitialized(); + dynamic env = await testEnv(); + + + setUpAll(() { + dito.initialize(apiKey: env["apiKey"], secretKey: convertToSHA1(env["secret"])); }); group('User interface', () { @@ -19,17 +22,22 @@ void main() { }); test('Set User on memory', () async { - await dito.user - .identify(UserEntity(userID: id, email: "teste@teste.com")); + await dito.user.identify(userID: id, email: "teste@teste.com"); expect(dito.user.data.id, id); expect(dito.user.data.email, "teste@teste.com"); }); test('Send identify', () async { - final result = await dito.user.identify( - UserEntity(userID: "11111111111", email: "teste@teste.com")); + final result = await dito.user + .identify(userID: id, email: "teste@teste.com"); expect(result, true); - expect(dito.user.data.id, "11111111111"); + expect(dito.user.data.id, id); + }); + + test('Send login', () async { + final result = await dito.user.login(userID: id); + expect(result, true); + expect(dito.user.data.id, id); }); }); } diff --git a/package/test/utils.dart b/package/test/utils.dart index d52a86d..e2adf7f 100644 --- a/package/test/utils.dart +++ b/package/test/utils.dart @@ -6,3 +6,4 @@ dynamic testEnv() async { final json = jsonDecode(await file.readAsString()); return json; } + diff --git a/pubspec.lock b/pubspec.lock deleted file mode 100644 index 2048d15..0000000 --- a/pubspec.lock +++ /dev/null @@ -1,325 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - ansi_styles: - dependency: transitive - description: - name: ansi_styles - sha256: "9c656cc12b3c27b17dd982b2cc5c0cfdfbdabd7bc8f3ae5e8542d9867b47ce8a" - url: "https://pub.dev" - source: hosted - version: "0.3.2+1" - args: - dependency: transitive - description: - name: args - sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" - url: "https://pub.dev" - source: hosted - version: "2.5.0" - async: - dependency: transitive - description: - name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" - url: "https://pub.dev" - source: hosted - version: "2.11.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - charcode: - dependency: transitive - description: - name: charcode - sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 - url: "https://pub.dev" - source: hosted - version: "1.3.1" - cli_launcher: - dependency: transitive - description: - name: cli_launcher - sha256: "5e7e0282b79e8642edd6510ee468ae2976d847a0a29b3916e85f5fa1bfe24005" - url: "https://pub.dev" - source: hosted - version: "0.3.1" - cli_util: - dependency: transitive - description: - name: cli_util - sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 - url: "https://pub.dev" - source: hosted - version: "0.4.1" - collection: - dependency: transitive - description: - name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a - url: "https://pub.dev" - source: hosted - version: "1.18.0" - conventional_commit: - dependency: transitive - description: - name: conventional_commit - sha256: dec15ad1118f029c618651a4359eb9135d8b88f761aa24e4016d061cd45948f2 - url: "https://pub.dev" - source: hosted - version: "0.6.0+1" - file: - dependency: transitive - description: - name: file - sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" - url: "https://pub.dev" - source: hosted - version: "7.0.0" - glob: - dependency: transitive - description: - name: glob - sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - graphs: - dependency: transitive - description: - name: graphs - sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 - url: "https://pub.dev" - source: hosted - version: "2.3.1" - http: - dependency: transitive - description: - name: http - sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" - url: "https://pub.dev" - source: hosted - version: "1.2.1" - http_parser: - dependency: transitive - description: - name: http_parser - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" - url: "https://pub.dev" - source: hosted - version: "4.0.2" - io: - dependency: transitive - description: - name: io - sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" - url: "https://pub.dev" - source: hosted - version: "1.0.4" - json_annotation: - dependency: transitive - description: - name: json_annotation - sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" - url: "https://pub.dev" - source: hosted - version: "4.9.0" - matcher: - dependency: transitive - description: - name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb - url: "https://pub.dev" - source: hosted - version: "0.12.16+1" - melos: - dependency: "direct dev" - description: - name: melos - sha256: f9a6fc4f4842b7edfca2e00ab3b5b06928584f24bdc3d776ab0b30be7d599450 - url: "https://pub.dev" - source: hosted - version: "6.0.0" - meta: - dependency: transitive - description: - name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 - url: "https://pub.dev" - source: hosted - version: "1.15.0" - mustache_template: - dependency: transitive - description: - name: mustache_template - sha256: a46e26f91445bfb0b60519be280555b06792460b27b19e2b19ad5b9740df5d1c - url: "https://pub.dev" - source: hosted - version: "2.0.0" - path: - dependency: transitive - description: - name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" - url: "https://pub.dev" - source: hosted - version: "1.9.0" - platform: - dependency: transitive - description: - name: platform - sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" - url: "https://pub.dev" - source: hosted - version: "3.1.4" - pool: - dependency: transitive - description: - name: pool - sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" - url: "https://pub.dev" - source: hosted - version: "1.5.1" - process: - dependency: transitive - description: - name: process - sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32" - url: "https://pub.dev" - source: hosted - version: "5.0.2" - prompts: - dependency: transitive - description: - name: prompts - sha256: "3773b845e85a849f01e793c4fc18a45d52d7783b4cb6c0569fad19f9d0a774a1" - url: "https://pub.dev" - source: hosted - version: "2.0.0" - pub_semver: - dependency: transitive - description: - name: pub_semver - sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - pub_updater: - dependency: transitive - description: - name: pub_updater - sha256: "54e8dc865349059ebe7f163d6acce7c89eb958b8047e6d6e80ce93b13d7c9e60" - url: "https://pub.dev" - source: hosted - version: "0.4.0" - pubspec: - dependency: transitive - description: - name: pubspec - sha256: f534a50a2b4d48dc3bc0ec147c8bd7c304280fff23b153f3f11803c4d49d927e - url: "https://pub.dev" - source: hosted - version: "2.3.0" - quiver: - dependency: transitive - description: - name: quiver - sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47 - url: "https://pub.dev" - source: hosted - version: "3.2.1" - source_span: - dependency: transitive - description: - name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" - url: "https://pub.dev" - source: hosted - version: "1.10.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" - url: "https://pub.dev" - source: hosted - version: "1.11.1" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 - url: "https://pub.dev" - source: hosted - version: "2.1.2" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - test_api: - dependency: transitive - description: - name: test_api - sha256: "2419f20b0c8677b2d67c8ac4d1ac7372d862dc6c460cdbb052b40155408cd794" - url: "https://pub.dev" - source: hosted - version: "0.7.1" - typed_data: - dependency: transitive - description: - name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c - url: "https://pub.dev" - source: hosted - version: "1.3.2" - uri: - dependency: transitive - description: - name: uri - sha256: "889eea21e953187c6099802b7b4cf5219ba8f3518f604a1033064d45b1b8268a" - url: "https://pub.dev" - source: hosted - version: "1.0.0" - web: - dependency: transitive - description: - name: web - sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" - url: "https://pub.dev" - source: hosted - version: "0.5.1" - yaml: - dependency: transitive - description: - name: yaml - sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" - url: "https://pub.dev" - source: hosted - version: "3.1.2" - yaml_edit: - dependency: transitive - description: - name: yaml_edit - sha256: e9c1a3543d2da0db3e90270dbb1e4eebc985ee5e3ffe468d83224472b2194a5f - url: "https://pub.dev" - source: hosted - version: "2.2.1" -sdks: - dart: ">=3.3.0 <4.0.0"