feat: Drift local SQLite database — tables, repositories, and generated code
- Add 4 Drift table definitions (Users, Tasks, PetStates, TaskCompletions) - Add AppDatabase with driftDatabase() connection (drift_flutter 0.2.8 API) - Add TaskRepository, PetRepository, UserRepository with entity mappers - Add domain entities and enums (TaskEntity, PetStateEntity, UserEntity, TaskStatus, PetType, TaskCompletionEntity) - Run build_runner to generate app_database.g.dart - flutter analyze: no issues Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:drift_flutter/drift_flutter.dart';
|
||||
|
||||
import '../models/tables.dart';
|
||||
|
||||
part 'app_database.g.dart';
|
||||
|
||||
@DriftDatabase(tables: [Users, Tasks, PetStates, TaskCompletions])
|
||||
class AppDatabase extends _$AppDatabase {
|
||||
AppDatabase([QueryExecutor? executor])
|
||||
: super(executor ?? _openConnection());
|
||||
|
||||
@override
|
||||
int get schemaVersion => 1;
|
||||
|
||||
static QueryExecutor _openConnection() {
|
||||
return driftDatabase(name: 'kittie_mobile_db');
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,75 @@
|
||||
import 'package:drift/drift.dart';
|
||||
|
||||
/// User profiles — device identity and pet selection.
|
||||
class Users extends Table {
|
||||
TextColumn get id => text()();
|
||||
TextColumn get deviceId => text().unique()();
|
||||
TextColumn get name => text().withDefault(const Constant('Friend'))();
|
||||
TextColumn get petType => text().withDefault(const Constant('cat'))();
|
||||
TextColumn get petName => text().withDefault(const Constant('Yoshi'))();
|
||||
DateTimeColumn get createdAt =>
|
||||
dateTime().withDefault(currentDateAndTime)();
|
||||
DateTimeColumn get updatedAt =>
|
||||
dateTime().withDefault(currentDateAndTime)();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {id};
|
||||
}
|
||||
|
||||
/// Tasks — the core task items with sync support.
|
||||
class Tasks extends Table {
|
||||
TextColumn get id => text()();
|
||||
TextColumn get userId => text().references(Users, #id)();
|
||||
TextColumn get title => text()();
|
||||
TextColumn get description => text().nullable()();
|
||||
IntColumn get energyLevel => integer().withDefault(const Constant(2))();
|
||||
TextColumn get status =>
|
||||
text().withDefault(const Constant('pending'))();
|
||||
BoolColumn get isSelfCare =>
|
||||
boolean().withDefault(const Constant(false))();
|
||||
DateTimeColumn get deadline => dateTime().nullable()();
|
||||
TextColumn get photoPath => text().nullable()();
|
||||
DateTimeColumn get snoozeUntil => dateTime().nullable()();
|
||||
IntColumn get sortOrder => integer().withDefault(const Constant(0))();
|
||||
DateTimeColumn get createdAt =>
|
||||
dateTime().withDefault(currentDateAndTime)();
|
||||
DateTimeColumn get updatedAt =>
|
||||
dateTime().withDefault(currentDateAndTime)();
|
||||
DateTimeColumn get syncedAt => dateTime().nullable()();
|
||||
TextColumn get localId => text().nullable().unique()();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {id};
|
||||
}
|
||||
|
||||
/// Pet state — energy, streaks, and unlocked items per user.
|
||||
class PetStates extends Table {
|
||||
TextColumn get id => text()();
|
||||
TextColumn get userId => text().unique().references(Users, #id)();
|
||||
IntColumn get energyToday => integer().nullable()();
|
||||
DateTimeColumn get energyDate => dateTime().nullable()();
|
||||
IntColumn get streakDays => integer().withDefault(const Constant(0))();
|
||||
IntColumn get totalTasksDone =>
|
||||
integer().withDefault(const Constant(0))();
|
||||
TextColumn get unlockedItems =>
|
||||
text().withDefault(const Constant(''))();
|
||||
DateTimeColumn get updatedAt =>
|
||||
dateTime().withDefault(currentDateAndTime)();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {id};
|
||||
}
|
||||
|
||||
/// Task completion log — records each completion for rewards and analytics.
|
||||
class TaskCompletions extends Table {
|
||||
TextColumn get id => text()();
|
||||
TextColumn get userId => text().references(Users, #id)();
|
||||
TextColumn get taskId => text().nullable()();
|
||||
TextColumn get title => text()();
|
||||
IntColumn get energyLevel => integer()();
|
||||
DateTimeColumn get completedAt =>
|
||||
dateTime().withDefault(currentDateAndTime)();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {id};
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
import 'package:drift/drift.dart';
|
||||
|
||||
import '../../domain/entities/pet_state_entity.dart';
|
||||
import '../db/app_database.dart';
|
||||
|
||||
/// Data access layer for pet state.
|
||||
class PetRepository {
|
||||
final AppDatabase _db;
|
||||
|
||||
PetRepository(this._db);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Queries
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Future<PetStateEntity?> getPetState(String userId) async {
|
||||
final row = await (_db.select(_db.petStates)
|
||||
..where((t) => t.userId.equals(userId)))
|
||||
.getSingleOrNull();
|
||||
return row == null ? null : _toEntity(row);
|
||||
}
|
||||
|
||||
Stream<PetStateEntity?> watchPetState(String userId) {
|
||||
return (_db.select(_db.petStates)
|
||||
..where((t) => t.userId.equals(userId)))
|
||||
.watchSingleOrNull()
|
||||
.map((row) => row == null ? null : _toEntity(row));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Mutations
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Future<void> updatePetState(PetStateEntity entity) async {
|
||||
await (_db.update(_db.petStates)
|
||||
..where((t) => t.id.equals(entity.id)))
|
||||
.write(_toCompanion(entity));
|
||||
}
|
||||
|
||||
Future<void> setEnergyToday(String userId, int energy) async {
|
||||
final now = DateTime.now();
|
||||
await (_db.update(_db.petStates)
|
||||
..where((t) => t.userId.equals(userId)))
|
||||
.write(PetStatesCompanion(
|
||||
energyToday: Value(energy),
|
||||
energyDate: Value(now),
|
||||
updatedAt: Value(now),
|
||||
));
|
||||
}
|
||||
|
||||
Future<void> incrementStreak(String userId) async {
|
||||
final row = await (_db.select(_db.petStates)
|
||||
..where((t) => t.userId.equals(userId)))
|
||||
.getSingle();
|
||||
|
||||
await (_db.update(_db.petStates)
|
||||
..where((t) => t.userId.equals(userId)))
|
||||
.write(PetStatesCompanion(
|
||||
streakDays: Value(row.streakDays + 1),
|
||||
updatedAt: Value(DateTime.now()),
|
||||
));
|
||||
}
|
||||
|
||||
Future<void> insertPetState(PetStateEntity entity) async {
|
||||
await _db.into(_db.petStates).insert(_toCompanion(entity));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Mappers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
PetStateEntity _toEntity(PetState row) {
|
||||
return PetStateEntity(
|
||||
id: row.id,
|
||||
userId: row.userId,
|
||||
energyToday: row.energyToday,
|
||||
energyDate: row.energyDate,
|
||||
streakDays: row.streakDays,
|
||||
totalTasksDone: row.totalTasksDone,
|
||||
unlockedItems: row.unlockedItems,
|
||||
updatedAt: row.updatedAt,
|
||||
);
|
||||
}
|
||||
|
||||
PetStatesCompanion _toCompanion(PetStateEntity e) {
|
||||
return PetStatesCompanion(
|
||||
id: Value(e.id),
|
||||
userId: Value(e.userId),
|
||||
energyToday: Value(e.energyToday),
|
||||
energyDate: Value(e.energyDate),
|
||||
streakDays: Value(e.streakDays),
|
||||
totalTasksDone: Value(e.totalTasksDone),
|
||||
unlockedItems: Value(e.unlockedItems),
|
||||
updatedAt: Value(e.updatedAt),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
import '../../domain/entities/task_completion_entity.dart';
|
||||
import '../../domain/entities/task_entity.dart';
|
||||
import '../../domain/entities/task_status.dart';
|
||||
import '../db/app_database.dart';
|
||||
|
||||
/// Data access layer for tasks and task completions.
|
||||
class TaskRepository {
|
||||
final AppDatabase _db;
|
||||
static const _uuid = Uuid();
|
||||
|
||||
TaskRepository(this._db);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Queries
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Future<List<TaskEntity>> getAllTasks(String userId) async {
|
||||
final rows = await (_db.select(_db.tasks)
|
||||
..where((t) => t.userId.equals(userId))
|
||||
..orderBy([(t) => OrderingTerm.asc(t.sortOrder)]))
|
||||
.get();
|
||||
return rows.map(_toEntity).toList();
|
||||
}
|
||||
|
||||
Future<List<TaskEntity>> getTodayTasks(String userId) async {
|
||||
final rows = await (_db.select(_db.tasks)
|
||||
..where((t) =>
|
||||
t.userId.equals(userId) &
|
||||
t.status.equals(TaskStatus.pending.name))
|
||||
..orderBy([(t) => OrderingTerm.asc(t.sortOrder)]))
|
||||
.get();
|
||||
return rows.map(_toEntity).toList();
|
||||
}
|
||||
|
||||
Future<TaskEntity?> getTaskById(String id) async {
|
||||
final row = await (_db.select(_db.tasks)
|
||||
..where((t) => t.id.equals(id)))
|
||||
.getSingleOrNull();
|
||||
return row == null ? null : _toEntity(row);
|
||||
}
|
||||
|
||||
Stream<List<TaskEntity>> watchTasks(String userId) {
|
||||
return (_db.select(_db.tasks)
|
||||
..where((t) => t.userId.equals(userId))
|
||||
..orderBy([(t) => OrderingTerm.asc(t.sortOrder)]))
|
||||
.watch()
|
||||
.map((rows) => rows.map(_toEntity).toList());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Mutations
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Future<void> insertTask(TaskEntity entity) async {
|
||||
await _db.into(_db.tasks).insert(_toCompanion(entity));
|
||||
}
|
||||
|
||||
Future<void> updateTask(TaskEntity entity) async {
|
||||
await (_db.update(_db.tasks)..where((t) => t.id.equals(entity.id)))
|
||||
.write(_toCompanion(entity));
|
||||
}
|
||||
|
||||
Future<void> deleteTask(String id) async {
|
||||
await (_db.delete(_db.tasks)..where((t) => t.id.equals(id))).go();
|
||||
}
|
||||
|
||||
/// Marks a task as done and creates a [TaskCompletionEntity] log entry.
|
||||
Future<void> completeTask(String taskId) async {
|
||||
await _db.transaction(() async {
|
||||
final task = await (_db.select(_db.tasks)
|
||||
..where((t) => t.id.equals(taskId)))
|
||||
.getSingle();
|
||||
|
||||
final now = DateTime.now();
|
||||
|
||||
// Update task status to done.
|
||||
await (_db.update(_db.tasks)..where((t) => t.id.equals(taskId)))
|
||||
.write(TasksCompanion(
|
||||
status: Value(TaskStatus.done.name),
|
||||
updatedAt: Value(now),
|
||||
));
|
||||
|
||||
// Log the completion.
|
||||
await _db.into(_db.taskCompletions).insert(TaskCompletionsCompanion(
|
||||
id: Value(_uuid.v4()),
|
||||
userId: Value(task.userId),
|
||||
taskId: Value(taskId),
|
||||
title: Value(task.title),
|
||||
energyLevel: Value(task.energyLevel),
|
||||
completedAt: Value(now),
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Mappers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
TaskEntity _toEntity(Task row) {
|
||||
return TaskEntity(
|
||||
id: row.id,
|
||||
userId: row.userId,
|
||||
title: row.title,
|
||||
description: row.description,
|
||||
energyLevel: row.energyLevel,
|
||||
status: TaskStatus.fromString(row.status),
|
||||
isSelfCare: row.isSelfCare,
|
||||
deadline: row.deadline,
|
||||
photoPath: row.photoPath,
|
||||
snoozeUntil: row.snoozeUntil,
|
||||
sortOrder: row.sortOrder,
|
||||
createdAt: row.createdAt,
|
||||
updatedAt: row.updatedAt,
|
||||
syncedAt: row.syncedAt,
|
||||
localId: row.localId,
|
||||
);
|
||||
}
|
||||
|
||||
TasksCompanion _toCompanion(TaskEntity e) {
|
||||
return TasksCompanion(
|
||||
id: Value(e.id),
|
||||
userId: Value(e.userId),
|
||||
title: Value(e.title),
|
||||
description: Value(e.description),
|
||||
energyLevel: Value(e.energyLevel),
|
||||
status: Value(e.status.name),
|
||||
isSelfCare: Value(e.isSelfCare),
|
||||
deadline: Value(e.deadline),
|
||||
photoPath: Value(e.photoPath),
|
||||
snoozeUntil: Value(e.snoozeUntil),
|
||||
sortOrder: Value(e.sortOrder),
|
||||
createdAt: Value(e.createdAt),
|
||||
updatedAt: Value(e.updatedAt),
|
||||
syncedAt: Value(e.syncedAt),
|
||||
localId: Value(e.localId),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import 'package:drift/drift.dart';
|
||||
|
||||
import '../../domain/entities/pet_type.dart';
|
||||
import '../../domain/entities/user_entity.dart';
|
||||
import '../db/app_database.dart';
|
||||
|
||||
/// Data access layer for user profiles.
|
||||
class UserRepository {
|
||||
final AppDatabase _db;
|
||||
|
||||
UserRepository(this._db);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Queries
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Returns the first (and typically only) user row, or `null`.
|
||||
Future<UserEntity?> getCurrentUser() async {
|
||||
final row = await (_db.select(_db.users)..limit(1)).getSingleOrNull();
|
||||
return row == null ? null : _toEntity(row);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Mutations
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Future<void> createUser({
|
||||
required String id,
|
||||
required String deviceId,
|
||||
required String name,
|
||||
required PetType petType,
|
||||
required String petName,
|
||||
}) async {
|
||||
final now = DateTime.now();
|
||||
await _db.into(_db.users).insert(UsersCompanion(
|
||||
id: Value(id),
|
||||
deviceId: Value(deviceId),
|
||||
name: Value(name),
|
||||
petType: Value(petType.name),
|
||||
petName: Value(petName),
|
||||
createdAt: Value(now),
|
||||
updatedAt: Value(now),
|
||||
));
|
||||
}
|
||||
|
||||
Future<void> updateUser(UserEntity entity) async {
|
||||
await (_db.update(_db.users)..where((t) => t.id.equals(entity.id)))
|
||||
.write(_toCompanion(entity));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Mappers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UserEntity _toEntity(User row) {
|
||||
return UserEntity(
|
||||
id: row.id,
|
||||
deviceId: row.deviceId,
|
||||
name: row.name,
|
||||
petType: PetType.fromString(row.petType),
|
||||
petName: row.petName,
|
||||
createdAt: row.createdAt,
|
||||
updatedAt: row.updatedAt,
|
||||
);
|
||||
}
|
||||
|
||||
UsersCompanion _toCompanion(UserEntity e) {
|
||||
return UsersCompanion(
|
||||
id: Value(e.id),
|
||||
deviceId: Value(e.deviceId),
|
||||
name: Value(e.name),
|
||||
petType: Value(e.petType.name),
|
||||
petName: Value(e.petName),
|
||||
createdAt: Value(e.createdAt),
|
||||
updatedAt: Value(e.updatedAt),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/// Pure domain entity for pet state.
|
||||
class PetStateEntity {
|
||||
final String id;
|
||||
final String userId;
|
||||
final int? energyToday;
|
||||
final DateTime? energyDate;
|
||||
final int streakDays;
|
||||
final int totalTasksDone;
|
||||
final String unlockedItems;
|
||||
final DateTime updatedAt;
|
||||
|
||||
const PetStateEntity({
|
||||
required this.id,
|
||||
required this.userId,
|
||||
this.energyToday,
|
||||
this.energyDate,
|
||||
this.streakDays = 0,
|
||||
this.totalTasksDone = 0,
|
||||
this.unlockedItems = '',
|
||||
required this.updatedAt,
|
||||
});
|
||||
|
||||
PetStateEntity copyWith({
|
||||
String? id,
|
||||
String? userId,
|
||||
int? energyToday,
|
||||
DateTime? energyDate,
|
||||
int? streakDays,
|
||||
int? totalTasksDone,
|
||||
String? unlockedItems,
|
||||
DateTime? updatedAt,
|
||||
}) {
|
||||
return PetStateEntity(
|
||||
id: id ?? this.id,
|
||||
userId: userId ?? this.userId,
|
||||
energyToday: energyToday ?? this.energyToday,
|
||||
energyDate: energyDate ?? this.energyDate,
|
||||
streakDays: streakDays ?? this.streakDays,
|
||||
totalTasksDone: totalTasksDone ?? this.totalTasksDone,
|
||||
unlockedItems: unlockedItems ?? this.unlockedItems,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/// Available pet companions.
|
||||
enum PetType {
|
||||
dog,
|
||||
cat,
|
||||
capybara;
|
||||
|
||||
/// Parse from database string value.
|
||||
static PetType fromString(String value) =>
|
||||
PetType.values.firstWhere((e) => e.name == value);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/// Pure domain entity for a task completion log entry.
|
||||
class TaskCompletionEntity {
|
||||
final String id;
|
||||
final String userId;
|
||||
final String? taskId;
|
||||
final String title;
|
||||
final int energyLevel;
|
||||
final DateTime completedAt;
|
||||
|
||||
const TaskCompletionEntity({
|
||||
required this.id,
|
||||
required this.userId,
|
||||
this.taskId,
|
||||
required this.title,
|
||||
required this.energyLevel,
|
||||
required this.completedAt,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import 'task_status.dart';
|
||||
|
||||
/// Pure domain entity for a task.
|
||||
class TaskEntity {
|
||||
final String id;
|
||||
final String userId;
|
||||
final String title;
|
||||
final String? description;
|
||||
final int energyLevel;
|
||||
final TaskStatus status;
|
||||
final bool isSelfCare;
|
||||
final DateTime? deadline;
|
||||
final String? photoPath;
|
||||
final DateTime? snoozeUntil;
|
||||
final int sortOrder;
|
||||
final DateTime createdAt;
|
||||
final DateTime updatedAt;
|
||||
final DateTime? syncedAt;
|
||||
final String? localId;
|
||||
|
||||
const TaskEntity({
|
||||
required this.id,
|
||||
required this.userId,
|
||||
required this.title,
|
||||
this.description,
|
||||
this.energyLevel = 2,
|
||||
this.status = TaskStatus.pending,
|
||||
this.isSelfCare = false,
|
||||
this.deadline,
|
||||
this.photoPath,
|
||||
this.snoozeUntil,
|
||||
this.sortOrder = 0,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
this.syncedAt,
|
||||
this.localId,
|
||||
});
|
||||
|
||||
TaskEntity copyWith({
|
||||
String? id,
|
||||
String? userId,
|
||||
String? title,
|
||||
String? description,
|
||||
int? energyLevel,
|
||||
TaskStatus? status,
|
||||
bool? isSelfCare,
|
||||
DateTime? deadline,
|
||||
String? photoPath,
|
||||
DateTime? snoozeUntil,
|
||||
int? sortOrder,
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
DateTime? syncedAt,
|
||||
String? localId,
|
||||
}) {
|
||||
return TaskEntity(
|
||||
id: id ?? this.id,
|
||||
userId: userId ?? this.userId,
|
||||
title: title ?? this.title,
|
||||
description: description ?? this.description,
|
||||
energyLevel: energyLevel ?? this.energyLevel,
|
||||
status: status ?? this.status,
|
||||
isSelfCare: isSelfCare ?? this.isSelfCare,
|
||||
deadline: deadline ?? this.deadline,
|
||||
photoPath: photoPath ?? this.photoPath,
|
||||
snoozeUntil: snoozeUntil ?? this.snoozeUntil,
|
||||
sortOrder: sortOrder ?? this.sortOrder,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
syncedAt: syncedAt ?? this.syncedAt,
|
||||
localId: localId ?? this.localId,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/// All possible states a task can be in.
|
||||
enum TaskStatus {
|
||||
pending,
|
||||
done,
|
||||
snoozed,
|
||||
someday,
|
||||
archived;
|
||||
|
||||
/// Parse from database string value.
|
||||
static TaskStatus fromString(String value) =>
|
||||
TaskStatus.values.firstWhere((e) => e.name == value);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import 'pet_type.dart';
|
||||
|
||||
/// Pure domain entity for a user profile.
|
||||
class UserEntity {
|
||||
final String id;
|
||||
final String deviceId;
|
||||
final String name;
|
||||
final PetType petType;
|
||||
final String petName;
|
||||
final DateTime createdAt;
|
||||
final DateTime updatedAt;
|
||||
|
||||
const UserEntity({
|
||||
required this.id,
|
||||
required this.deviceId,
|
||||
required this.name,
|
||||
required this.petType,
|
||||
required this.petName,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
});
|
||||
|
||||
UserEntity copyWith({
|
||||
String? id,
|
||||
String? deviceId,
|
||||
String? name,
|
||||
PetType? petType,
|
||||
String? petName,
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
}) {
|
||||
return UserEntity(
|
||||
id: id ?? this.id,
|
||||
deviceId: deviceId ?? this.deviceId,
|
||||
name: name ?? this.name,
|
||||
petType: petType ?? this.petType,
|
||||
petName: petName ?? this.petName,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user