Giter VIP home page Giter VIP logo

hydrate-app's People

Contributors

frozen-burrito avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar

hydrate-app's Issues

[BUG] Las implementaciones de `ArticleProvider` y `ArticleCollection` no son compatibles con la API paginada.

Descripción del Error
La API REST recibió cambios que implementaron un sistema de paginación de resultados. El widget ArticleSliverList y las clases ArticleCollection y ArticleProvider no son compatibles con este cambio.

Cómo Reproducir
Pasos para reproducir el error:

  1. Navegar a la sección de todos los artículos.
  2. Esperar a obtener un resultado
  3. Ver el error

Comportamiento Esperado
El comportamiento de las clases con la API paginada todavía no está especificado. Probablemente, lo mejor sería implementar un "scroll infinito", que solicite artículos a la API conforme el usuario vaya llegando al final de la lista de artículos. La clase ArticleCollection debe ser capaz de transformar el resultado JSON de la API a un resultado significativo para la app.

**Entorno **

  • Dispositivo: Galaxy A30
  • OS: Android 11
  • Versión de la app: 1.0.0-beta+1

Problemas con Slivers

La app funciona con slivers, que permiten hacer scroll en varias direcciones y en distintos niveles.

Por ejemplo, para hacer scroll horizontal por la navegación principal y luego scroll vertical en cada una de las pestañas.

El problema es en la pestaña de artículos, que a su vez tiene otro TabBarView que hace scroll horizontal y varios slivers de sroll vertical. Para integrarlos hace falta NestedScrollView, que controla el scroll anidado.

Actualmente, el usuario puede desplazarse horizontalmente entre artículos nuevos y guardados, así como verticalmente por cada lista de articulos. Esto invalida el scroll horizontal para navegar a inicio o historial, por ejemplo.

El error es:

The following assertion was thrown building SliverPersistentHeader(delegate: Instance of '_SliverCustomHeaderDelegate', mode: [floating]):
A _RenderSingleChildViewport expected a child of type RenderBox but received a child of type _RenderSliverFloatingPersistentHeaderForWidgets.

RenderObjects expect specific types of children because they coordinate with their children during layout and paint. For example, a RenderSliver cannot be the child of a RenderBox because a RenderSliver does not understand the RenderBox layout protocol.

