fix: resolve flutter analyze errors - remove leaked code, fix method calls, cleanup imports
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
class AppConfig {
|
||||
AppConfig._();
|
||||
|
||||
static const String baseUrl = String.fromEnvironment(
|
||||
'BASE_URL',
|
||||
defaultValue: 'https://localhost:3000',
|
||||
);
|
||||
|
||||
static const String googleClientId = String.fromEnvironment(
|
||||
'GOOGLE_CLIENT_ID',
|
||||
defaultValue: '',
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
import 'novel_model.dart';
|
||||
|
||||
class BookmarkModel extends Equatable {
|
||||
const BookmarkModel({
|
||||
required this.id,
|
||||
required this.novelId,
|
||||
this.lastChapterId,
|
||||
this.lastChapterNumber,
|
||||
this.readChapters = const [],
|
||||
this.novel,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final String novelId;
|
||||
final String? lastChapterId;
|
||||
final int? lastChapterNumber;
|
||||
final List<int> readChapters;
|
||||
final NovelModel? novel;
|
||||
|
||||
factory BookmarkModel.fromJson(Map<String, dynamic> json) => BookmarkModel(
|
||||
id: json['id'] as String,
|
||||
novelId: json['novelId'] as String,
|
||||
lastChapterId: json['lastChapterId'] as String?,
|
||||
lastChapterNumber: json['lastChapterNumber'] as int?,
|
||||
readChapters: (json['readChapters'] as List<dynamic>?)
|
||||
?.map((e) => (e as num).toInt())
|
||||
.toList() ??
|
||||
[],
|
||||
novel: json['novel'] != null
|
||||
? NovelModel.fromJson(json['novel'] as Map<String, dynamic>)
|
||||
: null,
|
||||
);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [id, novelId];
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
class ChapterModel extends Equatable {
|
||||
const ChapterModel({
|
||||
required this.id,
|
||||
required this.novelId,
|
||||
required this.number,
|
||||
required this.title,
|
||||
required this.content,
|
||||
this.views = 0,
|
||||
this.volumeNumber,
|
||||
this.volumeTitle,
|
||||
this.volumeChapterNumber,
|
||||
this.prevChapterId,
|
||||
this.prevChapterNumber,
|
||||
this.nextChapterId,
|
||||
this.nextChapterNumber,
|
||||
required this.createdAt,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final String novelId;
|
||||
final int number;
|
||||
final String title;
|
||||
final String content;
|
||||
final int views;
|
||||
final int? volumeNumber;
|
||||
final String? volumeTitle;
|
||||
final int? volumeChapterNumber;
|
||||
final String? prevChapterId;
|
||||
final int? prevChapterNumber;
|
||||
final String? nextChapterId;
|
||||
final int? nextChapterNumber;
|
||||
final DateTime createdAt;
|
||||
|
||||
factory ChapterModel.fromJson(Map<String, dynamic> json) => ChapterModel(
|
||||
id: json['id'] as String,
|
||||
novelId: json['novelId'] as String,
|
||||
number: (json['number'] as num).toInt(),
|
||||
title: json['title'] as String,
|
||||
content: json['content'] as String,
|
||||
views: (json['views'] as num?)?.toInt() ?? 0,
|
||||
volumeNumber: json['volumeNumber'] as int?,
|
||||
volumeTitle: json['volumeTitle'] as String?,
|
||||
volumeChapterNumber: json['volumeChapterNumber'] as int?,
|
||||
prevChapterId: json['prevChapterId'] as String?,
|
||||
prevChapterNumber: json['prevChapterNumber'] as int?,
|
||||
nextChapterId: json['nextChapterId'] as String?,
|
||||
nextChapterNumber: json['nextChapterNumber'] as int?,
|
||||
createdAt: DateTime.parse(json['createdAt'] as String),
|
||||
);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [id, number];
|
||||
}
|
||||
|
||||
class ChapterListItem extends Equatable {
|
||||
const ChapterListItem({
|
||||
required this.id,
|
||||
required this.number,
|
||||
required this.title,
|
||||
this.volumeNumber,
|
||||
this.volumeTitle,
|
||||
this.volumeChapterNumber,
|
||||
required this.createdAt,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final int number;
|
||||
final String title;
|
||||
final int? volumeNumber;
|
||||
final String? volumeTitle;
|
||||
final int? volumeChapterNumber;
|
||||
final DateTime createdAt;
|
||||
|
||||
factory ChapterListItem.fromJson(Map<String, dynamic> json) => ChapterListItem(
|
||||
id: json['id'] as String,
|
||||
number: (json['number'] as num).toInt(),
|
||||
title: json['title'] as String,
|
||||
volumeNumber: json['volumeNumber'] as int?,
|
||||
volumeTitle: json['volumeTitle'] as String?,
|
||||
volumeChapterNumber: json['volumeChapterNumber'] as int?,
|
||||
createdAt: DateTime.parse(json['createdAt'] as String),
|
||||
);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [id, number];
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
class CommentModel extends Equatable {
|
||||
const CommentModel({
|
||||
required this.id,
|
||||
required this.userId,
|
||||
required this.username,
|
||||
required this.novelId,
|
||||
required this.content,
|
||||
required this.createdAt,
|
||||
this.avatarUrl,
|
||||
this.chapterId,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final String userId;
|
||||
final String username;
|
||||
final String novelId;
|
||||
final String content;
|
||||
final DateTime createdAt;
|
||||
final String? avatarUrl;
|
||||
final String? chapterId;
|
||||
|
||||
factory CommentModel.fromJson(Map<String, dynamic> json) => CommentModel(
|
||||
id: json['id'] as String,
|
||||
userId: json['userId'] as String,
|
||||
username: json['username'] as String? ?? 'User',
|
||||
novelId: json['novelId'] as String,
|
||||
content: json['content'] as String,
|
||||
createdAt: DateTime.parse(json['createdAt'] as String),
|
||||
avatarUrl: json['avatarUrl'] as String?,
|
||||
chapterId: json['chapterId'] as String?,
|
||||
);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [id];
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
class NovelModel extends Equatable {
|
||||
const NovelModel({
|
||||
required this.id,
|
||||
required this.title,
|
||||
required this.slug,
|
||||
required this.authorName,
|
||||
required this.status,
|
||||
required this.totalChapters,
|
||||
this.originalTitle,
|
||||
this.description,
|
||||
this.coverUrl,
|
||||
this.coverColor,
|
||||
this.views = 0,
|
||||
this.rating = 0,
|
||||
this.ratingCount = 0,
|
||||
this.bookmarkCount = 0,
|
||||
this.genres = const [],
|
||||
this.seriesId,
|
||||
this.series,
|
||||
this.latestChapter,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final String title;
|
||||
final String slug;
|
||||
final String authorName;
|
||||
final String status;
|
||||
final int totalChapters;
|
||||
final String? originalTitle;
|
||||
final String? description;
|
||||
final String? coverUrl;
|
||||
final String? coverColor;
|
||||
final int views;
|
||||
final double rating;
|
||||
final int ratingCount;
|
||||
final int bookmarkCount;
|
||||
final List<GenreModel> genres;
|
||||
final String? seriesId;
|
||||
final SeriesModel? series;
|
||||
final LatestChapterInfo? latestChapter;
|
||||
|
||||
factory NovelModel.fromJson(Map<String, dynamic> json) => NovelModel(
|
||||
id: json['id'] as String,
|
||||
title: json['title'] as String,
|
||||
slug: json['slug'] as String,
|
||||
authorName: json['authorName'] as String,
|
||||
status: json['status'] as String,
|
||||
totalChapters: (json['totalChapters'] as num).toInt(),
|
||||
originalTitle: json['originalTitle'] as String?,
|
||||
description: json['description'] as String?,
|
||||
coverUrl: json['coverUrl'] as String?,
|
||||
coverColor: json['coverColor'] as String?,
|
||||
views: (json['views'] as num?)?.toInt() ?? 0,
|
||||
rating: (json['rating'] as num?)?.toDouble() ?? 0,
|
||||
ratingCount: (json['ratingCount'] as num?)?.toInt() ?? 0,
|
||||
bookmarkCount: (json['bookmarkCount'] as num?)?.toInt() ?? 0,
|
||||
genres: (json['genres'] as List<dynamic>?)
|
||||
?.map((g) => GenreModel.fromJson(g as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
[],
|
||||
seriesId: json['seriesId'] as String?,
|
||||
series: json['series'] != null
|
||||
? SeriesModel.fromJson(json['series'] as Map<String, dynamic>)
|
||||
: null,
|
||||
latestChapter: json['latestChapter'] != null
|
||||
? LatestChapterInfo.fromJson(
|
||||
json['latestChapter'] as Map<String, dynamic>)
|
||||
: null,
|
||||
);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [id, slug];
|
||||
}
|
||||
|
||||
class GenreModel extends Equatable {
|
||||
const GenreModel({required this.id, required this.name, required this.slug, this.description, this.icon, this.novelCount = 0});
|
||||
final String id;
|
||||
final String name;
|
||||
final String slug;
|
||||
final String? description;
|
||||
final String? icon;
|
||||
final int novelCount;
|
||||
|
||||
factory GenreModel.fromJson(Map<String, dynamic> json) => GenreModel(
|
||||
id: json['id'] as String,
|
||||
name: json['name'] as String,
|
||||
slug: json['slug'] as String,
|
||||
description: json['description'] as String?,
|
||||
icon: json['icon'] as String?,
|
||||
novelCount: (json['novelCount'] as num?)?.toInt() ?? 0,
|
||||
);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [id, slug];
|
||||
}
|
||||
|
||||
class SeriesModel extends Equatable {
|
||||
const SeriesModel({required this.id, required this.name, required this.slug, this.novels = const []});
|
||||
final String id;
|
||||
final String name;
|
||||
final String slug;
|
||||
final List<NovelModel> novels;
|
||||
|
||||
factory SeriesModel.fromJson(Map<String, dynamic> json) => SeriesModel(
|
||||
id: json['id'] as String,
|
||||
name: json['name'] as String,
|
||||
slug: json['slug'] as String,
|
||||
novels: (json['novels'] as List<dynamic>?)
|
||||
?.map((n) => NovelModel.fromJson(n as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
[],
|
||||
);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [id, slug];
|
||||
}
|
||||
|
||||
class LatestChapterInfo extends Equatable {
|
||||
const LatestChapterInfo({required this.number, required this.title, required this.createdAt});
|
||||
final int number;
|
||||
final String title;
|
||||
final DateTime createdAt;
|
||||
|
||||
factory LatestChapterInfo.fromJson(Map<String, dynamic> json) => LatestChapterInfo(
|
||||
number: (json['number'] as num).toInt(),
|
||||
title: json['title'] as String,
|
||||
createdAt: DateTime.parse(json['createdAt'] as String),
|
||||
);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [number];
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
class ReadingSettings {
|
||||
const ReadingSettings({
|
||||
this.fontSize = 18,
|
||||
this.lineHeight = 1.8,
|
||||
this.letterSpacing = 0,
|
||||
this.fontFamily = 'serif',
|
||||
});
|
||||
|
||||
final double fontSize;
|
||||
final double lineHeight;
|
||||
final double letterSpacing;
|
||||
final String fontFamily;
|
||||
|
||||
ReadingSettings copyWith({
|
||||
double? fontSize,
|
||||
double? lineHeight,
|
||||
double? letterSpacing,
|
||||
String? fontFamily,
|
||||
}) =>
|
||||
ReadingSettings(
|
||||
fontSize: fontSize ?? this.fontSize,
|
||||
lineHeight: lineHeight ?? this.lineHeight,
|
||||
letterSpacing: letterSpacing ?? this.letterSpacing,
|
||||
fontFamily: fontFamily ?? this.fontFamily,
|
||||
);
|
||||
|
||||
factory ReadingSettings.fromJson(Map<String, dynamic> json) => ReadingSettings(
|
||||
fontSize: (json['fontSize'] as num?)?.toDouble() ?? 18,
|
||||
lineHeight: (json['lineHeight'] as num?)?.toDouble() ?? 1.8,
|
||||
letterSpacing: (json['letterSpacing'] as num?)?.toDouble() ?? 0,
|
||||
fontFamily: json['fontFamily'] as String? ?? 'serif',
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'fontSize': fontSize,
|
||||
'lineHeight': lineHeight,
|
||||
'letterSpacing': letterSpacing,
|
||||
'fontFamily': fontFamily,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
class UserModel extends Equatable {
|
||||
const UserModel({
|
||||
required this.id,
|
||||
required this.email,
|
||||
this.name,
|
||||
this.image,
|
||||
this.role = 'USER',
|
||||
});
|
||||
|
||||
final String id;
|
||||
final String email;
|
||||
final String? name;
|
||||
final String? image;
|
||||
final String role;
|
||||
|
||||
factory UserModel.fromJson(Map<String, dynamic> json) => UserModel(
|
||||
id: json['id'] as String,
|
||||
email: json['email'] as String,
|
||||
name: json['name'] as String?,
|
||||
image: json['image'] as String?,
|
||||
role: json['role'] as String? ?? 'USER',
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'id': id,
|
||||
'email': email,
|
||||
'name': name,
|
||||
'image': image,
|
||||
'role': role,
|
||||
};
|
||||
|
||||
@override
|
||||
List<Object?> get props => [id, email];
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../config/app_config.dart';
|
||||
import '../storage/secure_store.dart';
|
||||
import 'api_client.dart';
|
||||
|
||||
final secureStoreProvider = Provider<SecureStore>((ref) => SecureStore());
|
||||
|
||||
final apiClientProvider = Provider<ApiClient>((ref) {
|
||||
final secureStore = ref.watch(secureStoreProvider);
|
||||
return ApiClient(baseUrl: AppConfig.baseUrl, secureStore: secureStore);
|
||||
});
|
||||
@@ -1,31 +1,61 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../models/reading_settings.dart';
|
||||
|
||||
class LocalStore {
|
||||
static const _kFontSize = 'reader_font_size';
|
||||
static const _kLineHeight = 'reader_line_height';
|
||||
static const _kLetterSpacing = 'reader_letter_spacing';
|
||||
static const _kFontFamily = 'reader_font_family';
|
||||
static const _kProgressChapterId = 'progress_chapter_id_';
|
||||
static const _kProgressChapterNum = 'progress_chapter_num_';
|
||||
static const _kProgressOffset = 'progress_offset_';
|
||||
|
||||
Future<void> saveReadingSettings({
|
||||
required double fontSize,
|
||||
required double lineHeight,
|
||||
required double letterSpacing,
|
||||
required String fontFamily,
|
||||
}) async {
|
||||
// ── Reading settings ──────────────────────────────────────────────────────
|
||||
|
||||
Future<void> saveReadingSettings(ReadingSettings settings) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setDouble(_kFontSize, fontSize);
|
||||
await prefs.setDouble(_kLineHeight, lineHeight);
|
||||
await prefs.setDouble(_kLetterSpacing, letterSpacing);
|
||||
await prefs.setString(_kFontFamily, fontFamily);
|
||||
await prefs.setDouble(_kFontSize, settings.fontSize);
|
||||
await prefs.setDouble(_kLineHeight, settings.lineHeight);
|
||||
await prefs.setDouble(_kLetterSpacing, settings.letterSpacing);
|
||||
await prefs.setString(_kFontFamily, settings.fontFamily);
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getReadingSettings() async {
|
||||
Future<ReadingSettings?> loadReadingSettings() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
if (!prefs.containsKey(_kFontSize)) return null;
|
||||
return ReadingSettings(
|
||||
fontSize: prefs.getDouble(_kFontSize) ?? 18,
|
||||
lineHeight: prefs.getDouble(_kLineHeight) ?? 1.8,
|
||||
letterSpacing: prefs.getDouble(_kLetterSpacing) ?? 0,
|
||||
fontFamily: prefs.getString(_kFontFamily) ?? 'serif',
|
||||
);
|
||||
}
|
||||
|
||||
// ── Reading progress ──────────────────────────────────────────────────────
|
||||
|
||||
Future<void> saveProgress(
|
||||
String novelId,
|
||||
String chapterId,
|
||||
int chapterNumber,
|
||||
double offset,
|
||||
) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString('$_kProgressChapterId$novelId', chapterId);
|
||||
await prefs.setInt('$_kProgressChapterNum$novelId', chapterNumber);
|
||||
await prefs.setDouble('$_kProgressOffset$novelId', offset);
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>?> loadProgress(String novelId) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final chapterId = prefs.getString('$_kProgressChapterId$novelId');
|
||||
if (chapterId == null) return null;
|
||||
return {
|
||||
'fontSize': prefs.getDouble(_kFontSize) ?? 18,
|
||||
'lineHeight': prefs.getDouble(_kLineHeight) ?? 1.8,
|
||||
'letterSpacing': prefs.getDouble(_kLetterSpacing) ?? 0,
|
||||
'fontFamily': prefs.getString(_kFontFamily) ?? 'serif',
|
||||
'chapterId': chapterId,
|
||||
'chapterNumber': prefs.getInt('$_kProgressChapterNum$novelId') ?? 1,
|
||||
'scrollOffset': prefs.getDouble('$_kProgressOffset$novelId') ?? 0.0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
final localStoreProvider = Provider<LocalStore>((_) => LocalStore());
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../models/chapter_model.dart';
|
||||
|
||||
class OfflineCache {
|
||||
static const _dbName = 'reader_offline.db';
|
||||
static const _version = 1;
|
||||
|
||||
Database? _db;
|
||||
|
||||
Future<Database> get db async {
|
||||
_db ??= await _open();
|
||||
return _db!;
|
||||
}
|
||||
|
||||
Future<Database> _open() async {
|
||||
final dir = await getDatabasesPath();
|
||||
final path = p.join(dir, _dbName);
|
||||
return openDatabase(
|
||||
path,
|
||||
version: _version,
|
||||
onCreate: (db, _) async {
|
||||
await db.execute('''
|
||||
CREATE TABLE cached_chapters (
|
||||
id TEXT PRIMARY KEY,
|
||||
novel_id TEXT NOT NULL,
|
||||
chapter_number INTEGER NOT NULL,
|
||||
title TEXT,
|
||||
content TEXT NOT NULL,
|
||||
prev_chapter_id TEXT,
|
||||
prev_chapter_number INTEGER,
|
||||
next_chapter_id TEXT,
|
||||
next_chapter_number INTEGER,
|
||||
volume_title TEXT,
|
||||
cached_at INTEGER NOT NULL
|
||||
)
|
||||
''');
|
||||
await db.execute('''
|
||||
CREATE INDEX idx_novel_chapters ON cached_chapters(novel_id, chapter_number)
|
||||
''');
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> saveChapter(ChapterModel chapter) async {
|
||||
final database = await db;
|
||||
await database.insert(
|
||||
'cached_chapters',
|
||||
{
|
||||
'id': chapter.id,
|
||||
'novel_id': chapter.novelId,
|
||||
'chapter_number': chapter.number,
|
||||
'title': chapter.title,
|
||||
'content': chapter.content,
|
||||
'prev_chapter_id': chapter.prevChapterId,
|
||||
'prev_chapter_number': chapter.prevChapterNumber,
|
||||
'next_chapter_id': chapter.nextChapterId,
|
||||
'next_chapter_number': chapter.nextChapterNumber,
|
||||
'volume_title': chapter.volumeTitle,
|
||||
'cached_at': DateTime.now().millisecondsSinceEpoch,
|
||||
},
|
||||
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||
);
|
||||
}
|
||||
|
||||
Future<ChapterModel?> loadChapter(String chapterId) async {
|
||||
final database = await db;
|
||||
final rows = await database.query(
|
||||
'cached_chapters',
|
||||
where: 'id = ?',
|
||||
whereArgs: [chapterId],
|
||||
limit: 1,
|
||||
);
|
||||
if (rows.isEmpty) return null;
|
||||
return _rowToChapter(rows.first);
|
||||
}
|
||||
|
||||
Future<List<String>> cachedChapterIdsForNovel(String novelId) async {
|
||||
final database = await db;
|
||||
final rows = await database.query(
|
||||
'cached_chapters',
|
||||
columns: ['id'],
|
||||
where: 'novel_id = ?',
|
||||
whereArgs: [novelId],
|
||||
orderBy: 'chapter_number ASC',
|
||||
);
|
||||
return rows.map((r) => r['id'] as String).toList();
|
||||
}
|
||||
|
||||
Future<void> deleteNovelCache(String novelId) async {
|
||||
final database = await db;
|
||||
await database.delete(
|
||||
'cached_chapters',
|
||||
where: 'novel_id = ?',
|
||||
whereArgs: [novelId],
|
||||
);
|
||||
}
|
||||
|
||||
Future<int> getCacheSizeBytes() async {
|
||||
final database = await db;
|
||||
final result = await database.rawQuery(
|
||||
'SELECT SUM(LENGTH(content)) as total FROM cached_chapters',
|
||||
);
|
||||
return (result.first['total'] as int?) ?? 0;
|
||||
}
|
||||
|
||||
ChapterModel _rowToChapter(Map<String, dynamic> row) {
|
||||
return ChapterModel(
|
||||
id: row['id'] as String,
|
||||
novelId: row['novel_id'] as String,
|
||||
number: row['chapter_number'] as int,
|
||||
title: (row['title'] as String?) ?? '',
|
||||
content: row['content'] as String,
|
||||
prevChapterId: row['prev_chapter_id'] as String?,
|
||||
prevChapterNumber: row['prev_chapter_number'] as int?,
|
||||
nextChapterId: row['next_chapter_id'] as String?,
|
||||
nextChapterNumber: row['next_chapter_number'] as int?,
|
||||
volumeTitle: row['volume_title'] as String?,
|
||||
createdAt: DateTime.fromMillisecondsSinceEpoch(row['cached_at'] as int),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final offlineCacheProvider = Provider<OfflineCache>((_) => OfflineCache());
|
||||
Reference in New Issue
Block a user