The _RenderSingleChildViewport that expected a RenderBox child was created by: _SingleChildViewport ← IgnorePointer-[GlobalKey#6624b] ← Semantics ← Listener ← _GestureSemantics ← RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#ca79e] ← Listener ← _ScrollableScope ← _ScrollSemantics-[GlobalKey#c7223] ← RepaintBoundary ← CustomPaint ← RepaintBoundary ← ⋯

The _RenderSliverFloatingPersistentHeaderForWidgets that did not match the expected child type was created by: _SliverFloatingPersistentHeader ← SliverPersistentHeader ← _SingleChildViewport ← IgnorePointer-[GlobalKey#6624b] ← Semantics ← Listener ← _GestureSemantics ← RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#ca79e] ← Listener ← _ScrollableScope ← _ScrollSemantics-[GlobalKey#c7223] ← RepaintBoundary ← ⋯

The relevant error-causing widget was
SliverPersistentHeader

Necesito ver más sobre NestedScrollView y slivers.

Algunos modelos de SQLite se insertan con un ID repetido

Al pasar un id específico en su método toMap(), SQLite intenta crear un registro con ese mismo id, que es 0 por defecto. Cuando previamente existe un registro, SQLite marca el error:

SqfliteDatabaseException (DatabaseException(UNIQUE constraint failed: reporte_habitos.id (code 1555 SQLITE_CONSTRAINT_PRIMARYKEY[1555])) sql 'INSERT INTO reporte_habitos (id, horas_sueno, horas_act_fisica, horas_ocupacion, temperatura_max, fecha) VALUES (?, ?, ?, ?, ?, ?)' args [0, 0, 0, 0, 0, 2022-03-01T20:46:45.833798])

Por el error de constraint con primary keys unicos.

[BUG] Advertencias de tipos de errores de validación no manejados en formación de mensajes.

Descripción del Error
Al modificar los valores de campos que usan el nuevo esquema para validación y construcción de mensajes de error, hay ciertos valores para ciertos campos que producen tipos de errores que, por el momento, no producen un mensaje de error dedicado.

Algunos ejemplos son:

Unhandled username/email validation message for error: UsernameError.incorrectUsernameFormat
I/flutter (22583): Unhandled activity record distance validation message for error: NumericInputError.isNaN
I/flutter (22583): Unhandled activity record kCal validation message for error: NumericInputError.isNaN

Cómo Reproducir
Pasos para reproducir el error:

  1. Navegar a la vista de creación de registro de actividad, o a la vista de creación de cuenta.
  2. Introducir valores erróneos en los campos (se obtuvieron los mensajes de arriba al introducir "Prueba" en el campo para nombre de usuario y "245" (una cantidad sin unidad o con la unidad sin separación por espacios) en los formularios especificados.
  3. Ver las advertencias en la consola de debug.

Comportamiento Esperado
Las funciones de validación deben producir el error adecuado para cada posible clase de valor de entrada y el constructor de mensajes localizados debe manejar los casos para cada tipo de error producido por los validadores.

Entorno

  • Dispositivo: Galaxy A30
  • OS: Android 11
  • Versión de la app: 1.0.0-beta+3

Contexto Adicional
Posiblemente es buena idea revisar las pruebas unitarias para los validators.

Advertencia con insert SQLite

Al intentar usar el método insert(SQLiteModel entity) de SQLiteDB con el modelo Article, surge la siguiente advertencia:

Invalid argument 2022-02-21 21:44:29.785877 with type DateTime.

Only num, String and Uint8List are supported. See [https://github.com/tekartik/sqflite/blob/master/sqflite/doc/supported_types.md]() for details

This will throw an exception in the future. For now it is displayed once per type.

Probablemente hace falta convertir la fecha en DateTime a un ISO String.

[BUG] Los cambios de perfil no son fluidos y no actualizan los datos accesibles

Descripción del Error
Cuando un usuario cambia de perfil, el cambio no es evidente y no hay una transición que lo indique (ya sea en un inicio de sesión, creación de cuenta, cierre de sesión). Además, cambiar el perfil no cambia los datos del perfil anterior por los del nuevo perfil de muchos providers (incluyendo metas, registros de hidratación y actividad física, entre otros)

Cómo Reproducir
Pasos para reproducir el error:

  1. Navegar por la app y cargar los datos del perfil actual.
  2. Realizar una acción para cambiar de perfil, a excepción de la creación inicial del perfil por defecto.
  3. Notar como los datos no son actualizados en varios casos según el perfil.

Comportamiento Esperado
Cada perfil debe tener sus metas, registros de actividad, progreso y otros datos dedicados. Un perfil no debería poder visualizar los datos de otro perfil. Al cambiar de perfil, la app debe obtener los datos asociados con el nuevo perfil.

Entorno

  • Dispositivo: Galaxy A30
  • OS: Android 11
  • Versión de la app: 1.0.0-beta+3

[BUG] Error visual en las gráficas de hidratación y actividad

Descripción del Error
Parece que las barras en la gráfica de hidratación quedan con valores de 0, aún cuando hay consumo de agua.

Cómo Reproducir
Pasos para reproducir el error:
1.Abrir la aplicación.
2. Navegar a la pestaña de historial.
3. Observar que las gráficas de barras no cambian cuando los registros de hidratación/actividad son cargados.

Comportamiento Esperado
La gráfica primero debería mostrar todas las barras con un valor de 0. Cuando los registros son obtenidos desde la BD con un future, la gráfica debería hacer una transición para mostrar los valores obtenidos.

**Entorno **

  • Dispositivo: Galaxy A30
  • OS: Android 11
  • Versión de la app: 1.0.0-beta+1

[BUG] El servicio de sincronización con Google Fit no persiste las sesiones recuperadas

Descripción del Error
El servicio de sincronización de actividad física entre la app y Google Fit no guarda las sesiones obtenidas para el perfil de usuario. Esto hace que los registros de actividad no sean visibles fuera de la consola. Tampoco se actualiza la fecha de sincronización con Google Fit en el perfil de usuario activo.

Cómo Reproducir
Pasos para reproducir el error:

  1. Activar la sincronización con Google Fit.
  2. Sincronizar, ya sea automática o manualmente los registros.
  3. Observar que es posible acceder a los registros de sesiones de ejercicio en Google Fit.
  4. Notar que ni las actividades, ni la fecha de sincronización, son persistidos para el perfil actual.

Comportamiento Esperado
La app debería guardar las actividades obtenidas desde Google Fit, y debería persistir la fecha de la última sincronización para evitar registros duplicados.

Entorno

  • Dispositivo: Galaxy A30s
  • OS: Android 11
  • Versión de la app: 1.0.0+1

[FEATURE] Permitir distintos bookmarks por perfil de usuario

La funcionalidad que sugieres surgió de un problema? Por favor descríbelo.
Otras entidades están asociadas al ID del perfil del usuario activo. De esta forma metas, registros de hidratación y demás son propios de un solo perfil, en vez de ser compartidos sin importar el perfil.

Describe la solución que te gustaría
Es posible hacer que cada Article incluya un campo para el ID del perfil de usuario que lo marcó.

Describe las alternativas que has considerado
También puede dejarse como está, sin problemas por compartir bookmarks.

Contexto adicional
Es importante que, si se decide asociar cada Article con un perfil de usuario, hará falta definir la funcionalidad y compatibilidad con la API de recursos informativos, además de si deberían ser respaldados.

[BUG] Problemas visuales en la vista de perfil

Descripción del Error
La vista de perfil tiene dos problemas visuales:

  1. Al editar el perfil, entrando en modo edición, a través de ProfileForm, la cuenta de caracteres del nombre y apellido no es actualizada conforme el usuario escribe.
  2. El nombre completo no aparece centrado en la vista por defecto.

Cómo Reproducir
Pasos para reproducir el error:

  1. Navegar a la vista de perfil, usando un perfil con un nombre completo con más de 30 caracteres.
  2. Notar que el texto para el nombre completo no está centrado.
  3. Entrar en modo "edición", presionando el botón de ícono con un lápiz.
  4. Modificar los campos de nombre y apellido, escribiendo y/o borrando.
  5. Notar como la cuenta de caracteres de los campos no es actualizada.

Comportamiento Esperado
El texto con el nombre completo del perfil debería tener alineación centrada y recortar nombres que usen más de dos líneas. En modo edición, los campos de nombre y apellido deberían actualizar y mostrar la cuenta de caracteres con los cambios a nombre y apellido.

Entorno

  • Dispositivo: Galaxy A30
  • OS: Android 11
  • Versión de la app: 1.0.0-beta+3

[BUG] [Visual] La app no muestra el mensaje adecuado cuando no hay artículos informativos disponibles.

Descripción del Error
Cuando no hay recursos informativos disponibles y el usuario navega a la pestaña de exploración en la vista de recursos informativos, la app mantiene el indicador de carga y no muestra un resultado, aún cuando la petición fue realizada con éxito y se confirmó que no hay recursos informativos en el resultado.

Cómo Reproducir
Pasos para reproducir el error:

  1. Navegar a la vista de recursos informativos, en la pestaña de exploración.
  2. Esperar un momento a que la petición sea completada.
  3. Notar el mensaje "0 articles fetched." en la consola.
  4. Observar como el UI mantiene activo el indicador de carga.

Comportamiento Esperado
La app debería mostrar un placeholder cuando no hay recursos informativos disponibles o cuando ocurre un error durante el fetch, como lo hace en la pestaña de recursos informativos marcados.

Entorno

  • Dispositivo: Galaxy A30s
  • OS: Android 11
  • Versión de la app: 1.0.0+1

[FEATURE] Mejoras visuales variadas

La funcionalidad que sugieres surgió de un problema? Por favor descríbelo.
Hay ciertas funciones de la app que no son mostradas de una forma tan clara, como las recompensas por actividad o cuando se registra un nuevo consumo de agua. También hay detalles que pueden mejorar con animaciones simples o colores / efectos diferentes.

Describe la solución que te gustaría
Hay varias mejoras que se pueden hacer, si es que hay tiempo, descritas en el proyecto "Mejoras de UX"

[BUG] Problemas en el `build()` de `InitialForm` cuando es usado para la creación de perfil

Descripción del Error
El widget InitialForm usa un FutureBuilder para obtener el perfil de usuario actual cuando es usado en la página de perfil. El problema sucede cuando InitialForm es usado para crear el primer perfil local, porque todavía no hay un perfil de usuario creado. Esto hace que se quede intentando obtener un perfil indefinidamente.

Cómo Reproducir
Pasos para reproducir el error:

  1. Instalar una nueva instancia de la app (no actualizar una versión con perfiles ya creados)
  2. Abrir la app
  3. Ver el error

Comportamiento Esperado
Cuando el usuario abre la app por primera vez, la app debería mostrar el formulario de inicio para que el usuario cree su perfil. El usuario puede elegir no completar los datos del perfil inicial, pero siempre debe mostrarse el formulario.

**Entorno **

  • Dispositivo: Galaxy A30s
  • OS: Android 11
  • Versión de la app: 1.0.0-beta+0

[BUG] La lectura / escritura de atributos GATT produce excepciones poco predecibles

Descripción del Error
Los registros de hidratación pueden ser sincronizados correctamente desde la extensión para botellas, pero en ciertas condiciones todavía no identificadas, invocar los métodos characteristic.read() y characteristic.write(bytes) resulta en una Exception genérica no manejada.

Cómo Reproducir
Pasos para reproducir el error:

  1. Iniciar la app de pruebas.
  2. Iniciar el dispositivo de la extensión para botellas, esperar a que inicialice su servidor GATT y entre en modo de anuncio.
  3. Emparejar la app con la extensión para botellas.
  4. Esperar a que suceda la excepción genérica no manejada.

Comportamiento Esperado
Invocar los métodos characteristic.read() y characteristic.write(bytes) nunca debería producir un error mientras el dispositivo se encuentre conectado. Las invocaciones a estos métodos deberían ser sincronizadas correctamente.

Entorno

  • Dispositivo: Galaxy A30s
  • OS: Android 11
  • Versión de la app: 1.0.0-beta+6

Contexto Adicional
La salida por consola de la app:

(...)
V/InputMethodManager(23953): Starting input: tba=com.example.ble_prototype ic=null mNaviBarColor -16711423 mIsGetNaviBarColorSuccess true , NavVisible : true , NavTrans : false
D/InputMethodManager(23953): startInputInner - Id : 0
I/SurfaceControl(23953): nativeRelease nativeObject s[507572011936]
I/SurfaceControl(23953): nativeRelease nativeObject e[507572011936]
I/SurfaceControl(23953): nativeRelease nativeObject s[507572013088]
I/SurfaceControl(23953): nativeRelease nativeObject e[507572013088]
D/FlutterBluePlugin(23953): mDevices size: 0
D/FlutterBluePlugin(23953): mDevices size: 0
D/FlutterBluePlugin(23953): mDevices size: 0
(...)
D/FlutterBluePlugin(23953): mDevices size: 0
I/ViewRootImpl@87b963[MainActivity](23953): ViewPostIme pointer 0
I/ViewRootImpl@87b963[MainActivity](23953): ViewPostIme pointer 1
I/BluetoothAdapter(23953): STATE_ON
I/BluetoothAdapter(23953): STATE_ON
I/BluetoothAdapter(23953): STATE_ON
I/BluetoothAdapter(23953): STATE_ON
D/BluetoothLeScanner(23953): Start Scan with callback
D/BluetoothLeScanner(23953): onScannerRegistered() - status=0 scannerId=13 mScannerId=0
D/FlutterBluePlugin(23953): mDevices size: 0
D/FlutterBluePlugin(23953): mDevices size: 0
D/FlutterBluePlugin(23953): mDevices size: 0
I/BluetoothAdapter(23953): STATE_ON
I/BluetoothAdapter(23953): STATE_ON
D/BluetoothLeScanner(23953): Stop Scan with callback
I/ViewRootImpl@87b963[MainActivity](23953): ViewPostIme pointer 0
I/ViewRootImpl@87b963[MainActivity](23953): ViewPostIme pointer 1
I/flutter (23953): Device state changed BluetoothDeviceState.disconnected
D/FlutterBluePlugin(23953): mDevices size: 0
D/FlutterBluePlugin(23953): mDevices size: 0
I/ViewRootImpl@87b963[MainActivity](23953): ViewPostIme pointer 0
I/ViewRootImpl@87b963[MainActivity](23953): ViewPostIme pointer 1
I/flutter (23953): Connecting to device
I/BluetoothAdapter(23953): STATE_ON
D/BluetoothGatt(23953): connect() - device: E0:E2:E6:9B:3B:C2, auto: true
I/BluetoothAdapter(23953): isSecureModeEnabled
D/BluetoothGatt(23953): registerApp()
D/BluetoothGatt(23953): registerApp() - UUID=8e6ad573-ac61-4ef0-91e0-aabb0ad2663d
D/BluetoothGatt(23953): onClientRegistered() - status=0 clientIf=13
D/BluetoothGatt(23953): onClientConnectionState() - status=0 clientIf=13 device=E0:E2:E6:9B:3B:C2
D/FlutterBluePlugin(23953): [onConnectionStateChange] status: 0 newState: 2
I/flutter (23953): Device state changed BluetoothDeviceState.connected
I/flutter (23953): Device connected, discovering services
D/BluetoothGatt(23953): discoverServices() - device: E0:E2:E6:9B:3B:C2
D/BluetoothGatt(23953): onConnectionUpdated() - Device=E0:E2:E6:9B:3B:C2 interval=24 latency=0 timeout=400 status=0
D/BluetoothGatt(23953): onConnectionUpdated() - Device=E0:E2:E6:9B:3B:C2 interval=6 latency=0 timeout=500 status=0
D/BluetoothGatt(23953): onSearchComplete() = Device=E0:E2:E6:9B:3B:C2 Status=0
D/FlutterBluePlugin(23953): [onServicesDiscovered] count: 4 status: 0
D/BluetoothGatt(23953): onConnectionUpdated() - Device=E0:E2:E6:9B:3B:C2 interval=24 latency=0 timeout=400 status=0
D/BluetoothGatt(23953): setCharacteristicNotification() - uuid: 0faf892f-0000-1000-8000-00805f9b34fb enable: true
I/flutter (23953): Handling change on pendingRecordsCount: []
I/flutter (23953): Pending record count bytes is empty, no data.
D/FlutterBluePlugin(23953): [onDescriptorWrite] uuid: 00002902-0000-1000-8000-00805f9b34fb status: 0
D/FlutterBluePlugin(23953): [onCharacteristicChanged] uuid: 0faf892f-0000-1000-8000-00805f9b34fb
D/FlutterBluePlugin(23953): mDevices size: 1
I/flutter (23953): Handling change on pendingRecordsCount: [1]
I/flutter (23953): Number of records pending sync: 1
I/flutter (23953): remoteId: E0:E2:E6:9B:3B:C2 characteristicUuid: 0faf892c-0000-1000-8000-00805f9b34fb serviceUuid: 000019f5-0000-1000-8000-00805f9b34fb
D/FlutterBluePlugin(23953): [onCharacteristicRead] uuid: 0faf892c-0000-1000-8000-00805f9b34fb status: 0
I/flutter (23953): Ml char bytes: [68, 0]
I/flutter (23953): remoteId: E0:E2:E6:9B:3B:C2 characteristicUuid: 00002a6e-0000-1000-8000-00805f9b34fb serviceUuid: 000019f5-0000-1000-8000-00805f9b34fb
D/FlutterBluePlugin(23953): [onCharacteristicRead] uuid: 00002a6e-0000-1000-8000-00805f9b34fb status: 0
I/flutter (23953): Temperature char bytes: [247, 255]
I/flutter (23953): remoteId: E0:E2:E6:9B:3B:C2 characteristicUuid: 00000fff-0000-1000-8000-00805f9b34fb serviceUuid: 000019f5-0000-1000-8000-00805f9b34fb
D/FlutterBluePlugin(23953): [onCharacteristicRead] uuid: 00000fff-0000-1000-8000-00805f9b34fb status: 0
I/flutter (23953): Date char bytes: [13, 0, 0, 0, 0, 0, 0, 0]
I/flutter (23953): remoteId: E0:E2:E6:9B:3B:C2 characteristicUuid: 0faf892f-0000-1000-8000-00805f9b34fb serviceUuid: 000019f5-0000-1000-8000-00805f9b34fb
D/FlutterBluePlugin(23953): [onCharacteristicRead] uuid: 0faf892f-0000-1000-8000-00805f9b34fb status: 0
I/flutter (23953): Ignored device service characteristic, uuid: 0faf892f-0000-1000-8000-00805f9b34fb
I/flutter (23953): remoteId: E0:E2:E6:9B:3B:C2 characteristicUuid: 00002a19-0000-1000-8000-00805f9b34fb serviceUuid: 0000180f-0000-1000-8000-00805f9b34fb
I/flutter (23953): Handling change on pendingRecordsCount: [1]
I/flutter (23953): Number of records pending sync: 1
I/flutter (23953): remoteId: E0:E2:E6:9B:3B:C2 characteristicUuid: 0faf892c-0000-1000-8000-00805f9b34fb serviceUuid: 000019f5-0000-1000-8000-00805f9b34fb
I/flutter (23953): Error al leer de la caracteristica con UUID = pendingCountCharGUID: PlatformException(read_characteristic_error, unknown reason, may occur if readCharacteristic was called before last read finished., null, null)
I/flutter (23953): Ml char bytes: []
I/flutter (23953): remoteId: E0:E2:E6:9B:3B:C2 characteristicUuid: 00002a6e-0000-1000-8000-00805f9b34fb serviceUuid: 000019f5-0000-1000-8000-00805f9b34fb
I/flutter (23953): Error al leer de la caracteristica con UUID = pendingCountCharGUID: PlatformException(read_characteristic_error, unknown reason, may occur if readCharacteristic was called before last read finished., null, null)
I/flutter (23953): Temperature char bytes: []
I/flutter (23953): remoteId: E0:E2:E6:9B:3B:C2 characteristicUuid: 00000fff-0000-1000-8000-00805f9b34fb serviceUuid: 000019f5-0000-1000-8000-00805f9b34fb
I/flutter (23953): Error al leer de la caracteristica con UUID = pendingCountCharGUID: PlatformException(read_characteristic_error, unknown reason, may occur if readCharacteristic was called before last read finished., null, null)
I/flutter (23953): Date char bytes: []
I/flutter (23953): remoteId: E0:E2:E6:9B:3B:C2 characteristicUuid: 0faf892f-0000-1000-8000-00805f9b34fb serviceUuid: 000019f5-0000-1000-8000-00805f9b34fb
I/flutter (23953): Error leer de la caracteristica con UUID = pendingCountCharGUID: PlatformException(read_characteristic_error, unknown reason, may occur if readCharacteristic was called before last read finished., null, null)
I/flutter (23953): Ignored device service characteristic, uuid: 0faf892f-0000-1000-8000-00805f9b34fb
I/flutter (23953): remoteId: E0:E2:E6:9B:3B:C2 characteristicUuid: 00002a19-0000-1000-8000-00805f9b34fb serviceUuid: 0000180f-0000-1000-8000-00805f9b34fb
I/flutter (23953): Error al leer de la caracteristica con UUID = pendingCountCharGUID: PlatformException(read_characteristic_error, unknown reason, may occur if readCharacteristic was called before last read finished., null, null)
D/FlutterBluePlugin(23953): [onCharacteristicRead] uuid: 00002a19-0000-1000-8000-00805f9b34fb status: 0
I/flutter (23953): Battery char bytes: []
I/flutter (23953): Synchronized data through BLE: {}
I/flutter (23953): Not all required attributes could be obtained, expected 4
I/flutter (23953): Battery char bytes: [32]
I/flutter (23953): Synchronized data through BLE: {cantidad: 68, temperatura: -0.09, fecha: 1969-12-31 18:00:13.000, porcentaje_bateria: 32}
D/FlutterBluePlugin(23953): [onCharacteristicChanged] uuid: 0faf892f-0000-1000-8000-00805f9b34fb
I/flutter (23953): Handling change on pendingRecordsCount: [1]
I/flutter (23953): Number of records pending sync: 1
D/FlutterBluePlugin(23953): mDevices size: 1
D/FlutterBluePlugin(23953): mDevices size: 1
D/FlutterBluePlugin(23953): [onCharacteristicChanged] uuid: 0faf892f-0000-1000-8000-00805f9b34fb
I/flutter (23953): Handling change on pendingRecordsCount: [1]
I/flutter (23953): Number of records pending sync: 1
D/FlutterBluePlugin(23953): mDevices size: 1
D/FlutterBluePlugin(23953): mDevices size: 1
D/FlutterBluePlugin(23953): mDevices size: 1
D/FlutterBluePlugin(23953): [onCharacteristicChanged] uuid: 0faf892f-0000-1000-8000-00805f9b34fb
I/flutter (23953): Handling change on pendingRecordsCount: [1]
I/flutter (23953): Number of records pending sync: 1
D/FlutterBluePlugin(23953): mDevices size: 1
D/FlutterBluePlugin(23953): mDevices size: 1
D/FlutterBluePlugin(23953): [onCharacteristicChanged] uuid: 0faf892f-0000-1000-8000-00805f9b34fb
I/flutter (23953): Handling change on pendingRecordsCount: [1]
I/flutter (23953): Number of records pending sync: 1
D/FlutterBluePlugin(23953): mDevices size: 1
D/FlutterBluePlugin(23953): mDevices size: 1
D/FlutterBluePlugin(23953): mDevices size: 1
D/FlutterBluePlugin(23953): [onCharacteristicChanged] uuid: 0faf892f-0000-1000-8000-00805f9b34fb
I/flutter (23953): Handling change on pendingRecordsCount: [1]
I/flutter (23953): Number of records pending sync: 1
D/FlutterBluePlugin(23953): mDevices size: 1
D/FlutterBluePlugin(23953): mDevices size: 1
D/FlutterBluePlugin(23953): mDevices size: 1
D/FlutterBluePlugin(23953): mDevices size: 1
D/FlutterBluePlugin(23953): mDevices size: 1
D/FlutterBluePlugin(23953): [onCharacteristicWrite] uuid: 0faf892f-0000-1000-8000-00805f9b34fb status: 133
D/BluetoothGatt(23953): onClientConnectionState() - status=22 clientIf=13 device=E0:E2:E6:9B:3B:C2
D/FlutterBluePlugin(23953): [onConnectionStateChange] status: 22 newState: 0
I/flutter (23953): remoteId: E0:E2:E6:9B:3B:C2 characteristicUuid: 0faf892c-0000-1000-8000-00805f9b34fb serviceUuid: 000019f5-0000-1000-8000-00805f9b34fb
I/flutter (23953): Error al escribir a la caracteristica con UUID = pendingCountCharGUID: Exception: Failed to write the characteristic
I/flutter (23953): Hydration record synchronized: Test Hydration record = {id:-1, amount:68 ml, temperature:-0.09 °C, remaining battery:32%, date:1969-12-31T18:00:13.000, profileId:-1, }
I/flutter (23953): Device state changed BluetoothDeviceState.disconnected
D/BluetoothGatt(23953): onClientConnectionState() - status=0 clientIf=13 device=E0:E2:E6:9B:3B:C2
D/FlutterBluePlugin(23953): [onConnectionStateChange] status: 0 newState: 2
I/flutter (23953): Device state changed BluetoothDeviceState.connected
I/flutter (23953): Device connected, discovering services
D/BluetoothGatt(23953): discoverServices() - device: E0:E2:E6:9B:3B:C2
D/BluetoothGatt(23953): onConnectionUpdated() - Device=E0:E2:E6:9B:3B:C2 interval=24 latency=0 timeout=400 status=0
D/BluetoothGatt(23953): onConnectionUpdated() - Device=E0:E2:E6:9B:3B:C2 interval=6 latency=0 timeout=500 status=0
Application finished.

La salida por consola de la extensión para botellas:

I (29) boot: ESP-IDF v4.4.1 2nd stage bootloader
I (29) boot: compile time 19:54:45
I (29) boot: chip revision: 3
I (32) boot_comm: chip revision: 3, min. bootloader chip revision: 0
I (39) boot.esp32: SPI Speed      : 40MHz
I (44) boot.esp32: SPI Mode       : DIO
I (48) boot.esp32: SPI Flash Size : 2MB
I (53) boot: Enabling RNG early entropy source...
I (58) boot: Partition Table:
I (62) boot: ## Label            Usage          Type ST Offset   Length
I (69) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (76) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (84) boot:  2 factory          factory app      00 00 00010000 00100000
I (91) boot: End of partition table
I (96) boot_comm: chip revision: 3, min. application chip revision: 0
I (103) esp_image: segment 0: paddr=00010020 vaddr=3f400020 size=207f8h (133112) map
I (159) esp_image: segment 1: paddr=00030820 vaddr=3ffbdb60 size=04760h ( 18272) load
I (167) esp_image: segment 2: paddr=00034f88 vaddr=40080000 size=0b090h ( 45200) load
I (186) esp_image: segment 3: paddr=00040020 vaddr=400d0020 size=7fb40h (523072) map
I (375) esp_image: segment 4: paddr=000bfb68 vaddr=4008b090 size=0dcb0h ( 56496) load
I (399) esp_image: segment 5: paddr=000cd820 vaddr=50000000 size=00010h (    16) load
I (411) boot: Loaded app from partition at offset 0x10000
I (411) boot: Disabling RNG early entropy source...
I (423) cpu_start: Pro cpu up.
I (423) cpu_start: Starting app cpu, entry point is 0x400813a4
0x400813a4: call_start_cpu1 at C:/Espressif/frameworks/esp-idf-v4.4.1-2/components/esp_system/port/cpu_start.c:160

I (0) cpu_start: App cpu up.
I (439) cpu_start: Pro cpu start user code
I (439) cpu_start: cpu freq: 160000000
I (439) cpu_start: Application information:
I (444) cpu_start: Project name:     extension-inteligente-hydrate
I (451) cpu_start: App version:      6cb320a-dirty
I (456) cpu_start: Compile time:     Sep 20 2022 17:43:26
I (462) cpu_start: ELF file SHA256:  aff40b73cb2af185...
I (468) cpu_start: ESP-IDF:          v4.4.1
I (473) heap_init: Initializing. RAM available for dynamic allocation:
I (480) heap_init: At 3FFAFF10 len 000000F0 (0 KiB): DRAM
I (486) heap_init: At 3FFB6388 len 00001C78 (7 KiB): DRAM
I (492) heap_init: At 3FFB9A20 len 00004108 (16 KiB): DRAM
I (499) heap_init: At 3FFC6DA0 len 00019260 (100 KiB): DRAM
I (505) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
I (511) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (518) heap_init: At 40098D40 len 000072C0 (28 KiB): IRAM
I (525) spi_flash: detected chip: generic
I (529) spi_flash: flash io: dio
W (533) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
I (547) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
I (556) MAIN: Size minimo de heap libre (antes de setup()): 226324 bytes
I (616) MAIN: Size minimo de heap libre (despues de setup()): 223524 bytes
I (3616) BTDM_INIT: BT controller compile version [5688ed5]
I (3616) system_api: Base MAC address is not set
I (3616) system_api: read default base MAC address from EFUSE
I (3616) MAIN: NVS inicializado
I (3626) phy_init: phy_version 4670,719f9f6,Feb 18 2021,17:07:07
I (3736) BLE: Estado de BLE: Initializing
I (3736) BATTERY: eFuse Two Point: no soportado
I (3736) BATTERY: Characterized using eFuse Vref

I (3736) MAIN: Battery sensor status (ESP_OK)
I (3746) MAIN: MPU6050 device ID: 0
I (4066) BLE: Tabla GATT para HYDR_SVC creada, handle = 10
I (4066) BLE: Tabla GATT para BAT_SVC creada, handle = 3
I (4076) BLE: SERVICE_START_EVT, status 0, service_handle = 40
I (4076) BLE: SERVICE_START_EVT, status 0, service_handle = 50
I (4086) BLE: Advertising comenzado
I (4236) BLE: Estado de BLE: Advertising
I (4736) BLE: Estado de BLE: Advertising
I (5236) BLE: Estado de BLE: Advertising
I (5736) BLE: Estado de BLE: Advertising
I (6236) BLE: Estado de BLE: Advertising
I (6736) BLE: Estado de BLE: Advertising
I (7236) BLE: Estado de BLE: Advertising
I (7736) BLE: Estado de BLE: Advertising
I (8236) BLE: Estado de BLE: Advertising
I (8616) BLE: Estado de BLE: Advertising
I (8616) MAIN: Nuevo registro de hidratacion generado: { water: 74, temp: -2, bat: 16, time: 8}
I (8616) MAIN: Nuevo registro de hidratacion enviado para almacenamiento, total 1
I (8736) BLE: Estado de BLE: Advertising
I (8736) STORAGE: Sample key name: wtr_amount_1
I (8736) STORAGE: Sample key name: temperature_1
I (8736) STORAGE: Sample key name: battery_lvl_1
I (8746) STORAGE: Sample key name: timestamp_1
I (8766) MAIN: Registro almacenado en NVS, con indice = 1
I (9056) BLE: Estado de BLE: Advertising
I (9266) BLE: Estado de BLE: Advertising
I (9766) BLE: Estado de BLE: Advertising
I (10266) BLE: Estado de BLE: Advertising
I (10766) BLE: Estado de BLE: Advertising
I (11266) BLE: Estado de BLE: Advertising
I (11586) BLE: ESP_GATTS_CONNECT_EVT, conn_id = 0
I (11586) BLE: 77 ef 12 a7 9f 0d
I (11586) BLE: Estado de BLE: Paired
I (11766) BLE: Estado de BLE: Paired
I (11766) MAIN: Numero de registros almacenados: 1
I (11776) MAIN: Registro obtenido de NVS: (indice 0) { water: 68, temp: -9, bat: 32, time: 13}
I (11836) BLE: Estado de BLE: Paired
I (11836) BLE: Registro convertido a buffers para ser sincronizado (water_amount, temperature, battery_level, timestamp):
I (11846) BLE: 44 00
I (11846) BLE: f7 ff
I (11846) BLE: 20
I (11846) BLE: 0d 00 00 00 00 00 00 00
I (11856) BLE: ESP_GATTS_CONF_EVT, status = 0, attr_handle = 48
I (11976) BLE: Parámetros de conexión actualizados:
status = 0
min_int = 16
max_int = 32
conn_int = 24
latency = 0
timeout = 400
I (12246) BLE: Parámetros de conexión actualizados:
status = 0
min_int = 0
max_int = 0
conn_int = 6
latency = 0
timeout = 500
I (12276) BLE: Estado de BLE: Paired
I (12276) MAIN: Numero de registros almacenados: 0
I (12546) BLE: Parámetros de conexión actualizados:
status = 0
min_int = 0
max_int = 0
conn_int = 24
latency = 0
timeout = 400
I (12646) BLE: GATT_WRITE_EVT, handle = 49, value len = 2, value :
I (12646) BLE: Hydration service WRITE
I (12646) BLE: 'Notify' activado para HYDR_IDX_CHAR_NUM_NEW_RECORDS_NTF_CFG
I (12656) BLE: ESP_GATTS_CONF_EVT, status = 0, attr_handle = 48
I (12776) BLE: Estado de BLE: Paired
I (12776) MAIN: Numero de registros almacenados: 0
I (12796) BLE: Hydration service READ, attribute index = 2
I (12796) BLE: Respuesta enviada para evento de READ
I (12876) BLE: Hydration service READ, attribute index = 4
I (12886) BLE: Respuesta enviada para evento de READ
I (12946) BLE: Hydration service READ, attribute index = 6
I (12946) BLE: Respuesta enviada para evento de READ
I (13006) BLE: Hydration service READ, attribute index = 8
I (13006) BLE: Respuesta enviada para evento de READ
I (13066) BLE: Battery service READ, attribute index = 2
I (13066) BLE: Respuesta enviada para evento de READ
I (13176) BLE: GATT_WRITE_EVT, handle = 48, value len = 1, value :
I (13186) BLE: Hydration service WRITE
I (13186) BLE: Confirmado: registro fue obtenido por dispositivo periferico
I (13186) MAIN: Registro obtenido por el dispositivo periferico, restantes = 0
I (13196) BLE: Estado de BLE: Paired
I (13276) BLE: Estado de BLE: Paired
I (13276) MAIN: Numero de registros almacenados: 0
I (13456) BLE: Estado de BLE: Paired
I (13626) BLE: Estado de BLE: Paired
I (13626) MAIN: Nuevo registro de hidratacion generado: { water: 68, temp: -9, bat: 32, time: 13}
I (13626) MAIN: Nuevo registro de hidratacion enviado para sincronizacion, total 2
I (13706) BLE: Estado de BLE: Paired
I (13706) BLE: Registro convertido a buffers para ser sincronizado (water_amount, temperature, battery_level, timestamp):
I (13706) BLE: 44 00
I (13716) BLE: f7 ff
I (13716) BLE: 20
I (13716) BLE: 0d 00 00 00 00 00 00 00
I (13726) BLE: ESP_GATTS_CONF_EVT, status = 0, attr_handle = 48
I (13746) BATTERY: ADC2 Channel[7] voltage: 2130 mV
I (13746) MAIN: Carga restante de la bateria: 0%
I (13776) BLE: Estado de BLE: Paired
I (13776) MAIN: Numero de registros almacenados: 0
I (14276) BLE: Estado de BLE: Paired
I (14276) MAIN: Numero de registros almacenados: 0
I (14776) BLE: Estado de BLE: Paired
I (14776) MAIN: Numero de registros almacenados: 0
I (15276) BLE: Estado de BLE: Paired
I (15276) MAIN: Numero de registros almacenados: 0
I (15776) BLE: Estado de BLE: Paired
I (15776) MAIN: Numero de registros almacenados: 0
I (16276) BLE: Estado de BLE: Paired
I (16276) MAIN: Numero de registros almacenados: 0
I (16776) BLE: Estado de BLE: Paired
I (16776) MAIN: Numero de registros almacenados: 0
I (17276) BLE: Estado de BLE: Paired
I (17276) MAIN: Numero de registros almacenados: 0
I (17776) BLE: Estado de BLE: Paired
I (17776) MAIN: Numero de registros almacenados: 0
I (18276) BLE: Estado de BLE: Paired
I (18276) MAIN: Numero de registros almacenados: 0
I (18636) BLE: Estado de BLE: Paired
I (18636) MAIN: Nuevo registro de hidratacion generado: { water: 536, temp: 12, bat: 19, time: 18}
I (18636) MAIN: Nuevo registro de hidratacion enviado para sincronizacion, total 3
W (18726) MAIN: Registro sincronizado, pero no fue obtenido por dispositivo periferico, restantes = 1
I (18726) BLE: Estado de BLE: Paired
I (18726) BLE: Registro convertido a buffers para ser sincronizado (water_amount, temperature, battery_level, timestamp):
I (18736) BLE: 44 00
I (18746) BLE: f7 ff
I (18746) BLE: 20
I (18746) BLE: 0d 00 00 00 00 00 00 00
I (18756) BLE: ESP_GATTS_CONF_EVT, status = 0, attr_handle = 48
I (18776) BLE: Estado de BLE: Paired
I (18776) MAIN: Numero de registros almacenados: 0
I (19276) BLE: Estado de BLE: Paired
I (19276) MAIN: Numero de registros almacenados: 0
I (19776) BLE: Estado de BLE: Paired
I (19776) MAIN: Numero de registros almacenados: 0
I (20276) BLE: Estado de BLE: Paired
I (20276) MAIN: Numero de registros almacenados: 0
I (20776) BLE: Estado de BLE: Paired
I (20776) MAIN: Numero de registros almacenados: 0
I (21276) BLE: Estado de BLE: Paired
I (21276) MAIN: Numero de registros almacenados: 0
I (21776) BLE: Estado de BLE: Paired
I (21776) MAIN: Numero de registros almacenados: 0
I (22276) BLE: Estado de BLE: Paired
I (22276) MAIN: Numero de registros almacenados: 0
I (22776) BLE: Estado de BLE: Paired
I (22776) MAIN: Numero de registros almacenados: 0
I (23276) BLE: Estado de BLE: Paired
I (23276) MAIN: Numero de registros almacenados: 0
I (23646) BLE: Estado de BLE: Paired
I (23646) MAIN: Nuevo registro de hidratacion generado: { water: 642, temp: 34, bat: 25, time: 23}
I (23646) MAIN: Nuevo registro de hidratacion enviado para sincronizacion, total 4
I (23746) BATTERY: ADC2 Channel[7] voltage: 2091 mV
I (23746) MAIN: Carga restante de la bateria: 0%
W (23756) MAIN: Registro sincronizado, pero no fue obtenido por dispositivo periferico, restantes = 1
I (23756) BLE: Estado de BLE: Paired
I (23756) BLE: Registro convertido a buffers para ser sincronizado (water_amount, temperature, battery_level, timestamp):
I (23766) BLE: 44 00
I (23776) BLE: f7 ff
I (23776) BLE: 20
I (23776) BLE: 0d 00 00 00 00 00 00 00
I (23786) BLE: Estado de BLE: Paired
I (23786) BLE: ESP_GATTS_CONF_EVT, status = 0, attr_handle = 48
I (23786) MAIN: Numero de registros almacenados: 0
I (24296) BLE: Estado de BLE: Paired
I (24296) MAIN: Numero de registros almacenados: 0
I (24796) BLE: Estado de BLE: Paired
I (24796) MAIN: Numero de registros almacenados: 0
I (25296) BLE: Estado de BLE: Paired
I (25296) MAIN: Numero de registros almacenados: 0
I (25796) BLE: Estado de BLE: Paired
I (25796) MAIN: Numero de registros almacenados: 0
I (26296) BLE: Estado de BLE: Paired
I (26296) MAIN: Numero de registros almacenados: 0
I (26796) BLE: Estado de BLE: Paired
I (26796) MAIN: Numero de registros almacenados: 0
I (27296) BLE: Estado de BLE: Paired
I (27296) MAIN: Numero de registros almacenados: 0
I (27796) BLE: Estado de BLE: Paired
I (27796) MAIN: Numero de registros almacenados: 0
I (28296) BLE: Estado de BLE: Paired
I (28296) MAIN: Numero de registros almacenados: 0
I (28656) BLE: Estado de BLE: Paired
I (28656) MAIN: Nuevo registro de hidratacion generado: { water: 200, temp: -5, bat: 60, time: 28}
I (28656) MAIN: Nuevo registro de hidratacion enviado para sincronizacion, total 5
W (28786) MAIN: Registro sincronizado, pero no fue obtenido por dispositivo periferico, restantes = 1
I (28786) BLE: Estado de BLE: Paired
I (28786) BLE: Registro convertido a buffers para ser sincronizado (water_amount, temperature, battery_level, timestamp):
I (28796) BLE: 44 00
I (28806) BLE: f7 ff
I (28806) BLE: 20
I (28806) BLE: 0d 00 00 00 00 00 00 00
I (28816) BLE: Estado de BLE: Paired
I (28816) BLE: ESP_GATTS_CONF_EVT, status = 0, attr_handle = 48
I (28816) MAIN: Numero de registros almacenados: 0
I (29326) BLE: Estado de BLE: Paired
I (29326) MAIN: Numero de registros almacenados: 0
I (29826) BLE: Estado de BLE: Paired
I (29826) MAIN: Numero de registros almacenados: 0
I (30326) BLE: Estado de BLE: Paired
I (30326) MAIN: Numero de registros almacenados: 0
I (30826) BLE: Estado de BLE: Paired
I (30826) MAIN: Numero de registros almacenados: 0
I (31326) BLE: Estado de BLE: Paired
I (31326) MAIN: Numero de registros almacenados: 0
I (31826) BLE: Estado de BLE: Paired
I (31826) MAIN: Numero de registros almacenados: 0
I (32326) BLE: Estado de BLE: Paired
I (32326) MAIN: Numero de registros almacenados: 0
I (32826) BLE: Estado de BLE: Paired
I (32826) MAIN: Numero de registros almacenados: 0
I (33326) BLE: Estado de BLE: Paired
I (33326) MAIN: Numero de registros almacenados: 0
I (33666) BLE: Estado de BLE: Paired
I (33666) MAIN: Nuevo registro de hidratacion generado: { water: 132, temp: -35, bat: 47, time: 33}
I (33666) MAIN: Nuevo registro de hidratacion enviado para sincronizacion, total 6
I (33746) BATTERY: ADC2 Channel[7] voltage: 2110 mV
I (33746) MAIN: Carga restante de la bateria: 0%
W (33816) MAIN: Registro sincronizado, pero no fue obtenido por dispositivo periferico, restantes = 1
I (33816) BLE: Estado de BLE: Paired
I (33816) BLE: Registro convertido a buffers para ser sincronizado (water_amount, temperature, battery_level, timestamp):
I (33826) BLE: 44 00
I (33836) BLE: f7 ff
I (33836) BLE: 20
I (33836) BLE: 0d 00 00 00 00 00 00 00
I (33846) BLE: Estado de BLE: Paired
I (33846) BLE: ESP_GATTS_CONF_EVT, status = 0, attr_handle = 48
I (33846) MAIN: Numero de registros almacenados: 0
I (34356) BLE: Estado de BLE: Paired
I (34356) MAIN: Numero de registros almacenados: 0
I (34856) BLE: Estado de BLE: Paired
I (34856) MAIN: Numero de registros almacenados: 0
I (35356) BLE: Estado de BLE: Paired
I (35356) MAIN: Numero de registros almacenados: 0
I (35856) BLE: Estado de BLE: Paired
I (35856) MAIN: Numero de registros almacenados: 0
I (36356) BLE: Estado de BLE: Paired
I (36356) MAIN: Numero de registros almacenados: 0
I (36856) BLE: Estado de BLE: Paired
I (36856) MAIN: Numero de registros almacenados: 0
I (37356) BLE: Estado de BLE: Paired
I (37356) MAIN: Numero de registros almacenados: 0
I (37856) BLE: Estado de BLE: Paired
I (37856) MAIN: Numero de registros almacenados: 0
I (38356) BLE: Estado de BLE: Paired
I (38356) MAIN: Numero de registros almacenados: 0
I (38676) BLE: Estado de BLE: Paired
I (38676) MAIN: Nuevo registro de hidratacion generado: { water: 410, temp: 13, bat: 0, time: 38}
W (38726) MAIN: Un nuevo registro 6 no pudo ser enviado para sincronizacion (errQUEUE_FULL)
W (38846) MAIN: Registro sincronizado, pero no fue obtenido por dispositivo periferico, restantes = 1
I (38846) BLE: Estado de BLE: Paired
I (38856) BLE: Estado de BLE: Paired
I (38856) MAIN: Numero de registros almacenados: 0
I (39356) BLE: Estado de BLE: Paired
I (39356) MAIN: Numero de registros almacenados: 0
I (39856) BLE: Estado de BLE: Paired
I (39856) MAIN: Numero de registros almacenados: 0
I (40356) BLE: Estado de BLE: Paired
I (40356) MAIN: Numero de registros almacenados: 0
I (40856) BLE: Estado de BLE: Paired
I (40856) MAIN: Numero de registros almacenados: 0

Bugs en validación durante creación de meta

Al enviar el formulario, solo se verifica el primer campo. Cuando los demás campos tienen valores incorrectos y el primero si está bien, el proceso sigue como si nada.

Tampoco muestra los mensajes de error al enviar el formulario.

[BUG] Ciertas llamadas a la API REST producen excepciones no manejadas cuando el dispositivo no tiene conexión a internet.

Descripción del Error
Cuando el dispositivo móvil no tiene acceso a conexión a internet, ciertas llamadas a la API REST hechas por la app resultan en IOException y SocketException que no son manejadas. Este problema ocurre para cualquier invocación a la API de sincronización de datos (perfil, configuración y datos).

Cómo Reproducir
Pasos para reproducir el error:

  1. Desactivar la conexión a internet del dispositivo.
  2. Realizar alguna acción que requiera sincronización (obtener/modificar configuración, perfil, agregar datos)
  3. Observar excepción no manejada.

Comportamiento Esperado
La app debería manejar excepciones producidas por el cliente HTTP al intentar realizar peticiones sin conexión a internet. Sería bueno que la app notificara al usuario de estos problemas de sincronización.

Entorno

  • Dispositivo: Galaxy A30s
  • OS: Android 11
  • Versión de la app: 1.0.0+1

Contexto Adicional
Las invocaciones a la API que no manejan IOException ni SocketException son:

  • _updateProfileWithLocalChanges()
  • syncUpdatedHydrationGoalsWithAccount()
  • syncSettingsWithLocalChanges()
  • syncLocalActivityRecordsWithAccount()

Es importante arreglar estos problemas, y evitar hacer el mismo error cuando implemente la obtención de datos sincronizados.

[BUG] Al crear una meta, las etiquetas que "ya fueron creadas" no son referenciadas y se crean duplicados

Descripción del Error
El método Goal.parseTags() asocia incorrectamente las etiquetas escritas por el usuario con las etiquetas creadas con anterioridad: cuando revisa la colección de etiquetas existentes para ver si contiene el valor de la etiqueta escrita, lo hace una sola vez con el primer carácter de la etiqueta que el usaurio está escribiendo. Esto hace que ninguna etiqueta escrita pueda asociarse con las etiquetas creadas.

Cómo Reproducir
Pasos para reproducir el error:

  1. Navegar a la vista de creación de nueva meta.
  2. Escribir una, dos o tres etiquetas en el campo adecuado, separadas por comas.
  3. Crear la meta.
  4. Repetir el proceso, introduciendo exactamente las mismas etiquetas que la primera vez.
  5. Si se analizan las etiquetas creadas, hay registros duplicados.

Comportamiento Esperado
Al crear una nueva meta que tiene una o varias etiquetas creadas con anterioridad, la meta debería hacer referencia a los registros existentes, en vez de volver a insertar las mismas etiquetas en la base de datos.

Entorno

  • Dispositivo: Galaxy A30
  • OS: Android 11
  • Versión de la app: 1.0.0-beta+3

[BUG] El dropdown para país en `ProfileForm` muestra muchas opciones de "Prefiero no especificar"

Descripción del Error
El dropdown de país obtiene los países soportados desde la base de datos local. Si el código de país de una de las opciones es igual a "--", el texto de la opción queda como "Prefiero no especificar". El dropdown muestra más opciones de las que debería, como si en algún punto se estuvieran registrando nuevos países por defecto en la BD.

Cómo Reproducir
Pasos para reproducir el error:

  1. Navegar a la vista de perfil, o a la vista de formulario inicial.
  2. Observar el campo con dropdown para país.
  3. Presionar el dropdown para cambiar el país seleccionado.
  4. Notar como hay más opciones de las que debería haber.

Comportamiento Esperado
Esto solo debe pasar con el primero, que representa la opción de un país no especificado. Acciones en la base de datos no deberían modificar la tabla de países.

Entorno

  • Dispositivo:Galaxy A30
  • OS: Android 11
  • Versión de la app: 1.0.0-beta+3

Contexto Adicional
El problema posiblemente está relacionado con la base de datos y SQLiteDB, porque fue observado tras realizar pruebas que insertaban perfiles vacíos en la base de datos. Hay un problema similar con las etiquetas de metas de hidratación.

[BUG] Excepción no manejada en `MainView`: "Looking up a deactivated widget's ancestor is unsafe."

Descripción del Error
El método _showGuidesDialog() de MainView produce una excepción no manejada al usar un contexto obsoleto (probablemente después de un async gap). Este error también es provocado por el método _showDialogIfReportAvailable().

Cómo Reproducir
Pasos para reproducir el error:

  1. Abrir la app.
  2. Navegar a una vista que no sea MainView (como la vista de login o la de ajustes).
  3. Regresar a la MainView
  4. Ver el error en debug output.

Comportamiento Esperado
El método _showGuidesDialog() debe mostrar, bajo ciertas condiciones, un AlertDialog preguntando al usuario si desea consultar las guías de usuario online. No debería acceder a un BuildContext obsoleto.

Entorno

  • Dispositivo: Galaxy A30s
  • OS: Android 11
  • Versión de la app: 1.0.0+1

Contexto Adicional
Mensaje de la excepción en la consola:

E/flutter (11861): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: Looking up a deactivated widget's ancestor is unsafe.
E/flutter (11861): At this point the state of the widget's element tree is no longer stable.
E/flutter (11861): To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.
E/flutter (11861): #0      Element._debugCheckStateIsActiveForAncestorLookup.<anonymous closure>
package:flutter/…/widgets/framework.dart:4186
E/flutter (11861): #1      Element._debugCheckStateIsActiveForAncestorLookup
package:flutter/…/widgets/framework.dart:4200
E/flutter (11861): #2      Element.getElementForInheritedWidgetOfExactType
package:flutter/…/widgets/framework.dart:4226
E/flutter (11861): #3      Provider._inheritedElementOf
package:provider/src/provider.dart:339
E/flutter (11861): #4      Provider.of
package:provider/src/provider.dart:293
E/flutter (11861): #5      MainView._showGuidesDialog
package:hydrate_app/…/views/main_view.dart:27
E/flutter (11861): #6      MainView.build.<anonymous closure>
package:hydrate_app/…/views/main_view.dart:94
E/flutter (11861): #7      new Future.delayed.<anonymous closure> (dart:async/future.dart:423:39)
E/flutter (11861): #8      _rootRun (dart:async/zone.dart:1418:47)
E/flutter (11861): #9      _CustomZone.run (dart:async/zone.dart:1328:19)
E/flutter (11861): #10     _CustomZone.runGuarded (dart:async/zone.dart:1236:7)
E/flutter (11861): #11     _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1276:23)
E/flutter (11861): #12     _rootRun (dart:async/zone.dart:1426:13)
E/flutter (11861): #13     _CustomZone.run (dart:async/zone.dart:1328:19)
E/flutter (11861): #14     _CustomZone.bindCallback.<anonymous closure> (dart:async/zone.dart:1260:23)
E/flutter (11861): #15     Timer._createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:18:15)
E/flutter (11861): #16     _Timer._runTimers (dart:isolate-patch/timer_impl.dart:398:19)
E/flutter (11861): #17     _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:429:5)
E/flutter (11861): #18     _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:192:12)

[BUG] La cuenta de monedas mostrada por el widget `CoinDisplay` no parece correcta

Descripción del Error
Durante pruebas de la app, he notado que el número de monedas mostrado por CoinDisplay no es correcto; la mayoría de las veces es "3", un número poco común para haber sido obtenido como recompensa por hidratación o actividad.

Cómo Reproducir
Pasos para reproducir el error:

  1. Abrir la aplicación.
  2. Navegar a una pestaña de la vista principal que muestre un CoinDisplay en su appbar superior, como las pestañas de inicio o perfil.
  3. Observar la cantidad extraña.

Comportamiento Esperado
El widget debería mostrar una cantidad de monedas más normal, como 0, 50 o lo que haya conseguido el usuario por recompensas.

**Entorno **

  • Dispositivo: Galaxy A30
  • OS: Android 11
  • Versión de la app: v1.0.0-beta+1

Contexto Adicional
El widget CoinDisplay obtiene el número de monedas del usuario al consumir el estado de ProfileProvider. En el widget en sí no parece que haya problemas, entonces es probable que el problema sea 1 de 2 posibilidades:

  1. ProfileProvider o el modelo UserProfile alteran el valor de profile.coins.
  2. En algún punto hubo una actualización de datos de perfil que almacenó el número anormal en la BD. En este caso, habría que revisar todos los puntos donde se guardan los cambios al perfil para asegurar que el problema ya no esté.

[FEATURE] Mejorar el algoritmo de hidratación recomendada

La funcionalidad que sugieres surgió de un problema? Por favor descríbelo.
La funcionalidad no surge de un problema o error en sí, sino que es una mejora para cumplir totalmente los requisitos y la especificación para la función de recomendación de hidratación. Por ahora, la implementación incluye varios de los factores, pero todavía es necesario hacerla más precisa, legible y luego optimizarla.

Describe la solución que te gustaría
Falta considerar ciertos factores al aproximar la ingesta de agua recomendada:

  • Definir el plazo de tiempo (diaria, semanal o mensual).
  • Determinar claramente cuando se tiene que recalcular (cuando cambia alguno de los valores de entrada) y cuando se usa simplemente el mismo valor. Revisar la manera en que se toma en cuenta la sugerencia previa.
  • Mejorar el incremento por actividad física.
  • Mejorar el incremento por temperatura.
  • Crear varias pruebas unitarias para verificar el algoritmo bajo diversas condiciones y entradas.
  • Mejorar la forma en que los datos de múltiples servicios son obtenidos para determinar la hidratación recomendada.

[BUG] El cliente de API lanza un `ClientException` que no es manejado, cuando se interrumpe la conexión durante una petición.

Descripción del Error.
Cuando la conexión a internet es interrumpida mientras la app está en proceso de hacer una petición HTTP, una ClientException es lanzada y no es manejada con un try/catch.

Cómo Reproducir
Pasos para reproducir el error:

  1. Iniciar la app.
  2. Cuando la app esté haciendo una petición HTTP para obtener recursos informativos, desactivar el Wifi del dispositivo
  3. Esperar un momento.
  4. Observar como la app produce un ClientException que no es manejado

Comportamiento Esperado
La app debería manejar con gracia las posibles excepciones producidas por causas externas (como una interrupción de conectividad). No debería crashear con ellas.

Entorno

  • Dispositivo: Galaxy A30
  • OS: Android 11
  • Versión de la app: 1.0.0-beta+2

Contexto Adicional
Por ejemplo, cuando el método ArticlesApi.fetchArticles() es invocado para obtener los recursos informativos desde la API web y la conexión a internet es interrumpida durante la petición, la siguiente excepción es lanzada en la línea 34:

ClientException (Software caused connection abort)

Esto debería ser manejado cachando la excepción e indicando visualmente al usuario que los recursos informativos no pudieron ser obtenidos por falta de conexión a internet.

[BUG] Se registran nuevas entradas en la tabla de entornos y perfiles, cuando ya existe la entrada necesaria

Descripción del Error
Parece que con cada ejecución de la app, de alguna manera se agregan nuevos registros en la tabla muchos a muchos entre perfiles y entornos. Los registros son idénticos (referencian al mismo perfil de usuario y el mismo entorno). Esto provoca que métodos como UserProfile.selectedEnvironment produzcan errores inesperados.

Cómo Reproducir
Pasos para reproducir el error:

  1. Abrir la aplicación
  2. Cerrar la aplicación
  3. Abrir la app nuevamente
  4. Ver el error

Comportamiento Esperado
La app agrega un solo registro a la tabla muchos a muchos, cuando se crea un nuevo perfil de usuario. Este registro referencia el nuevo perfil con el entorno por defecto (el entorno que es desbloqueado gratis, para todos).

**Entorno **

  • Dispositivo: Galaxy A30
  • OS: Android 11
  • Versión de la app: 1.0.0-beta+0

Contexto Adicional
El total de registros en la tabla debe equivaler a la suma del número de perfiles desbloqueados de cada perfil de la app.

También, el getter UserProfile.selectedEnvironment produce un error cuando unlockedEnvironments no está vacío, pero el ID de selectedEnvId no coincide con ninguno de los entornos desbloqueados. Actualmente, esto genera un StateError. Quizás es más apropiado que simplemente modifique selectedEnvId a un valor correcto y retorne el entorno por defecto.

[BUG] El progreso de hidratación no se muestra cuando una meta fue creada el mismo día

Descripción del Error
Si el usuario crea una meta de hidratación con una fecha de inicio del mismo día y luego registra uno o más consumos de agua ese mismo día, la barra de progreso de la meta no refleja la cantidad de agua ingerida en ese día.

Cómo Reproducir
Pasos para reproducir el error:

  1. Crear una meta con el día de hoy como fecha de inicio.
  2. Regresar a la vista principal. Observar que se muestra la meta, pero la barra de progreso está vacía.
  3. Registrar un nuevo consumo de agua ese mismo día.
  4. En la vista principal, la barra de progreso sigue estando vacía.

Comportamiento Esperado
Todas las metas deberían actualizar su progreso cada vez que la app registra un nuevo consumo de agua. Este cambio debe ser reflejado en la UI y en los datos internos (en la otorgación de recompensas, definición de metas sugeridas, etc.)

Entorno

  • Dispositivo: Galaxy A30
  • OS: Android 11
  • Versión de la app: 1.0.0-beta+2

Contexto Adicional
Revisar los rangos de fechas usados para obtener el progreso de una meta, en el método getGoalProgressInMl() de HydrationRecordProvider.

[BUG] Problemas con el momento y forma en que `ReportAvailableDialog` es mostrado

Descripción del Error
Parece que los dialog mostrados por ReportAvailableDialog aparecen demasiadas veces, incluso cuando el usuario ya completó el formulario correspondiente. Además, los dialogs para formularios recurrentes y médicos aparecen al mismo tiempo, por lo que uno tapa al otro.

Cómo Reproducir
Pasos para reproducir el error:

  1. Abrir la app
  2. Esperar un momento
  3. Observar que varios dialogs aparecen simultáneamente
  4. Responder el formulario asociado a uno de los dialogs
  5. Regresando a la vista principal, el mismo dialog vuelve a ser mostrado
  6. A veces, dos dialogs son mostrados al mismo tiempo y se enciman

Comportamiento Esperado
Los dialogs de ReportAvailableDialog deberían ser mostrados una vez por apertura de la app cuando sea necesario, no "encimarse" uno sobre otro y deberían registrar que el usuario ya respondió el formulario.

**Entorno **

  • Dispositivo: Galaxy A30
  • OS: Android 11
  • Versión de la app: 1.0.0-beta+2

[BUG] La sincronización de perfil falla por el ID del país de residencia

Descripción del Error
Cuando la app intenta sincronizar el perfil de usuario con su cuenta a través de la API REST de perfiles, obtiene un error 404 mencionando que "no existe un pais con el ID especificado".

Cómo Reproducir
Pasos para reproducir el error:

  1. Abrir la app, en su versión 1.0.0+1
  2. Verificar que exista una conexión a internet disponible
  3. Iniciar sesión con una cuenta de usuario (o registrar una nueva cuenta)
  4. Realizar un cambio al perfil de usuario activo (editar información, obtener/gastar monedas, sincronizar con Google Fit)
  5. Observar el mensaje de error, indicando que la sincronización de perfil no pudo ser completada.

Comportamiento Esperado
Si el usuario ha iniciado sesión y tiene un perfil asociado a su cuenta, cuando realiza modificaciones al perfil, los cambios deberían poder ser sincronizados con su cuenta a través de la API de perfiles (excepto cuando no tiene acceso a internet).

Entorno

  • Dispositivo: Galaxy A30s
  • OS: Android 11
  • Versión de la app: 1.0.0+1

Contexto Adicional
El mensaje de error mostrado es como el siguiente:

I/flutter ( 8840): Error al sincronizar cambios a perfil (ApiException:(requestError, status = 404) Request format is incorrect, details: "No existe un pais con el ID especificado")

[BUG] El widget `ArticleSliverList` no muestra un indicador cuando no hay conexión a internet

Descripción del Error
Con los cambios para los resultados paginados de recursos informativos, se removieron los indicadores de error de carga de ArticleSliverList y la app no hace aparente cuando no puede obtener los recursos porque no tiene conexión a internet.

Cómo Reproducir
Pasos para reproducir el error:

  1. Navegar a la pestaña de "Descubrir recursos informativos"
  2. Desconectar el dispositivo móvil de internet
  3. Esperar el tiempo típico de carga de artículos
  4. Ver el error

Comportamiento Esperado
El widget debería mostrar indicadores de condiciones anormales, como errores de conexión, base de datos, servicio web inalcanzable.

**Entorno **

  • Dispositivo: Galaxy A30
  • OS: Android 11
  • Versión de la app: 1.0.0-beta+1

Contexto Adicional
Es probable que otras vistas como la de inicio de sesión necesiten indicadores más claros

[BUG] Cancelar el dialog de fecha al crear una nueva meta produce una `DatabaseException`

Descripción del Error
Cuando el usuario intenta crear una nueva meta y le asigna fechas de inicio y fin, si el usuario vuelve a abrir el DatePicker y presiona el botón "cancelar", la fecha respectiva toma un valor null y no se incluye en el query para insertar la meta en la BD local. Esto produce un DatabaseException.

Cómo Reproducir
Pasos para reproducir el error:

  1. Navegar a la vista de creación de nuevas metas de hidratación.
  2. Completar el formulario correctamente.
  3. Ya sea en la fecha inicial o en la fecha final, abrir nuevamente su DatePicker y presionar el botón "cancelar".
  4. Presionar el botón "crear".
  5. Notar el error de BD.

Comportamiento Esperado
El usuario debería poder cancelar un DatePicker sin "eliminar" el valor previo. Debe haber congruencia entre el modelo de Goal y su esquema en la BD local (así como con su esquema de la API REST).

Entorno

  • Dispositivo: Galaxy A30s
  • OS: Android 11
  • Versión de la app: 1.0.0+1

Contexto Adicional
El error se repite infinitamente, como si la app siguiera intentando insertar la nueva meta indefinidamente.

[BUG] `SQLiteDB.insert()` produce registros incorrectos para relaciones muchos a muchos

Descripción del Error
Cuando el método SQLiteDB.insert() crea los registros necesarios para describir una relación muchos a muchos usada por alguna entidad, parece que crea registros duplicados en la tabla muchos a muchos.

Cómo Reproducir
Pasos para reproducir el error:

  1. Crear una nueva entidad que use relaciones muchos a muchos (probado con perfil, que se relaciona con varios entornos)
  2. Persistir la entidad en la base de datos, usando SQLiteDB.insert().
  3. Obtener los registros insertados en la tabla para relación muchos a muchos.
  4. Ver el error

Comportamiento Esperado
El método debería insertar sólo los registros necesarios en la tabla de relación muchos a muchos.

**Entorno **

  • Dispositivo: Galaxy A30
  • OS: Android 11
  • Versión de la app: 1.0.0-beta+0

La gráfica con los totales diarios de KCal quemadas no incluye datos de actividades rutinarias.

Problema

La gráfica de kilocalorías quemadas por día en los últimos siete días muestra información correcta cuando los datos son de ActivityRecords directamente. Cuando hay registros de actividad que son producidos por una actividad rutinaria, las KCal de estos no son tomadas en cuenta para los totales.

La gráfica obtiene sus datos de ActivityProvider, invocando su método _previousSevenDaysTotals(). Posiblemente este método no ha sido actualizado para tomar en cuenta actividades rutinarias.

[BUG] No puede guardarse la configuración para activar los formularios recurrentes

Descripción del Error
Cuando se modifica el valor del switch para activar/desactivar los formularios semanales, no aparece el snackbar de guardar cambios. Además, aún cuando se modifica otro valor y sí aparece la snackbar, al presionar "guardar" los cambios en la configuración de formularios semanales no son guardados.

Cómo Reproducir
Pasos para reproducir el error:

  1. Ir a la vista de configuración
  2. Modificar el valor del switch de formularios recurrentes
  3. Observar que no aparece el snackbar para confirmar cambios no aparece
  4. Modificar cualquier otro valor de configuración
  5. Presionar el botón "guardar" que aparece en el snackbar
  6. Salir de la vista de configuración
  7. Volver a entrar en configuración
  8. Observar que el valor de activar/desactivar formularios recurrentes no ha cambiado

Comportamiento Esperado
Al cambiar el valor del switch de activación de formularios recurrentes, debería aparecer el snackbar de confirmación de cambios. Cuando se presiona el botón de "guardar" en el snackbar de confirmación, los cambios deben ser persistidos, para mantener su valor en reinicios o durante el uso de la app.

**Entorno **

  • Dispositivo: Galaxy A30
  • OS: Android 11
  • Versión de la app: 1.0.0-beta+2

El progreso total de una meta no es correcto

Problema

El indicador de progreso lineal de todas las metas esta completado al 100% (su valor es 1.0). Es raro, dados los totales de los registros de hidratación de días pasados y los periodos de las metas.

Tareas

  • Imprimir los progresos obtenidos en el sliver list del GUI.
  • Revisar los valores producidos por getGoalsProgressValuesInMl() y _totalsFromPrevDaysInMl() en el debugger.

Problema con INSERT y columna para ID del perfil

Problema

Cuando se inserta un nuevo registro de una entidad que tiene una columna para el ID del perfil, la app lanza una excepción:

SqfliteDatabaseException (DatabaseException(table actividad has no column named perfil (code 1 SQLITE_ERROR[1]): , while compiling: INSERT INTO actividad (titulo, fecha, duracion, distancia, kilocalorias_quemadas, perfil, al_aire_libre, es_rutina, id_tipo_actividad) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?))

Lo más probable es que, debido a que MapOptions.includeCompleteSubEntities = true, todas los atributos de la entidad que hagan referencia a otra entidad cambian su nombre de "id_otra_entidad" a "otra_entidad".

Solución

  • Asegurar que el uso de MapOptions en SQLiteDB sea consistente.
  • Revisar caso especial con "id_perfil", si hace falta.

[BUG] Error visual al intentar crear una cuenta con las credenciales de una cuenta existente desde la app

Descripción del Error
Cuando se completa correctamente el formulario de creación de cuenta y se envía, produce un error de "credenciales incorrectas", a pesar de que las credenciales tengan formato correcto. Esto se debe a que el formulario tiene las credenciales de una cuenta existente. La indicación de error es poco clara y parece un error en sí.

Cómo Reproducir
Pasos para reproducir el error:

  1. Navegar a la vista de creación de cuenta.
  2. Introducir las credenciales de una cuenta de usuario ya creada.
  3. Enviar el formulario, presionando el botón "continuar".
  4. Observar el indicador de error "Tus credenciales son incorrectas".

Comportamiento Esperado
El formulario de creación de cuenta debería proporcionar un indicador de error más claro, sin comprometer la seguridad de una cuenta existente (e.g. no decir que el nombre de usuario ya está en uso).

Entorno

  • Dispositivo: Galaxy A30
  • OS: Android 11
  • Versión de la app: 1.0.0-beta+3

[BUG] Parece que la funcionalidad para compartir el progreso del usuario no ha sido implementada

Descripción del Error
Creía que la función para compartir el progreso de hidratación en otras apps ya había sido implementada, pero parece que quedó perdida en una rama alterna o en el repositorio local perdido alrededor de mayo.

Comportamiento Esperado
La app debe permitir al usuario compartir su progreso en apps de redes sociales (Facebook y Twitter, principalmente).

Entorno

  • Dispositivo: Galaxy A30s
  • OS: Android 11
  • Versión de la app: 1.0.0+1

[BUG] Usar el constructor factory `UserCredentials.forAction()` para login deja `username` y `email` vacíos

Descripción del Error
El constructor UserCredentials.forAction() completa incorrectamente los atributos de la nueva instancia de UserCredentials cuando authAction es AuthActionType.signIn y el valor del argumento username no puede ser usado como e-mail. Esto resulta en que tanto username y email queden como Strings vacíos, en vez de simplemente usar el username.

Cómo Reproducir
Pasos para reproducir el error:

  1. Navegar a la vista de inicio de sesión.
  2. Introducir un nombre de usuario (no un email) y contraseña válidos.
  3. Presionar el botón "continuar".
  4. El método UserCredentials.toMap() lanza un ArgumentError.

Comportamiento Esperado
Cuando authAction es para iniciar sesión, este método debería crear una instancia de credentials que use el valor del argumento username como para el atributo email solo cuando este valor tenga la forma de un email. Por el contrario, debe asignar el valor del argumento username a su atributo username.

Entorno

  • Dispositivo: Galaxy A30
  • OS: Android 11
  • Versión de la app: 1.0.0+3

[BUG] El formulario inicial no muestra el campo para seleccionar ocupación

Descripción del Error
Cuando ProfileForm es usado como formulario inicial, no muestra el campo para seleccionar la ocupación del usuario.

Cómo Reproducir
Pasos para reproducir el error:

  1. Instalar la app en un dispositivo.
  2. Abrir la app, para que muestre la vista de formulario inicial.
  3. Observar que el formulario no incluye ningún campo para seleccionar la ocupación

Comportamiento Esperado
ProfileForm incluye un campo para seleccionar ocupación cuando es usado para la creación inicial de un perfil. ProfileForm no muestra este campo cuando es usado para modificar un perfil existente.

**Entorno **

  • Dispositivo: Galaxy A30
  • OS: Android 11
  • Versión de la app: 1.0.0-beta+2]

Contexto Adicional
Por este error, cuando se crea un nuevo perfil el valor asignado a la ocupación del perfil siempre es "sin especificar" (el valor por defecto)

[BUG] Al obtener un `Article` desde la API web, la app no puede interpretar correctamente la fecha

Descripción del Error
Para los artículos producidos por la API web de recursos informativos, la app no puede hacer interpretar los publishDate desde strings ISO 8601.

Cómo Reproducir
Pasos para reproducir el error:

  1. Navegar a la pestaña "Descubrir" en la sección de recursos informativos.
  2. Esperar a que carguen los recursos informativos de la web.
  3. Ver que todos los recursos son mostrados como "Sin Fecha".

Comportamiento Esperado
La fecha de publicación de todos los artículos debería poder ser interpretada como un valor de string ISO 8601 y las tarjetas de recurso informativo deberían mostrar esa misma fecha.

**Entorno **

  • Dispositivo: Galaxy A30
  • OS: Android 11
  • Versión de la app: v1.0.0-beta+1

Contexto Adicional
El error está relacionado con inconsistencias en la representación de fechas en formato ISO 8601 entre la app y la API web.

[BUG] Problemas con selección de rango de tiempo en método `_getActivitiesFromPastWeek()`

Descripción del Error
La manera en que el método filtra los registros de actividad según fecha es poco coherente, hace falta revisar la forma en que determina los rangos de tiempo (usando includeToday, considerando el cambio de horario,

Cómo Reproducir
Pasos para reproducir el error:

  1. Abrir la app.
  2. Navegar a la vista de historial, en la pestaña de actividad física.
  3. Notar que la gráfica de que el total de kCal para el día actual siempre es 0, a pesar de que la gráfica tiene una barra para el.
  4. Crear un nuevo registro de actividad.
  5. Notar que la inferencia de rutinas no considera actividades realizadas el día de la semana anterior correspondiente al día actual (si el día de hoy es lunes, las actividades registradas el lunes de la semana pasada no son tomadas en cuenta).
  6. Observar que la app siempre otorga recompensa por registrar una actividad (en vez de solo las primeras tres de un día).

Comportamiento Esperado
El método _getActivitiesFromPastWeek() debería hacer más clara la forma en que determina el rango de días considerado como "la semana pasada", o adoptar un modelo como _totalsFromPrevDaysInMl() que es mucho más flexible.

Entorno

  • Dispositivo: Galaxy A30s
  • OS: Android 11
  • Versión de la app: 1.0.0+1

[BUG] El procesamiento de eventos no se detiene cuando la app se desconecta del dispositivo

Descripción del Error
Cuando la app es desconectada de la extensión para botellas (usando el método disconnect() de flutter_blue), parece que sigue procesando eventos por un rato, invocando el método callback _onRecordAvailable. Después de algunos momentos, los eventos dejan de producirse y la app vuelve a su estado normal.

Cómo Reproducir
Pasos para reproducir el error:

  1. Conectar la app con la extensión para botellas, a través de BLE.
  2. Recibir registros de hidratación.
  3. Desconectar la app de la extensión para botellas.
  4. Notar como se siguen procesando eventos del stream, lo que invoca _onRecordAvailable constantemente.

Comportamiento Esperado
La app debería cancelar cualquier procesamiento de eventos para sincronizar registros de hidratación, cuando la extensión para botellas sea desconectada.

Entorno

  • Dispositivo: Galaxy A30s
  • OS: Android 11
  • Versión de la app: 1.0.0-beta+6

Contexto Adicional
Logs de la app:

I/FlutterBluePlugin(27267): [onCharacteristicRead] uuid: 00000fff-0000-1000-8000-00805f9b34fb status: 0
I/flutter (27267): Date char bytes: [33, 0, 0, 0, 0, 0, 0, 0]
D/FlutterBluePlugin(27267): mDevices size: 1
I/flutter (27267): remoteId: E0:E2:E6:9B:3B:C2 characteristicUuid: 0faf892f-0000-1000-8000-00805f9b34fb serviceUuid: 000019f5-0000-1000-8000-00805f9b34fb
D/FlutterBluePlugin(27267): [onCharacteristicRead] uuid: 0faf892f-0000-1000-8000-00805f9b34fb status: 0
I/flutter (27267): Handling change on pendingRecordsCount: [1]
I/flutter (27267): A punto de tomar _gattLock
I/ViewRootImpl@87b963[MainActivity](27267): ViewPostIme pointer 0
I/ViewRootImpl@87b963[MainActivity](27267): ViewPostIme pointer 1
I/flutter (27267): Disconnecting from device
D/BluetoothGatt(27267): cancelOpen() - device: E0:E2:E6:9B:3B:C2
D/BluetoothGatt(27267): onClientConnectionState() - status=0 clientIf=13 device=E0:E2:E6:9B:3B:C2
D/FlutterBluePlugin(27267): [onConnectionStateChange] status: 0 newState: 0
D/BluetoothGatt(27267): close()
D/BluetoothGatt(27267): unregisterApp() - mClientIf=13
I/flutter (27267): Device state changed BluetoothDeviceState.disconnected
I/flutter (27267): remoteId: E0:E2:E6:9B:3B:C2 characteristicUuid: 00002a19-0000-1000-8000-00805f9b34fb serviceUuid: 0000180f-0000-1000-8000-00805f9b34fb
I/flutter (27267): Error al leer el valor de una caracteristica del servicio de bateria (UUID = 00002a19-0000-1000-8000-00805f9b34fb): PlatformException(read_characteristic_error, no instance of BluetoothGatt, have you connected first?, null, null)
I/flutter (27267): Battery char bytes: []
I/flutter (27267): Synchronized data through BLE: {cantidad: 200, temperatura: -5.0, fecha: 1969-12-31 18:00:33.000}
I/flutter (27267): Not all required attributes could be obtained, expected 4
I/flutter (27267): _gattLock tomado
I/flutter (27267): remoteId: E0:E2:E6:9B:3B:C2 characteristicUuid: 0faf892c-0000-1000-8000-00805f9b34fb serviceUuid: 000019f5-0000-1000-8000-00805f9b34fb
I/flutter (27267): Error al leer el valor de una caracteristica del servicio de hidratacion (UUID = 0faf892c-0000-1000-8000-00805f9b34fb): PlatformException(read_characteristic_error, no instance of BluetoothGatt, have you connected first?, null, null)
I/flutter (27267): Ml char bytes: []
I/flutter (27267): remoteId: E0:E2:E6:9B:3B:C2 characteristicUuid: 00002a6e-0000-1000-8000-00805f9b34fb serviceUuid: 000019f5-0000-1000-8000-00805f9b34fb
I/flutter (27267): Error al leer el valor de una caracteristica del servicio de hidratacion (UUID = 00002a6e-0000-1000-8000-00805f9b34fb): PlatformException(read_characteristic_error, no instance of BluetoothGatt, have you connected first?, null, null)

(......)

I/flutter (27267): Error al leer el valor de una caracteristica del servicio de hidratacion (UUID = 0faf892f-0000-1000-8000-00805f9b34fb): PlatformException(read_characteristic_error, no instance of BluetoothGatt, have you connected first?, null, null)
I/flutter (27267): remoteId: E0:E2:E6:9B:3B:C2 characteristicUuid: 00002a19-0000-1000-8000-00805f9b34fb serviceUuid: 0000180f-0000-1000-8000-00805f9b34fb
I/flutter (27267): Error al leer el valor de una caracteristica del servicio de bateria (UUID = 00002a19-0000-1000-8000-00805f9b34fb): PlatformException(read_characteristic_error, no instance of BluetoothGatt, have you connected first?, null, null)
I/flutter (27267): Battery char bytes: []
I/flutter (27267): Synchronized data through BLE: {}
I/flutter (27267): Not all required attributes could be obtained, expected 4
I/ViewRootImpl@87b963[MainActivity](27267): ViewPostIme pointer 0
I/ViewRootImpl@87b963[MainActivity](27267): ViewPostIme pointer 1
D/FlutterBluePlugin(27267): mDevices size: 0

[BUG] Error al usar un solo `where`, sin `unions`, en `SQLiteDB.select()`

Descripción del Error
Cuando el método SQLiteDB.select() es invocado, con un solo elemento como argumento a where y un valor nulo como argumento para unions, el where es completamente ignorado. Esto se debe a la condición (where != null && whereUnions != null), que solo es evaluada a true cuando se incluye al menos un elemento en unions (usualmente, para usar múltiples where).

El problema es encontrado en la línea 168 de SQLiteDB.dart:

final fullWhere = (where != null && whereUnions != null) 
    ? MultiWhereClause.fromConditions(where, whereUnions)
    : null;

Cómo Reproducir
Pasos para reproducir el error:

  1. Encontrar un uso de SQLiteDB.select() que use un solo WhereClause en su parámetro where. Este error fue encontrado en el método ProfileProvider.findProfileLinkedToAccount().
  2. Notar como el string "WHERE query: null, ARGS: null" es impreso en la debug console.
  3. Si se analizan los resultados del query, se puede observar que son incorrectos.

Comportamiento Esperado
El método SQLiteDB.select() debería realizar el query a SQLite usando los argumentos where proporcionados, para poder realizar queries selectivas.

Entorno

  • Dispositivo: Galaxy A30
  • OS: Android 11
  • Versión de la app: 1.0.0-beta+3

[BUG] Los `ActivityRecord` creados no se muestran en la versión `1.0.0-beta+2`

Descripción del Error
Cuando se introducen los datos de un ActivityRecord en NewActivityForm y se presiona el botón "Crear", la app redirige a la vista principal, pero al navegar a la pestaña de historial el nuevo registro no se muestra.

Cómo Reproducir
Pasos para reproducir el error:

  1. Compilar e iniciar la app como release.
  2. Navegar a formulario para crear un nuevo ActivityRecord.
  3. Completar los campos.
  4. Observar mensajes de advertencia generados por ValidationMessageBuilder, con cada cambio en el formulario.
  5. Presionar el botón "crear".
  6. Notar el mensaje de error producido por una excepción Bad state: no element en SQLiteDB.select(), línea 214 de sqlite_db.dart.
  7. Navegar a la pestaña de historial de actividad física.
  8. Observar que no hay una tarjeta para la actividad creada.

Comportamiento Esperado
La app debería mostrar todos los ActivityRecord en la pestaña de historial de actividad. Además, ValidationMessageBuilder no debería recibir errores de validación de tipo NumericInputError.isNaN cuando valida la duración, distancia o kilocalorías de un registro de actividad física, y menos si los valores del fomrulario son correctos.

**Entorno **

  • Dispositivo: Galaxy A30
  • OS: Android 11
  • Versión de la app: 1.0.0-beta+2

Contexto Adicional
Al parecer, este error solo ha sido observado en la versión release de la app. En modo debug no ha aparecido.

Los mensajes de error completos:

I/flutter ( 3002): Unhandled activity record duration validation message for error: NumericInputError.isNaN
I/flutter ( 3002): Unhandled activity record distance validation message for error: NumericInputError.isNaN
I/flutter ( 3002): Unhandled activity record kCal validation message for error: NumericInputError.isNaN
I/flutter ( 3002): Query result example: {id: 1, titulo: Paseo, fecha: 2022-08-25T14:04:00.000, duracion: 20, distancia: 1.6666666666666665, kilocalorias_quemadas: 80, al_aire_libre: 1, es_rutina: 0, id_tipo_actividad: 0, id_perfil: 0}
E/flutter ( 3002): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: Bad state: No element
E/flutter ( 3002): #0      ListMixin.firstWhere (dart:collection/list.dart:167)
E/flutter ( 3002): #1      SQLiteDB.select (package:hydrate_app/src/db/sqlite_db.dart:214)
E/flutter ( 3002): <asynchronous suspension>
E/flutter ( 3002): #2      ActivityProvider._queryActivityRecords (package:hydrate_app/src/provider/activity_provider.dart:151)
E/flutter ( 3002): <asynchronous suspension>
E/flutter ( 3002): #3      CacheState.refresh (package:hydrate_app/src/provider/cache_state.dart:126)
E/flutter ( 3002): <asynchronous suspension>

[BUG] El formulario de creación de cuenta no muestra errores en la confirmación de contraseña

Descripción del Error
El formulario de creación de nueva cuenta no muestra un mensaje de error en el campo adecuado cuando el usuario introduce una confirmación de contraseña que no coincide con la contraseña previamente introducida.

Cómo Reproducir
Pasos para reproducir el error:

  1. Navegar a la vista de creación de cuenta.
  2. Introducir una contraseña válida (entre 8 y 40 caracteres, al menos un número y una letra mayúscula).
  3. Introducir una confirmación de contraseña diferente a la contraseña del paso 2.
  4. Notar como el formulario no muestra un error.

Comportamiento Esperado
Debe aparecer un mensaje de error si la confirmación de contraseña no es idéntica a la contraseña. El estado del formulario debe cambiar de forma correspondiente (el botón de envío debe ser desactivado).

Entorno

  • Dispositivo: Galaxy A30s
  • OS: Android 11
  • Versión de la app: 1.0.0+1

[BUG] [Android] La configuración de build de release no optimiza ni ofusca, para mantener compatibilidad con `flutter_blue`

Descripción del Error
Esto no es necesariamente un error. De todos modos, esta configuración fue introducida como workaround para solucionar problemas de flutter_blue, sin considerar antes lo que implica desactivar la ofuscación, minimización y optimización de código en una build de release.

La configuración problemática en [nombre-del-proyecto]/android/app/build.gradle es la siguiente:

buildTypes {
    release {
        shrinkResources false
        minifyEnabled false
    }
}

Comportamiento Esperado
La funcionalidad de BLE no debería sacrificar aspectos importantes de la build de release.

Entorno

  • Dispositivo: Galaxy A30
  • OS: Android 11
  • Versión de la app: a partir de 0.2.0

Contexto Adicional
La guía oficial de Android sobre minimización de código. También encontré esta respuesta a una "solución" de StackOverflow. en ella, el autor describe que la solución dada tiene implicaciones más profundas y seguramente debe ser posible arreglar el problema de raíz, en vez de usar un workaround.

[BUG] Un mismo registro de hidratación puede ser guardado varias veces, resultando en duplicados

Descripción del Error
Cuando la app obtiene uno o más registros de hidratación desde la extensión, es común que un mismo registro de hidratación sea "creado" varias veces, haciendo que se muestren muchos registros idénticos.

Cómo Reproducir
Pasos para reproducir el error:

  1. Conectar la app con la extensión para botellas a través de BLE.
  2. Tomar agua, desde una botella con la extensión adherida.
  3. Navegar a la vista de historial de hidratación en la app.
  4. Ver los registros duplicados.

Comportamiento Esperado
La app debe obtener cada registro de hidratación una sola vez, y guardar cada registro en su BD una sola vez.

Entorno

  • Dispositivo: Galaxy A30s
  • OS: Android 11
  • Versión de la app: 1.0.0+1

Contexto Adicional
No estoy seguro si el problema es causado por la app al obtener los registros, o por la extensión, que notifica más de una vez a la app con el mismo registro.

[BUG] No es posible enviar el formulario de creación de nueva cuenta

Descripción del Error
El botón para enviar el formulario de creación de nueva cuenta permanece desactivado, aún cuando la información introducida en los campos es válida.

Cómo Reproducir
Pasos para reproducir el error:

  1. Navegar a la vista de creación de nueva cuenta.
  2. Completar los campos obligatorios con información válida.
  3. Notar que el botón permanece desactivado, haciendo imposible enviar el formulario.

Comportamiento Esperado
El usuario debería ser capaz de enviar el formulario de creación de nueva cuenta si los datos que introdujo en los campos son válidos. El botón debería estar desactivado cuando los datos estén incompletos o no sean válidos, y activarse cuando el usuario complete el formulario correctamente.

Entorno

  • Dispositivo: Galaxy A30s
  • OS: Android 11
  • Versión de la app: 1.0.0+1

[BUG] La tarea de Workmanager para la aportación a datos abiertos falla con un `NoSuchMethodError`

Descripción del Error
Cuando WorkManager intenta ejecutar la tarea para realizar una contribución a datos abiertos, se produce un NoSuchMethodError. Esto significa que WorkManager no tiene acceso al método designado para manejar sus callbacks, BackgroundTasks.callbackDispatcher().

Cómo Reproducir
Pasos para reproducir el error:

  1. Iniciar sesión con una cuenta de usuario válida.
  2. Activar la contribución a datos abiertos, en la vista de configuración.
  3. Asegurar que el dispositivo tiene conexión a internet.
  4. Esperar a que el task sea ejecutado por Workmanager.
  5. Ver el error, en consola y en la notificación de debug.

Comportamiento Esperado
El módulo que interactúa con WorkManager debería poder especificar una función que maneja los callbacks y ejecuta las tareas en segundo plano.

Entorno

  • Dispositivo: Galaxy A30s
  • OS: Android 11
  • Versión de la app: 1.0.0+1

Contexto Adicional
Probablemente la función usada para manejar callbacks debe ser de top level (no estática de una clase).

La excepción produce el siguiente output al ser producida en debug:

E/flutter (29114): [ERROR:flutter/shell/common/shell.cc(93)] Dart Unhandled Exception: NoSuchMethodError: No top-level getter 'callbackDispatcher' declared.
E/flutter (29114): Receiver: top-level
E/flutter (29114): Tried calling: callbackDispatcher, stack trace: #0      NoSuchMethodError._throwNew (dart:core-patch/errors_patch.dart:216:5)
E/flutter (29114):
E/flutter (29114): [ERROR:flutter/runtime/dart_isolate.cc(668)] Could not resolve main entrypoint function.
E/flutter (29114): [ERROR:flutter/runtime/dart_isolate.cc(167)] Could not run the run main Dart entrypoint.
E/flutter (29114): [ERROR:flutter/runtime/runtime_controller.cc(382)] Could not create root isolate.
E/flutter (29114): [ERROR:flutter/shell/common/shell.cc(600)] Could not launch engine with configuration.

Registros de actividades rutinarias quedan con la misma fecha que la actividad original

Problema

Cuando una actividad es definida como rutina, los registros son agregados con las fechas correctas. El problema es que ActivitySliverList, en su Builder, usa RoutineActivities.allActivities, que asigna correctamente los registros de actividad pero no especifica la fecha de cada uno. Entonces, cuando el builder solo referencia el ActivityRecord con ID = 2, usa su misma fecha, aunque sea una repetición por rutina.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.