fixes
This commit is contained in:
parent
7ac591172b
commit
c4c42e4af5
27 changed files with 435 additions and 236 deletions
|
|
@ -25,11 +25,11 @@ class AppRouter extends $AppRouter {
|
||||||
transitionsBuilder: TransitionsBuilders.noTransition,
|
transitionsBuilder: TransitionsBuilders.noTransition,
|
||||||
durationInMilliseconds: 200,
|
durationInMilliseconds: 200,
|
||||||
),
|
),
|
||||||
CustomRoute(
|
// CustomRoute(
|
||||||
page: ExplorePage.page,
|
// page: ExplorePage.page,
|
||||||
transitionsBuilder: TransitionsBuilders.noTransition,
|
// transitionsBuilder: TransitionsBuilders.noTransition,
|
||||||
durationInMilliseconds: 200,
|
// durationInMilliseconds: 200,
|
||||||
),
|
// ),
|
||||||
// CustomRoute(
|
// CustomRoute(
|
||||||
// page: ProfilePage.page,
|
// page: ProfilePage.page,
|
||||||
// transitionsBuilder: TransitionsBuilders.noTransition,
|
// transitionsBuilder: TransitionsBuilders.noTransition,
|
||||||
|
|
@ -71,6 +71,13 @@ class AppRouter extends $AppRouter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void closeAuthPage() {
|
||||||
|
final router = main.appRouter;
|
||||||
|
if (router.isRouteActive(AuthPage.name)) {
|
||||||
|
router.replace(PageRouteInfo(HomePage.name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void openAuthOrProfile() {
|
static void openAuthOrProfile() {
|
||||||
if (locator.userManager.hasUser) {
|
if (locator.userManager.hasUser) {
|
||||||
openProfile();
|
openProfile();
|
||||||
|
|
|
||||||
|
|
@ -8,35 +8,33 @@
|
||||||
// coverage:ignore-file
|
// coverage:ignore-file
|
||||||
|
|
||||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||||
import 'dart:ui' as _i11;
|
import 'dart:ui' as _i10;
|
||||||
|
|
||||||
import 'package:auto_route/auto_route.dart' as _i9;
|
import 'package:auto_route/auto_route.dart' as _i8;
|
||||||
import 'package:flutter/cupertino.dart' as _i13;
|
import 'package:flutter/material.dart' as _i11;
|
||||||
import 'package:flutter/material.dart' as _i12;
|
import 'package:mnemo_cards/features/tests/test_page.dart' as _i7;
|
||||||
import 'package:mnemo_cards/features/tests/test_page.dart' as _i8;
|
import 'package:mnemo_cards/main.dart' as _i5;
|
||||||
import 'package:mnemo_cards/main.dart' as _i6;
|
|
||||||
import 'package:mnemo_cards/pages/auth_page.dart' as _i1;
|
import 'package:mnemo_cards/pages/auth_page.dart' as _i1;
|
||||||
import 'package:mnemo_cards/pages/explore_page.dart' as _i4;
|
import 'package:mnemo_cards/pages/home_page.dart' as _i4;
|
||||||
import 'package:mnemo_cards/pages/home_page.dart' as _i5;
|
import 'package:mnemo_cards/pages/profile_page.dart' as _i6;
|
||||||
import 'package:mnemo_cards/pages/profile_page.dart' as _i7;
|
|
||||||
import 'package:mnemo_cards/widgets/card_game/card_game_page.dart' as _i2;
|
import 'package:mnemo_cards/widgets/card_game/card_game_page.dart' as _i2;
|
||||||
import 'package:mnemo_cards/widgets/card_pack/card_pack_page.dart' as _i3;
|
import 'package:mnemo_cards/widgets/card_pack/card_pack_page.dart' as _i3;
|
||||||
import 'package:mnemo_cards_common/mnemo_cards_common.dart' as _i10;
|
import 'package:mnemo_cards_common/mnemo_cards_common.dart' as _i9;
|
||||||
|
|
||||||
abstract class $AppRouter extends _i9.RootStackRouter {
|
abstract class $AppRouter extends _i8.RootStackRouter {
|
||||||
$AppRouter({super.navigatorKey});
|
$AppRouter({super.navigatorKey});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final Map<String, _i9.PageFactory> pagesMap = {
|
final Map<String, _i8.PageFactory> pagesMap = {
|
||||||
AuthPage.name: (routeData) {
|
AuthPage.name: (routeData) {
|
||||||
return _i9.AutoRoutePage<dynamic>(
|
return _i8.AutoRoutePage<dynamic>(
|
||||||
routeData: routeData,
|
routeData: routeData,
|
||||||
child: _i1.AuthPage(),
|
child: _i1.AuthPage(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
CardGamePage.name: (routeData) {
|
CardGamePage.name: (routeData) {
|
||||||
final args = routeData.argsAs<CardGamePageArgs>();
|
final args = routeData.argsAs<CardGamePageArgs>();
|
||||||
return _i9.AutoRoutePage<dynamic>(
|
return _i8.AutoRoutePage<dynamic>(
|
||||||
routeData: routeData,
|
routeData: routeData,
|
||||||
child: _i2.CardGamePage(
|
child: _i2.CardGamePage(
|
||||||
cards: args.cards,
|
cards: args.cards,
|
||||||
|
|
@ -50,7 +48,7 @@ abstract class $AppRouter extends _i9.RootStackRouter {
|
||||||
},
|
},
|
||||||
CardPackPage.name: (routeData) {
|
CardPackPage.name: (routeData) {
|
||||||
final args = routeData.argsAs<CardPackPageArgs>();
|
final args = routeData.argsAs<CardPackPageArgs>();
|
||||||
return _i9.AutoRoutePage<dynamic>(
|
return _i8.AutoRoutePage<dynamic>(
|
||||||
routeData: routeData,
|
routeData: routeData,
|
||||||
child: _i3.CardPackPage(
|
child: _i3.CardPackPage(
|
||||||
args.previewDto,
|
args.previewDto,
|
||||||
|
|
@ -58,35 +56,29 @@ abstract class $AppRouter extends _i9.RootStackRouter {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
ExplorePage.name: (routeData) {
|
|
||||||
return _i9.AutoRoutePage<dynamic>(
|
|
||||||
routeData: routeData,
|
|
||||||
child: _i4.ExplorePage(),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
HomePage.name: (routeData) {
|
HomePage.name: (routeData) {
|
||||||
return _i9.AutoRoutePage<dynamic>(
|
return _i8.AutoRoutePage<dynamic>(
|
||||||
routeData: routeData,
|
routeData: routeData,
|
||||||
child: _i5.HomePage(),
|
child: _i4.HomePage(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
MainTabsPage.name: (routeData) {
|
MainTabsPage.name: (routeData) {
|
||||||
return _i9.AutoRoutePage<dynamic>(
|
return _i8.AutoRoutePage<dynamic>(
|
||||||
routeData: routeData,
|
routeData: routeData,
|
||||||
child: const _i6.MainTabsPage(),
|
child: const _i5.MainTabsPage(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
ProfilePage.name: (routeData) {
|
ProfilePage.name: (routeData) {
|
||||||
return _i9.AutoRoutePage<dynamic>(
|
return _i8.AutoRoutePage<dynamic>(
|
||||||
routeData: routeData,
|
routeData: routeData,
|
||||||
child: _i7.ProfilePage(),
|
child: _i6.ProfilePage(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
TestPage.name: (routeData) {
|
TestPage.name: (routeData) {
|
||||||
final args = routeData.argsAs<TestPageArgs>();
|
final args = routeData.argsAs<TestPageArgs>();
|
||||||
return _i9.AutoRoutePage<dynamic>(
|
return _i8.AutoRoutePage<dynamic>(
|
||||||
routeData: routeData,
|
routeData: routeData,
|
||||||
child: _i8.TestPage(args.testId),
|
child: _i7.TestPage(args.testId),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -94,8 +86,8 @@ abstract class $AppRouter extends _i9.RootStackRouter {
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i1.AuthPage]
|
/// [_i1.AuthPage]
|
||||||
class AuthPage extends _i9.PageRouteInfo<void> {
|
class AuthPage extends _i8.PageRouteInfo<void> {
|
||||||
const AuthPage({List<_i9.PageRouteInfo>? children})
|
const AuthPage({List<_i8.PageRouteInfo>? children})
|
||||||
: super(
|
: super(
|
||||||
AuthPage.name,
|
AuthPage.name,
|
||||||
initialChildren: children,
|
initialChildren: children,
|
||||||
|
|
@ -103,20 +95,20 @@ class AuthPage extends _i9.PageRouteInfo<void> {
|
||||||
|
|
||||||
static const String name = 'AuthPage';
|
static const String name = 'AuthPage';
|
||||||
|
|
||||||
static const _i9.PageInfo<void> page = _i9.PageInfo<void>(name);
|
static const _i8.PageInfo<void> page = _i8.PageInfo<void>(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i2.CardGamePage]
|
/// [_i2.CardGamePage]
|
||||||
class CardGamePage extends _i9.PageRouteInfo<CardGamePageArgs> {
|
class CardGamePage extends _i8.PageRouteInfo<CardGamePageArgs> {
|
||||||
CardGamePage({
|
CardGamePage({
|
||||||
required List<_i10.GameCardDto> cards,
|
required List<_i9.GameCardDto> cards,
|
||||||
required _i11.Color? color,
|
required _i10.Color? color,
|
||||||
int? startCardId,
|
int? startCardId,
|
||||||
bool infinite = false,
|
bool infinite = false,
|
||||||
bool shuffle = true,
|
bool shuffle = true,
|
||||||
_i12.Key? key,
|
_i11.Key? key,
|
||||||
List<_i9.PageRouteInfo>? children,
|
List<_i8.PageRouteInfo>? children,
|
||||||
}) : super(
|
}) : super(
|
||||||
CardGamePage.name,
|
CardGamePage.name,
|
||||||
args: CardGamePageArgs(
|
args: CardGamePageArgs(
|
||||||
|
|
@ -132,8 +124,8 @@ class CardGamePage extends _i9.PageRouteInfo<CardGamePageArgs> {
|
||||||
|
|
||||||
static const String name = 'CardGamePage';
|
static const String name = 'CardGamePage';
|
||||||
|
|
||||||
static const _i9.PageInfo<CardGamePageArgs> page =
|
static const _i8.PageInfo<CardGamePageArgs> page =
|
||||||
_i9.PageInfo<CardGamePageArgs>(name);
|
_i8.PageInfo<CardGamePageArgs>(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
class CardGamePageArgs {
|
class CardGamePageArgs {
|
||||||
|
|
@ -146,9 +138,9 @@ class CardGamePageArgs {
|
||||||
this.key,
|
this.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
final List<_i10.GameCardDto> cards;
|
final List<_i9.GameCardDto> cards;
|
||||||
|
|
||||||
final _i11.Color? color;
|
final _i10.Color? color;
|
||||||
|
|
||||||
final int? startCardId;
|
final int? startCardId;
|
||||||
|
|
||||||
|
|
@ -156,7 +148,7 @@ class CardGamePageArgs {
|
||||||
|
|
||||||
final bool shuffle;
|
final bool shuffle;
|
||||||
|
|
||||||
final _i12.Key? key;
|
final _i11.Key? key;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
|
|
@ -166,11 +158,11 @@ class CardGamePageArgs {
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i3.CardPackPage]
|
/// [_i3.CardPackPage]
|
||||||
class CardPackPage extends _i9.PageRouteInfo<CardPackPageArgs> {
|
class CardPackPage extends _i8.PageRouteInfo<CardPackPageArgs> {
|
||||||
CardPackPage({
|
CardPackPage({
|
||||||
required _i10.CardPackPreviewDto previewDto,
|
required _i9.CardPackPreviewDto previewDto,
|
||||||
_i13.Key? key,
|
_i11.Key? key,
|
||||||
List<_i9.PageRouteInfo>? children,
|
List<_i8.PageRouteInfo>? children,
|
||||||
}) : super(
|
}) : super(
|
||||||
CardPackPage.name,
|
CardPackPage.name,
|
||||||
args: CardPackPageArgs(
|
args: CardPackPageArgs(
|
||||||
|
|
@ -182,8 +174,8 @@ class CardPackPage extends _i9.PageRouteInfo<CardPackPageArgs> {
|
||||||
|
|
||||||
static const String name = 'CardPackPage';
|
static const String name = 'CardPackPage';
|
||||||
|
|
||||||
static const _i9.PageInfo<CardPackPageArgs> page =
|
static const _i8.PageInfo<CardPackPageArgs> page =
|
||||||
_i9.PageInfo<CardPackPageArgs>(name);
|
_i8.PageInfo<CardPackPageArgs>(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
class CardPackPageArgs {
|
class CardPackPageArgs {
|
||||||
|
|
@ -192,9 +184,9 @@ class CardPackPageArgs {
|
||||||
this.key,
|
this.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
final _i10.CardPackPreviewDto previewDto;
|
final _i9.CardPackPreviewDto previewDto;
|
||||||
|
|
||||||
final _i13.Key? key;
|
final _i11.Key? key;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
|
|
@ -203,23 +195,9 @@ class CardPackPageArgs {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i4.ExplorePage]
|
/// [_i4.HomePage]
|
||||||
class ExplorePage extends _i9.PageRouteInfo<void> {
|
class HomePage extends _i8.PageRouteInfo<void> {
|
||||||
const ExplorePage({List<_i9.PageRouteInfo>? children})
|
const HomePage({List<_i8.PageRouteInfo>? children})
|
||||||
: super(
|
|
||||||
ExplorePage.name,
|
|
||||||
initialChildren: children,
|
|
||||||
);
|
|
||||||
|
|
||||||
static const String name = 'ExplorePage';
|
|
||||||
|
|
||||||
static const _i9.PageInfo<void> page = _i9.PageInfo<void>(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// generated route for
|
|
||||||
/// [_i5.HomePage]
|
|
||||||
class HomePage extends _i9.PageRouteInfo<void> {
|
|
||||||
const HomePage({List<_i9.PageRouteInfo>? children})
|
|
||||||
: super(
|
: super(
|
||||||
HomePage.name,
|
HomePage.name,
|
||||||
initialChildren: children,
|
initialChildren: children,
|
||||||
|
|
@ -227,13 +205,13 @@ class HomePage extends _i9.PageRouteInfo<void> {
|
||||||
|
|
||||||
static const String name = 'HomePage';
|
static const String name = 'HomePage';
|
||||||
|
|
||||||
static const _i9.PageInfo<void> page = _i9.PageInfo<void>(name);
|
static const _i8.PageInfo<void> page = _i8.PageInfo<void>(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i6.MainTabsPage]
|
/// [_i5.MainTabsPage]
|
||||||
class MainTabsPage extends _i9.PageRouteInfo<void> {
|
class MainTabsPage extends _i8.PageRouteInfo<void> {
|
||||||
const MainTabsPage({List<_i9.PageRouteInfo>? children})
|
const MainTabsPage({List<_i8.PageRouteInfo>? children})
|
||||||
: super(
|
: super(
|
||||||
MainTabsPage.name,
|
MainTabsPage.name,
|
||||||
initialChildren: children,
|
initialChildren: children,
|
||||||
|
|
@ -241,13 +219,13 @@ class MainTabsPage extends _i9.PageRouteInfo<void> {
|
||||||
|
|
||||||
static const String name = 'MainTabsPage';
|
static const String name = 'MainTabsPage';
|
||||||
|
|
||||||
static const _i9.PageInfo<void> page = _i9.PageInfo<void>(name);
|
static const _i8.PageInfo<void> page = _i8.PageInfo<void>(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i7.ProfilePage]
|
/// [_i6.ProfilePage]
|
||||||
class ProfilePage extends _i9.PageRouteInfo<void> {
|
class ProfilePage extends _i8.PageRouteInfo<void> {
|
||||||
const ProfilePage({List<_i9.PageRouteInfo>? children})
|
const ProfilePage({List<_i8.PageRouteInfo>? children})
|
||||||
: super(
|
: super(
|
||||||
ProfilePage.name,
|
ProfilePage.name,
|
||||||
initialChildren: children,
|
initialChildren: children,
|
||||||
|
|
@ -255,15 +233,15 @@ class ProfilePage extends _i9.PageRouteInfo<void> {
|
||||||
|
|
||||||
static const String name = 'ProfilePage';
|
static const String name = 'ProfilePage';
|
||||||
|
|
||||||
static const _i9.PageInfo<void> page = _i9.PageInfo<void>(name);
|
static const _i8.PageInfo<void> page = _i8.PageInfo<void>(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i8.TestPage]
|
/// [_i7.TestPage]
|
||||||
class TestPage extends _i9.PageRouteInfo<TestPageArgs> {
|
class TestPage extends _i8.PageRouteInfo<TestPageArgs> {
|
||||||
TestPage({
|
TestPage({
|
||||||
required String testId,
|
required String testId,
|
||||||
List<_i9.PageRouteInfo>? children,
|
List<_i8.PageRouteInfo>? children,
|
||||||
}) : super(
|
}) : super(
|
||||||
TestPage.name,
|
TestPage.name,
|
||||||
args: TestPageArgs(testId: testId),
|
args: TestPageArgs(testId: testId),
|
||||||
|
|
@ -272,8 +250,8 @@ class TestPage extends _i9.PageRouteInfo<TestPageArgs> {
|
||||||
|
|
||||||
static const String name = 'TestPage';
|
static const String name = 'TestPage';
|
||||||
|
|
||||||
static const _i9.PageInfo<TestPageArgs> page =
|
static const _i8.PageInfo<TestPageArgs> page =
|
||||||
_i9.PageInfo<TestPageArgs>(name);
|
_i8.PageInfo<TestPageArgs>(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestPageArgs {
|
class TestPageArgs {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:firebase_analytics/firebase_analytics.dart';
|
import 'package:firebase_analytics/firebase_analytics.dart';
|
||||||
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||||||
import 'package:mnemo_cards/managers/user_manager.dart';
|
import 'package:mnemo_cards/managers/user_manager.dart';
|
||||||
|
import 'package:mnemo_cards/utils/iterable_helper.dart';
|
||||||
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
|
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
|
||||||
|
|
||||||
class Analytics {
|
class Analytics {
|
||||||
|
|
@ -148,22 +149,18 @@ class Analytics {
|
||||||
}.asFirebaseMap);
|
}.asFirebaseMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
static crashlyticsError(Object? e, StackTrace? s) =>
|
static crashlyticsError({
|
||||||
_crashlytics.recordError(e, s);
|
Object? e,
|
||||||
|
StackTrace? s,
|
||||||
static error({Map<String, String?>? data, String? message}) {
|
String? message,
|
||||||
try {
|
Map<String, Object?>? data,
|
||||||
_analytics.logEvent(
|
}) =>
|
||||||
name: 'error',
|
_crashlytics.recordError(
|
||||||
parameters: {
|
e,
|
||||||
'msg': message,
|
s,
|
||||||
if (data != null) ...data,
|
reason: message,
|
||||||
}.asFirebaseMap,
|
information: data?.asFirebaseMap.entries.toList() ?? [],
|
||||||
);
|
);
|
||||||
} catch (e, s) {
|
|
||||||
_crashlytics.recordError(e, s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension on Map {
|
extension on Map {
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,14 @@ import 'package:mnemo_cards_common/mnemo_cards_common.dart';
|
||||||
class ImagesHolder {
|
class ImagesHolder {
|
||||||
Map<String, MemoryImage> _images = {};
|
Map<String, MemoryImage> _images = {};
|
||||||
Map<String, MemoryImage> _packImages = {};
|
Map<String, MemoryImage> _packImages = {};
|
||||||
|
String? packId;
|
||||||
DateTime? unlockTime;
|
DateTime? unlockTime;
|
||||||
|
|
||||||
ImagesHolder();
|
ImagesHolder();
|
||||||
|
|
||||||
void setPackImages(Map<String, MemoryImage> images) {
|
void setPackImages(Map<String, MemoryImage> images, String packId) {
|
||||||
_packImages = Map.from(images);
|
_packImages = Map.from(images);
|
||||||
|
this.packId = packId;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setImage(String key, MemoryImage image) {
|
void setImage(String key, MemoryImage image) {
|
||||||
|
|
@ -22,6 +24,7 @@ class ImagesHolder {
|
||||||
void clear() {
|
void clear() {
|
||||||
_images.clear();
|
_images.clear();
|
||||||
_packImages.clear();
|
_packImages.clear();
|
||||||
|
packId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryImage? get(String id) {
|
MemoryImage? get(String id) {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
import '../../di/locator.dart';
|
import '../../di/locator.dart';
|
||||||
|
import '../analytics/analytics.dart';
|
||||||
|
|
||||||
// persist
|
// persist
|
||||||
class PackCacheManager {
|
class PackCacheManager {
|
||||||
|
|
@ -21,6 +22,24 @@ class PackCacheManager {
|
||||||
_sharedPreferences = await SharedPreferences.getInstance();
|
_sharedPreferences = await SharedPreferences.getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> markPackForUpdate(String packId) async {
|
||||||
|
try {
|
||||||
|
final dir = await _packDirectory(packId);
|
||||||
|
final file = File('${dir.path}/pack.json');
|
||||||
|
if (file.existsSync()) {
|
||||||
|
final json = jsonEncode(await file.readAsString()) as Map;
|
||||||
|
json['id'] = '0';
|
||||||
|
await file.writeAsString(jsonEncode(json));
|
||||||
|
}
|
||||||
|
} catch (e, s) {
|
||||||
|
Analytics.crashlyticsError(
|
||||||
|
e: e,
|
||||||
|
s: s,
|
||||||
|
message: 'Cant mark pack for update',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<CardPackDto> loadPackFromCache(
|
Future<CardPackDto> loadPackFromCache(
|
||||||
String packId, {
|
String packId, {
|
||||||
bool withCardImages = true,
|
bool withCardImages = true,
|
||||||
|
|
@ -41,23 +60,25 @@ class PackCacheManager {
|
||||||
log('Cards ${packId}: ${pack.cards.length}');
|
log('Cards ${packId}: ${pack.cards.length}');
|
||||||
if (pack.cards.isNotEmpty) {
|
if (pack.cards.isNotEmpty) {
|
||||||
log('Loading cards ${packId}');
|
log('Loading cards ${packId}');
|
||||||
for (final dto in pack.cards) {
|
await Future.wait(
|
||||||
|
pack.cards.map((dto) async {
|
||||||
final file = File('${packDir.path}/images/${dto.id}');
|
final file = File('${packDir.path}/images/${dto.id}');
|
||||||
if (file.existsSync()) {
|
if (await file.exists()) {
|
||||||
try {
|
try {
|
||||||
images[dto.id.toString()] =
|
images[dto.id.toString()] =
|
||||||
MemoryImage(await file.readAsBytes());
|
MemoryImage(await file.readAsBytes());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw Exception('Cant load pack $packId $e');
|
markPackForUpdate(packId);
|
||||||
|
throw Exception('Cant read file $packId $e');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log('Cant load card ${dto.id} from $packId, skiping');
|
log('Cant load card ${dto.id} from $packId, skiping');
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
log('Pack loaded ${packId}');
|
log('Pack loaded ${packId}');
|
||||||
locator.imagesHolder.setPackImages(images);
|
locator.imagesHolder.setPackImages(images, packId);
|
||||||
}
|
}
|
||||||
return pack;
|
return pack;
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
|
|
|
||||||
|
|
@ -61,9 +61,6 @@ class PackManager {
|
||||||
yield await _cacheManager.loadPackFromCache(id);
|
yield await _cacheManager.loadPackFromCache(id);
|
||||||
} on Object catch (e, s) {
|
} on Object catch (e, s) {
|
||||||
log('Cant fetch pack', error: e, stackTrace: s);
|
log('Cant fetch pack', error: e, stackTrace: s);
|
||||||
} finally {
|
|
||||||
log('Updating');
|
|
||||||
unawaited(locator.packUpdater.updateAvailablePacks());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -78,9 +75,6 @@ class PackManager {
|
||||||
yield await _cacheManager.loadPackFromCache(id);
|
yield await _cacheManager.loadPackFromCache(id);
|
||||||
} on Object catch (e, s) {
|
} on Object catch (e, s) {
|
||||||
log('Cant fetch pack', error: e, stackTrace: s);
|
log('Cant fetch pack', error: e, stackTrace: s);
|
||||||
} finally {
|
|
||||||
log('Updating');
|
|
||||||
unawaited(locator.packUpdater.updateAvailablePacks());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -93,7 +87,6 @@ class PackManager {
|
||||||
Future<void> clearCache() async {
|
Future<void> clearCache() async {
|
||||||
_packHolder.clear();
|
_packHolder.clear();
|
||||||
await _cacheManager.clearCache();
|
await _cacheManager.clearCache();
|
||||||
await globalSharedPreferences.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<List<CardPackDto>?> get packsStream =>
|
Stream<List<CardPackDto>?> get packsStream =>
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ class PackUpdater {
|
||||||
|
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
final pollStream = Stream.periodic(const Duration(seconds: 60));
|
final pollStream = Stream.periodic(const Duration(seconds: 60));
|
||||||
pollStream.startWith(-1).listen((event) async {
|
pollStream.listen((event) async {
|
||||||
await updateAvailablePacks();
|
await updateAvailablePacks();
|
||||||
try {
|
try {
|
||||||
final r = await _packsApi.getPacksPreviews(null);
|
final r = await _packsApi.getPacksPreviews(null);
|
||||||
|
|
@ -105,18 +105,6 @@ class PackUpdater {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> updatePackInfoWithCardsAndSave(String packId) async {
|
|
||||||
try {
|
|
||||||
// todo
|
|
||||||
// final pack = await _updatePackInfoWithCards(packId);
|
|
||||||
// await _cacheManager.savePack(pack, updateCards: true);
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
log(e.toString());
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<CardPackDto> updatePackAndSaveImages(String packId) async {
|
Future<CardPackDto> updatePackAndSaveImages(String packId) async {
|
||||||
try {
|
try {
|
||||||
log('Updating with cards ${packId}', name: 'updatePackWithCards');
|
log('Updating with cards ${packId}', name: 'updatePackWithCards');
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ enum Result {
|
||||||
correct,
|
correct,
|
||||||
wrong,
|
wrong,
|
||||||
skiped,
|
skiped,
|
||||||
|
not_visited,
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ProgressWidgetState extends State<ProgressWidget> {
|
class _ProgressWidgetState extends State<ProgressWidget> {
|
||||||
|
|
@ -99,7 +100,8 @@ class _ProgressWidgetState extends State<ProgressWidget> {
|
||||||
widthFactor: length == 0
|
widthFactor: length == 0
|
||||||
? 0
|
? 0
|
||||||
: (results
|
: (results
|
||||||
.where((r) => r != Result.skiped)
|
.where((r) =>
|
||||||
|
r != Result.not_visited)
|
||||||
.length) /
|
.length) /
|
||||||
length,
|
length,
|
||||||
duration: Duration(milliseconds: 300),
|
duration: Duration(milliseconds: 300),
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ class InputButtonsTestState extends TestQuestionState {
|
||||||
final List<String> answerIds;
|
final List<String> answerIds;
|
||||||
final bool isCorrect;
|
final bool isCorrect;
|
||||||
final bool isAnswered;
|
final bool isAnswered;
|
||||||
|
final bool isSkipped;
|
||||||
final int seed;
|
final int seed;
|
||||||
|
|
||||||
InputButtonsTestState({
|
InputButtonsTestState({
|
||||||
|
|
@ -22,6 +23,7 @@ class InputButtonsTestState extends TestQuestionState {
|
||||||
this.answerIds = const [],
|
this.answerIds = const [],
|
||||||
this.isCorrect = false,
|
this.isCorrect = false,
|
||||||
this.isAnswered = false,
|
this.isAnswered = false,
|
||||||
|
this.isSkipped = false,
|
||||||
super.testType = TestQuestionType.simple,
|
super.testType = TestQuestionType.simple,
|
||||||
}) {}
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ abstract class _$InputButtonsTestStateCWProxy {
|
||||||
|
|
||||||
InputButtonsTestState isAnswered(bool isAnswered);
|
InputButtonsTestState isAnswered(bool isAnswered);
|
||||||
|
|
||||||
|
InputButtonsTestState isSkipped(bool isSkipped);
|
||||||
|
|
||||||
InputButtonsTestState testType(TestQuestionType testType);
|
InputButtonsTestState testType(TestQuestionType testType);
|
||||||
|
|
||||||
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `InputButtonsTestState(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
|
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `InputButtonsTestState(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
|
||||||
|
|
@ -31,6 +33,7 @@ abstract class _$InputButtonsTestStateCWProxy {
|
||||||
List<String>? answerIds,
|
List<String>? answerIds,
|
||||||
bool? isCorrect,
|
bool? isCorrect,
|
||||||
bool? isAnswered,
|
bool? isAnswered,
|
||||||
|
bool? isSkipped,
|
||||||
TestQuestionType? testType,
|
TestQuestionType? testType,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -59,6 +62,9 @@ class _$InputButtonsTestStateCWProxyImpl
|
||||||
InputButtonsTestState isAnswered(bool isAnswered) =>
|
InputButtonsTestState isAnswered(bool isAnswered) =>
|
||||||
this(isAnswered: isAnswered);
|
this(isAnswered: isAnswered);
|
||||||
|
|
||||||
|
@override
|
||||||
|
InputButtonsTestState isSkipped(bool isSkipped) => this(isSkipped: isSkipped);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
InputButtonsTestState testType(TestQuestionType testType) =>
|
InputButtonsTestState testType(TestQuestionType testType) =>
|
||||||
this(testType: testType);
|
this(testType: testType);
|
||||||
|
|
@ -77,6 +83,7 @@ class _$InputButtonsTestStateCWProxyImpl
|
||||||
Object? answerIds = const $CopyWithPlaceholder(),
|
Object? answerIds = const $CopyWithPlaceholder(),
|
||||||
Object? isCorrect = const $CopyWithPlaceholder(),
|
Object? isCorrect = const $CopyWithPlaceholder(),
|
||||||
Object? isAnswered = const $CopyWithPlaceholder(),
|
Object? isAnswered = const $CopyWithPlaceholder(),
|
||||||
|
Object? isSkipped = const $CopyWithPlaceholder(),
|
||||||
Object? testType = const $CopyWithPlaceholder(),
|
Object? testType = const $CopyWithPlaceholder(),
|
||||||
}) {
|
}) {
|
||||||
return InputButtonsTestState(
|
return InputButtonsTestState(
|
||||||
|
|
@ -101,6 +108,10 @@ class _$InputButtonsTestStateCWProxyImpl
|
||||||
? _value.isAnswered
|
? _value.isAnswered
|
||||||
// ignore: cast_nullable_to_non_nullable
|
// ignore: cast_nullable_to_non_nullable
|
||||||
: isAnswered as bool,
|
: isAnswered as bool,
|
||||||
|
isSkipped: isSkipped == const $CopyWithPlaceholder() || isSkipped == null
|
||||||
|
? _value.isSkipped
|
||||||
|
// ignore: cast_nullable_to_non_nullable
|
||||||
|
: isSkipped as bool,
|
||||||
testType: testType == const $CopyWithPlaceholder() || testType == null
|
testType: testType == const $CopyWithPlaceholder() || testType == null
|
||||||
? _value.testType
|
? _value.testType
|
||||||
// ignore: cast_nullable_to_non_nullable
|
// ignore: cast_nullable_to_non_nullable
|
||||||
|
|
@ -134,6 +145,7 @@ InputButtonsTestState _$InputButtonsTestStateFromJson(
|
||||||
const [],
|
const [],
|
||||||
isCorrect: json['isCorrect'] as bool? ?? false,
|
isCorrect: json['isCorrect'] as bool? ?? false,
|
||||||
isAnswered: json['isAnswered'] as bool? ?? false,
|
isAnswered: json['isAnswered'] as bool? ?? false,
|
||||||
|
isSkipped: json['isSkipped'] as bool? ?? false,
|
||||||
testType:
|
testType:
|
||||||
$enumDecodeNullable(_$TestQuestionTypeEnumMap, json['testType']) ??
|
$enumDecodeNullable(_$TestQuestionTypeEnumMap, json['testType']) ??
|
||||||
TestQuestionType.simple,
|
TestQuestionType.simple,
|
||||||
|
|
@ -147,6 +159,7 @@ Map<String, dynamic> _$InputButtonsTestStateToJson(
|
||||||
'answerIds': instance.answerIds,
|
'answerIds': instance.answerIds,
|
||||||
'isCorrect': instance.isCorrect,
|
'isCorrect': instance.isCorrect,
|
||||||
'isAnswered': instance.isAnswered,
|
'isAnswered': instance.isAnswered,
|
||||||
|
'isSkipped': instance.isSkipped,
|
||||||
'seed': instance.seed,
|
'seed': instance.seed,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ class SimpleTestState extends TestQuestionState {
|
||||||
final String answer;
|
final String answer;
|
||||||
final bool isCorrect;
|
final bool isCorrect;
|
||||||
final bool isAnswered;
|
final bool isAnswered;
|
||||||
|
final bool isSkipped;
|
||||||
final int seed;
|
final int seed;
|
||||||
|
|
||||||
SimpleTestState({
|
SimpleTestState({
|
||||||
|
|
@ -20,6 +21,7 @@ class SimpleTestState extends TestQuestionState {
|
||||||
this.answer = '',
|
this.answer = '',
|
||||||
this.isCorrect = false,
|
this.isCorrect = false,
|
||||||
this.isAnswered = false,
|
this.isAnswered = false,
|
||||||
|
this.isSkipped = false,
|
||||||
super.testType = TestQuestionType.simple,
|
super.testType = TestQuestionType.simple,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ abstract class _$SimpleTestStateCWProxy {
|
||||||
|
|
||||||
SimpleTestState isAnswered(bool isAnswered);
|
SimpleTestState isAnswered(bool isAnswered);
|
||||||
|
|
||||||
|
SimpleTestState isSkipped(bool isSkipped);
|
||||||
|
|
||||||
SimpleTestState testType(TestQuestionType testType);
|
SimpleTestState testType(TestQuestionType testType);
|
||||||
|
|
||||||
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `SimpleTestState(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
|
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `SimpleTestState(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
|
||||||
|
|
@ -28,6 +30,7 @@ abstract class _$SimpleTestStateCWProxy {
|
||||||
String? answer,
|
String? answer,
|
||||||
bool? isCorrect,
|
bool? isCorrect,
|
||||||
bool? isAnswered,
|
bool? isAnswered,
|
||||||
|
bool? isSkipped,
|
||||||
TestQuestionType? testType,
|
TestQuestionType? testType,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -50,6 +53,9 @@ class _$SimpleTestStateCWProxyImpl implements _$SimpleTestStateCWProxy {
|
||||||
@override
|
@override
|
||||||
SimpleTestState isAnswered(bool isAnswered) => this(isAnswered: isAnswered);
|
SimpleTestState isAnswered(bool isAnswered) => this(isAnswered: isAnswered);
|
||||||
|
|
||||||
|
@override
|
||||||
|
SimpleTestState isSkipped(bool isSkipped) => this(isSkipped: isSkipped);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
SimpleTestState testType(TestQuestionType testType) =>
|
SimpleTestState testType(TestQuestionType testType) =>
|
||||||
this(testType: testType);
|
this(testType: testType);
|
||||||
|
|
@ -67,6 +73,7 @@ class _$SimpleTestStateCWProxyImpl implements _$SimpleTestStateCWProxy {
|
||||||
Object? answer = const $CopyWithPlaceholder(),
|
Object? answer = const $CopyWithPlaceholder(),
|
||||||
Object? isCorrect = const $CopyWithPlaceholder(),
|
Object? isCorrect = const $CopyWithPlaceholder(),
|
||||||
Object? isAnswered = const $CopyWithPlaceholder(),
|
Object? isAnswered = const $CopyWithPlaceholder(),
|
||||||
|
Object? isSkipped = const $CopyWithPlaceholder(),
|
||||||
Object? testType = const $CopyWithPlaceholder(),
|
Object? testType = const $CopyWithPlaceholder(),
|
||||||
}) {
|
}) {
|
||||||
return SimpleTestState(
|
return SimpleTestState(
|
||||||
|
|
@ -87,6 +94,10 @@ class _$SimpleTestStateCWProxyImpl implements _$SimpleTestStateCWProxy {
|
||||||
? _value.isAnswered
|
? _value.isAnswered
|
||||||
// ignore: cast_nullable_to_non_nullable
|
// ignore: cast_nullable_to_non_nullable
|
||||||
: isAnswered as bool,
|
: isAnswered as bool,
|
||||||
|
isSkipped: isSkipped == const $CopyWithPlaceholder() || isSkipped == null
|
||||||
|
? _value.isSkipped
|
||||||
|
// ignore: cast_nullable_to_non_nullable
|
||||||
|
: isSkipped as bool,
|
||||||
testType: testType == const $CopyWithPlaceholder() || testType == null
|
testType: testType == const $CopyWithPlaceholder() || testType == null
|
||||||
? _value.testType
|
? _value.testType
|
||||||
// ignore: cast_nullable_to_non_nullable
|
// ignore: cast_nullable_to_non_nullable
|
||||||
|
|
@ -111,6 +122,7 @@ SimpleTestState _$SimpleTestStateFromJson(Map<String, dynamic> json) =>
|
||||||
answer: json['answer'] as String? ?? '',
|
answer: json['answer'] as String? ?? '',
|
||||||
isCorrect: json['isCorrect'] as bool? ?? false,
|
isCorrect: json['isCorrect'] as bool? ?? false,
|
||||||
isAnswered: json['isAnswered'] as bool? ?? false,
|
isAnswered: json['isAnswered'] as bool? ?? false,
|
||||||
|
isSkipped: json['isSkipped'] as bool? ?? false,
|
||||||
testType:
|
testType:
|
||||||
$enumDecodeNullable(_$TestQuestionTypeEnumMap, json['testType']) ??
|
$enumDecodeNullable(_$TestQuestionTypeEnumMap, json['testType']) ??
|
||||||
TestQuestionType.simple,
|
TestQuestionType.simple,
|
||||||
|
|
@ -122,6 +134,7 @@ Map<String, dynamic> _$SimpleTestStateToJson(SimpleTestState instance) =>
|
||||||
'answer': instance.answer,
|
'answer': instance.answer,
|
||||||
'isCorrect': instance.isCorrect,
|
'isCorrect': instance.isCorrect,
|
||||||
'isAnswered': instance.isAnswered,
|
'isAnswered': instance.isAnswered,
|
||||||
|
'isSkipped': instance.isSkipped,
|
||||||
'seed': instance.seed,
|
'seed': instance.seed,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,12 @@ class TestQuestionState {
|
||||||
|
|
||||||
bool get isCorrect => false;
|
bool get isCorrect => false;
|
||||||
|
|
||||||
bool get isAnswered => false;
|
final bool isAnswered;
|
||||||
|
final bool isSkipped;
|
||||||
|
|
||||||
const TestQuestionState({
|
const TestQuestionState({
|
||||||
required this.testType,
|
required this.testType,
|
||||||
|
this.isAnswered = false,
|
||||||
|
this.isSkipped = false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,10 @@ part of 'test_question_state.dart';
|
||||||
abstract class _$TestQuestionStateCWProxy {
|
abstract class _$TestQuestionStateCWProxy {
|
||||||
TestQuestionState testType(TestQuestionType testType);
|
TestQuestionState testType(TestQuestionType testType);
|
||||||
|
|
||||||
|
TestQuestionState isAnswered(bool isAnswered);
|
||||||
|
|
||||||
|
TestQuestionState isSkipped(bool isSkipped);
|
||||||
|
|
||||||
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `TestQuestionState(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
|
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `TestQuestionState(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
|
||||||
///
|
///
|
||||||
/// Usage
|
/// Usage
|
||||||
|
|
@ -17,6 +21,8 @@ abstract class _$TestQuestionStateCWProxy {
|
||||||
/// ````
|
/// ````
|
||||||
TestQuestionState call({
|
TestQuestionState call({
|
||||||
TestQuestionType? testType,
|
TestQuestionType? testType,
|
||||||
|
bool? isAnswered,
|
||||||
|
bool? isSkipped,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -30,6 +36,12 @@ class _$TestQuestionStateCWProxyImpl implements _$TestQuestionStateCWProxy {
|
||||||
TestQuestionState testType(TestQuestionType testType) =>
|
TestQuestionState testType(TestQuestionType testType) =>
|
||||||
this(testType: testType);
|
this(testType: testType);
|
||||||
|
|
||||||
|
@override
|
||||||
|
TestQuestionState isAnswered(bool isAnswered) => this(isAnswered: isAnswered);
|
||||||
|
|
||||||
|
@override
|
||||||
|
TestQuestionState isSkipped(bool isSkipped) => this(isSkipped: isSkipped);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
||||||
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `TestQuestionState(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
|
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `TestQuestionState(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
|
||||||
|
|
@ -40,12 +52,23 @@ class _$TestQuestionStateCWProxyImpl implements _$TestQuestionStateCWProxy {
|
||||||
/// ````
|
/// ````
|
||||||
TestQuestionState call({
|
TestQuestionState call({
|
||||||
Object? testType = const $CopyWithPlaceholder(),
|
Object? testType = const $CopyWithPlaceholder(),
|
||||||
|
Object? isAnswered = const $CopyWithPlaceholder(),
|
||||||
|
Object? isSkipped = const $CopyWithPlaceholder(),
|
||||||
}) {
|
}) {
|
||||||
return TestQuestionState(
|
return TestQuestionState(
|
||||||
testType: testType == const $CopyWithPlaceholder() || testType == null
|
testType: testType == const $CopyWithPlaceholder() || testType == null
|
||||||
? _value.testType
|
? _value.testType
|
||||||
// ignore: cast_nullable_to_non_nullable
|
// ignore: cast_nullable_to_non_nullable
|
||||||
: testType as TestQuestionType,
|
: testType as TestQuestionType,
|
||||||
|
isAnswered:
|
||||||
|
isAnswered == const $CopyWithPlaceholder() || isAnswered == null
|
||||||
|
? _value.isAnswered
|
||||||
|
// ignore: cast_nullable_to_non_nullable
|
||||||
|
: isAnswered as bool,
|
||||||
|
isSkipped: isSkipped == const $CopyWithPlaceholder() || isSkipped == null
|
||||||
|
? _value.isSkipped
|
||||||
|
// ignore: cast_nullable_to_non_nullable
|
||||||
|
: isSkipped as bool,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
149
lib/features/tests/scroll_physics.dart
Normal file
149
lib/features/tests/scroll_physics.dart
Normal file
|
|
@ -0,0 +1,149 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
/// This [ScrollPhysics] allows you to block the movement in the horizontal axis.
|
||||||
|
/// You can block both right and left movements.
|
||||||
|
///
|
||||||
|
/// {@tool sample}
|
||||||
|
///
|
||||||
|
/// This sample shows a [HorizontalBlockedScrollPhysics] blocking left movement
|
||||||
|
///
|
||||||
|
/// ```dart
|
||||||
|
/// HorizontalBlockedScrollPhysics(blockLeftMovent: true, blockRightMovement: false);
|
||||||
|
/// ```
|
||||||
|
/// {@end-tool}
|
||||||
|
class HorizontalBlockedScrollPhysics extends ScrollPhysics {
|
||||||
|
/// If [true] it blocks the left movement.
|
||||||
|
final bool blockLeftMovement;
|
||||||
|
|
||||||
|
/// If [true] it blocks the right movement.
|
||||||
|
final bool blockRightMovement;
|
||||||
|
|
||||||
|
const HorizontalBlockedScrollPhysics({
|
||||||
|
ScrollPhysics? parent,
|
||||||
|
this.blockLeftMovement = false,
|
||||||
|
this.blockRightMovement = false,
|
||||||
|
}) : super(parent: parent);
|
||||||
|
|
||||||
|
@override
|
||||||
|
HorizontalBlockedScrollPhysics applyTo(ScrollPhysics? ancestor) {
|
||||||
|
return HorizontalBlockedScrollPhysics(
|
||||||
|
parent: buildParent(ancestor),
|
||||||
|
blockLeftMovement: blockLeftMovement,
|
||||||
|
blockRightMovement: blockRightMovement,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
double applyBoundaryConditions(ScrollMetrics position, double value) {
|
||||||
|
assert(() {
|
||||||
|
if (value == position.pixels) {
|
||||||
|
throw FlutterError(
|
||||||
|
'$runtimeType.applyBoundaryConditions() was called redundantly.\n'
|
||||||
|
'The proposed new position, $value, is exactly equal to the current position of the '
|
||||||
|
'given ${position.runtimeType}, ${position.pixels}.\n'
|
||||||
|
'The applyBoundaryConditions method should only be called when the value is '
|
||||||
|
'going to actually change the pixels, otherwise it is redundant.\n'
|
||||||
|
'The physics object in question was:\n'
|
||||||
|
' $this\n'
|
||||||
|
'The position object in question was:\n'
|
||||||
|
' $position\n');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}());
|
||||||
|
|
||||||
|
if (value < position.pixels &&
|
||||||
|
position.pixels <= position.minScrollExtent) {
|
||||||
|
return value - position.pixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (position.maxScrollExtent <= position.pixels &&
|
||||||
|
position.pixels < value) {
|
||||||
|
// overscroll
|
||||||
|
return value - position.pixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value < position.minScrollExtent &&
|
||||||
|
position.minScrollExtent < position.pixels) {
|
||||||
|
// hit top edge
|
||||||
|
return value - position.minScrollExtent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (position.pixels < position.maxScrollExtent &&
|
||||||
|
position.maxScrollExtent < value) {
|
||||||
|
// hit bottom edge
|
||||||
|
return value - position.maxScrollExtent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If true, movement goes to the left. If it's a swipe, it goes to the left.
|
||||||
|
var isMovingLeft = value > position.pixels;
|
||||||
|
var screenIndex = (value / position.viewportDimension).floor();
|
||||||
|
var pointInScreen = value - (screenIndex * position.viewportDimension);
|
||||||
|
// If true, the middle point of the screen is in the left side of the screen.
|
||||||
|
// This will be useful in order to not block some movements when in returning position.
|
||||||
|
var isPointInScreenLeftRange =
|
||||||
|
pointInScreen < (position.viewportDimension / 2);
|
||||||
|
var delta = value - position.pixels;
|
||||||
|
|
||||||
|
// We're moving left and we want to block.
|
||||||
|
if (isMovingLeft && blockLeftMovement && isPointInScreenLeftRange) {
|
||||||
|
if (pointInScreen.abs() < delta.abs()) {
|
||||||
|
// fix for strong movements
|
||||||
|
return pointInScreen;
|
||||||
|
}
|
||||||
|
return delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're moving right and we want to block.
|
||||||
|
if (!isMovingLeft && blockRightMovement && !isPointInScreenLeftRange) {
|
||||||
|
return delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.applyBoundaryConditions(position, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This [ScrollPhysics] blocks the left movement in the horizontal axis allowing only movements to the right.
|
||||||
|
///
|
||||||
|
/// {@tool sample}
|
||||||
|
///
|
||||||
|
/// This sample shows a [LeftBlockedScrollPhysics] blocking left movement
|
||||||
|
///
|
||||||
|
/// ```dart
|
||||||
|
/// LeftBlockedScrollPhysics();
|
||||||
|
/// ```
|
||||||
|
/// {@end-tool}
|
||||||
|
class LeftBlockedScrollPhysics extends HorizontalBlockedScrollPhysics {
|
||||||
|
const LeftBlockedScrollPhysics({
|
||||||
|
ScrollPhysics? parent,
|
||||||
|
}) : super(parent: parent, blockLeftMovement: true);
|
||||||
|
|
||||||
|
@override
|
||||||
|
LeftBlockedScrollPhysics applyTo(ScrollPhysics? ancestor) {
|
||||||
|
return LeftBlockedScrollPhysics(
|
||||||
|
parent: buildParent(ancestor),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This [ScrollPhysics] blocks the right movement in the horizontal axis allowing only movements to the left.
|
||||||
|
///
|
||||||
|
/// {@tool sample}
|
||||||
|
///
|
||||||
|
/// This sample shows a [RightBlockedScrollPhysics] blocking right movement
|
||||||
|
///
|
||||||
|
/// ```dart
|
||||||
|
/// RightBlockedScrollPhysics();
|
||||||
|
/// ```
|
||||||
|
/// {@end-tool}
|
||||||
|
class RightBlockedScrollPhysics extends HorizontalBlockedScrollPhysics {
|
||||||
|
const RightBlockedScrollPhysics({
|
||||||
|
ScrollPhysics? parent,
|
||||||
|
}) : super(parent: parent, blockRightMovement: true);
|
||||||
|
|
||||||
|
@override
|
||||||
|
RightBlockedScrollPhysics applyTo(ScrollPhysics? ancestor) {
|
||||||
|
return RightBlockedScrollPhysics(
|
||||||
|
parent: buildParent(ancestor),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -30,6 +30,11 @@ class TestManager extends ChangeNotifier {
|
||||||
return tests;
|
return tests;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void skip(int id) {
|
||||||
|
testHolder.skip(id);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
Stream<List<TestDto>> loadTestsStream({String? packId}) {
|
Stream<List<TestDto>> loadTestsStream({String? packId}) {
|
||||||
return Stream.periodic(
|
return Stream.periodic(
|
||||||
Duration(seconds: 60),
|
Duration(seconds: 60),
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
|
import 'dart:developer';
|
||||||
|
import 'dart:math' as math;
|
||||||
|
|
||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
import 'package:mnemo_cards/features/tests/question_states/test_question_state.dart';
|
||||||
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
|
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
|
||||||
import 'package:pie_chart/pie_chart.dart';
|
import 'package:pie_chart/pie_chart.dart';
|
||||||
|
|
||||||
import '../../di/locator.dart';
|
import '../../di/locator.dart';
|
||||||
|
import 'scroll_physics.dart';
|
||||||
import 'test_progress_widget.dart';
|
import 'test_progress_widget.dart';
|
||||||
import 'test_widgets/input_buttons_test.dart';
|
import 'test_widgets/input_buttons_test.dart';
|
||||||
import 'test_widgets/simple_test.dart';
|
import 'test_widgets/simple_test.dart';
|
||||||
|
|
@ -21,13 +25,39 @@ class TestPage extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _TestPageState extends State<TestPage> {
|
class _TestPageState extends State<TestPage> {
|
||||||
|
|
||||||
|
PageController get pageController => locator.testManager.pageController;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
locator.testManager.setActiveTest(widget.testId);
|
locator.testManager.setActiveTest(widget.testId);
|
||||||
locator.testManager.preloadImages(context);
|
locator.testManager.preloadImages(context);
|
||||||
|
pageController.addListener(_pageScrollListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
pageController.removeListener(_pageScrollListener);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _pageScrollListener() {
|
||||||
|
final index = pageController.page?.floor() ?? 0;
|
||||||
|
if (index > 0) {
|
||||||
|
final prevId =
|
||||||
|
locator.testManager.questions[index - 1].id!;
|
||||||
|
final state =
|
||||||
|
locator.testManager.getState(prevId);
|
||||||
|
if (state?.isAnswered == false) {
|
||||||
|
log('Question skipped');
|
||||||
|
locator.testManager.skip(prevId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int lastTestIndex = 0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
|
@ -60,17 +90,12 @@ class _TestPageState extends State<TestPage> {
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: PageView.builder(
|
child: PageView.builder(
|
||||||
physics: NeverScrollableScrollPhysics(), //ClampingScrollPhysics(),
|
physics: RightBlockedScrollPhysics(),
|
||||||
controller: locator.testManager.pageController,
|
controller: locator.testManager.pageController,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
if (index > 0) {
|
if (index > locator.testManager.questions.length) {
|
||||||
final state = locator.testManager.getState(
|
|
||||||
locator.testManager.questions[index - 1].id!,
|
|
||||||
);
|
|
||||||
if (state?.isCorrect != true) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (index == locator.testManager.questions.length) {
|
if (index == locator.testManager.questions.length) {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -152,6 +177,7 @@ class _TestPageState extends State<TestPage> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastTestIndex = math.max(index, lastTestIndex);
|
||||||
return switch (locator.testManager
|
return switch (locator.testManager
|
||||||
.getTest(index)
|
.getTest(index)
|
||||||
.questionType) {
|
.questionType) {
|
||||||
|
|
|
||||||
|
|
@ -35,10 +35,12 @@ class _TestProgressWidgetState extends State<TestProgressWidget> {
|
||||||
results.add(Result.correct);
|
results.add(Result.correct);
|
||||||
} else {
|
} else {
|
||||||
// results.add(Result.wrong);
|
// results.add(Result.wrong);
|
||||||
results.add(Result.skiped);
|
results.add(Result.not_visited);
|
||||||
}
|
}
|
||||||
|
} else if (state.isSkipped) {
|
||||||
|
results.add(Result.correct);
|
||||||
} else {
|
} else {
|
||||||
results.add(Result.skiped);
|
results.add(Result.not_visited);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,31 @@ class TestHolder {
|
||||||
|
|
||||||
TestHolder() : __state = _TestState({});
|
TestHolder() : __state = _TestState({});
|
||||||
|
|
||||||
|
void skip(int id) {
|
||||||
|
final state = testQuestionState(id);
|
||||||
|
switch (state?.testType) {
|
||||||
|
case TestQuestionType.simple:
|
||||||
|
state as SimpleTestState;
|
||||||
|
setQuestionState(id, state.copyWith(isSkipped: true));
|
||||||
|
break;
|
||||||
|
case TestQuestionType.input_buttons:
|
||||||
|
state as InputButtonsTestState;
|
||||||
|
setQuestionState(id, state.copyWith(isSkipped: true));
|
||||||
|
break;
|
||||||
|
case TestQuestionType.matrix:
|
||||||
|
// TODO: Handle this case.
|
||||||
|
break;
|
||||||
|
case TestQuestionType.match:
|
||||||
|
// TODO: Handle this case.
|
||||||
|
break;
|
||||||
|
case TestQuestionType.undefined:
|
||||||
|
// TODO: Handle this case.
|
||||||
|
case null:
|
||||||
|
// TODO: Handle this case.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void setQuestionState(int id, TestQuestionState state) => _state = _TestState(
|
void setQuestionState(int id, TestQuestionState state) => _state = _TestState(
|
||||||
Map.from(_state.questions..[id] = state),
|
Map.from(_state.questions..[id] = state),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,7 @@
|
||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:mnemo_cards/features/tests/test_manager.dart';
|
import 'package:mnemo_cards/features/tests/test_manager.dart';
|
||||||
import 'package:mnemo_cards/features/tests/test_widgets/common_test_question_widget.dart';
|
import 'package:mnemo_cards/features/tests/test_widgets/common_test_question_widget.dart';
|
||||||
import 'package:mnemo_cards/features/tests/test_widgets/test_image.dart';
|
|
||||||
import 'package:mnemo_cards/theme/themes.dart';
|
|
||||||
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
|
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
|
||||||
import '../../../di/locator.dart';
|
import '../../../di/locator.dart';
|
||||||
import '../question_states/input_buttons_test_state.dart';
|
import '../question_states/input_buttons_test_state.dart';
|
||||||
|
|
@ -39,8 +34,6 @@ class InputButtonsTestWidget extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = Theme.of(context).textTheme;
|
|
||||||
// final random = Random(state.seed);
|
|
||||||
final buttons = model.buttons; //..shuffle(random);
|
final buttons = model.buttons; //..shuffle(random);
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
|
@ -49,6 +42,7 @@ class InputButtonsTestWidget extends StatelessWidget {
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
child: CommonTestQuestionWidget(
|
child: CommonTestQuestionWidget(
|
||||||
text: model.text,
|
text: model.text,
|
||||||
image: model.image,
|
image: model.image,
|
||||||
|
|
@ -142,6 +136,7 @@ class InputButtonsTestWidget extends StatelessWidget {
|
||||||
];
|
];
|
||||||
|
|
||||||
return Expanded(
|
return Expanded(
|
||||||
|
flex: 3,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,6 @@ class UserManager {
|
||||||
ExternalIdType idType,
|
ExternalIdType idType,
|
||||||
String? name,
|
String? name,
|
||||||
) async {
|
) async {
|
||||||
await locator.packCacheManager.clearCache();
|
|
||||||
final (user, token) =
|
final (user, token) =
|
||||||
await _repository.createOrGetUser(externalId, idType, name);
|
await _repository.createOrGetUser(externalId, idType, name);
|
||||||
_setAuthToken(token);
|
_setAuthToken(token);
|
||||||
|
|
@ -75,14 +74,14 @@ class UserManager {
|
||||||
await _sharedPreferences!.remove('authToken');
|
await _sharedPreferences!.remove('authToken');
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _updateUser() async {
|
Future<UserDto?> _updateUser() async {
|
||||||
String? authToken = _sharedPreferences!.getString('authToken');
|
String? authToken = _sharedPreferences!.getString('authToken');
|
||||||
if (authToken != null) {
|
if (authToken != null) {
|
||||||
_setAuthToken(authToken);
|
_setAuthToken(authToken);
|
||||||
try {
|
try {
|
||||||
final fetchedUser = await _repository.getUser();
|
final fetchedUser = await _repository.getUser();
|
||||||
userStateHolder.setUser(fetchedUser!);
|
userStateHolder.setUser(fetchedUser!);
|
||||||
return;
|
return fetchedUser;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_clearAuthToken();
|
_clearAuthToken();
|
||||||
AppRouter.openAuthOrProfile();
|
AppRouter.openAuthOrProfile();
|
||||||
|
|
@ -95,12 +94,11 @@ class UserManager {
|
||||||
// final id = deviceInfo.id;
|
// final id = deviceInfo.id;
|
||||||
// await _createUser(id, ExternalIdType.device);
|
// await _createUser(id, ExternalIdType.device);
|
||||||
// }
|
// }
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
_sharedPreferences = await SharedPreferences.getInstance();
|
_sharedPreferences = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
//todo add ios
|
|
||||||
try {
|
try {
|
||||||
await _updateUser();
|
await _updateUser();
|
||||||
} on Object catch (e, s) {
|
} on Object catch (e, s) {
|
||||||
|
|
@ -120,12 +118,15 @@ class UserManager {
|
||||||
await _createUser(token!, ExternalIdType.google, name);
|
await _createUser(token!, ExternalIdType.google, name);
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
..add(userStateHolder.asNullableStream.listen((user) async {
|
..add(userStateHolder.asNullableStream
|
||||||
|
.distinct((a, b) => a?.id == b?.id)
|
||||||
|
.listen((user) async {
|
||||||
try {
|
try {
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
await _updateUser();
|
AppRouter.openAuthOrProfile();
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
|
AppRouter.closeAuthPage();
|
||||||
await locator.previewPackPoller.poll();
|
await locator.previewPackPoller.poll();
|
||||||
await locator.packManager.clearCache();
|
await locator.packManager.clearCache();
|
||||||
await locator.packUpdater.updateAvailablePacks();
|
await locator.packUpdater.updateAvailablePacks();
|
||||||
|
|
@ -144,19 +145,7 @@ class UserManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}));
|
||||||
..add(
|
|
||||||
userStateHolder.asNullableStream
|
|
||||||
.map((user) => user != null)
|
|
||||||
.distinct()
|
|
||||||
.listen((hasUser) {
|
|
||||||
if (hasUser) {
|
|
||||||
appRouter.replace(PageRouteInfo(HomePage.name));
|
|
||||||
} else {
|
|
||||||
AppRouter.openAuthOrProfile();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> dispose() async {
|
Future<void> dispose() async {
|
||||||
|
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
import 'package:auto_route/auto_route.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:mnemo_cards/di/locator.dart';
|
|
||||||
|
|
||||||
import '../widgets/packs_menu_widget.dart';
|
|
||||||
|
|
||||||
@RoutePage()
|
|
||||||
class ExplorePage extends StatelessWidget {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final theme = Theme.of(context).textTheme;
|
|
||||||
return Column(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
padding: const EdgeInsets.all(12.0),
|
|
||||||
child: Text(
|
|
||||||
'Blah - blah',
|
|
||||||
style: theme.headlineSmall,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: PacksMenuWidget(
|
|
||||||
locator.repository,
|
|
||||||
locator.packManager,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () async {
|
|
||||||
await locator.packManager.clearCache();
|
|
||||||
locator.testManager.clearStates();
|
|
||||||
await locator.packUpdater.updateAvailablePacks();
|
|
||||||
},
|
|
||||||
child: Text('Clear cache'),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () async {
|
|
||||||
locator.userManager.login();
|
|
||||||
},
|
|
||||||
child: Text('Login'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const ExplorePage();
|
|
||||||
}
|
|
||||||
|
|
@ -36,9 +36,6 @@ class AvailablePack extends StatelessWidget {
|
||||||
CardPackHeader.fromDto(cardPack),
|
CardPackHeader.fromDto(cardPack),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ListView(
|
child: ListView(
|
||||||
physics: BouncingScrollPhysics(
|
|
||||||
decelerationRate: ScrollDecelerationRate.fast,
|
|
||||||
parent: AlwaysScrollableScrollPhysics()),
|
|
||||||
controller: _mainScrollController,
|
controller: _mainScrollController,
|
||||||
children: [
|
children: [
|
||||||
StatefulBuilder(builder: (context, setState) {
|
StatefulBuilder(builder: (context, setState) {
|
||||||
|
|
|
||||||
|
|
@ -34,17 +34,17 @@ class _CardPackPage extends State<CardPackPage> {
|
||||||
),
|
),
|
||||||
initialData: null,
|
initialData: null,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (!snapshot.hasData) return SizedBox.shrink();
|
if (!snapshot.hasData) return Loading(widget.previewDto);
|
||||||
if (snapshot.requireData.responseType ==
|
if (snapshot.requireData.responseType ==
|
||||||
GetCardPackResponseType.loading)
|
GetCardPackResponseType.loading)
|
||||||
return Loading(widget.previewDto);
|
return Loading(widget.previewDto);
|
||||||
final response = snapshot.requireData;
|
final response = snapshot.requireData;
|
||||||
if (response is CardPackBuyDto)
|
if (response is CardPackBuyDto) {
|
||||||
return BuyPack(response);
|
return BuyPack(response);
|
||||||
else if (response is CardPackDto) {
|
} else if (response is CardPackDto) {
|
||||||
return AvailablePack(response);
|
return AvailablePack(response);
|
||||||
}
|
}
|
||||||
Analytics.error(
|
Analytics.crashlyticsError(
|
||||||
data: {'response': jsonEncode(snapshot.data?.toJson() ?? '{}')},
|
data: {'response': jsonEncode(snapshot.data?.toJson() ?? '{}')},
|
||||||
message: 'cant load pack ${widget.previewDto.id}',
|
message: 'cant load pack ${widget.previewDto.id}',
|
||||||
);
|
);
|
||||||
|
|
@ -54,7 +54,9 @@ class _CardPackPage extends State<CardPackPage> {
|
||||||
CardPackHeader.fromDto(null),
|
CardPackHeader.fromDto(null),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text('Error'),
|
child: Text(
|
||||||
|
'Ошибка, попробуйте еще раз или попробуйте обновить приложение',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -201,7 +201,8 @@ class FrontCard extends StatelessWidget {
|
||||||
ar = 16 / 9;
|
ar = 16 / 9;
|
||||||
}
|
}
|
||||||
final textScale = min(1.0, ar / targetTextAr) * alpha * alpha;
|
final textScale = min(1.0, ar / targetTextAr) * alpha * alpha;
|
||||||
final imageScale = 0.9 * min(1.0, pow(ar / targetImageAr, 1.5 * alpha));
|
final imageScale =
|
||||||
|
0.9 * min(1.0, pow(ar / targetImageAr, 1.5 * alpha));
|
||||||
return Material(
|
return Material(
|
||||||
borderOnForeground: false,
|
borderOnForeground: false,
|
||||||
type: MaterialType.transparency,
|
type: MaterialType.transparency,
|
||||||
|
|
@ -310,9 +311,11 @@ class FrontCard extends StatelessWidget {
|
||||||
syncDuration: Duration.zero,
|
syncDuration: Duration.zero,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (card.mnemo?.isNotEmpty == true)
|
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
|
if (card.mnemo?.trim().isNotEmpty != true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
final sp = await SharedPreferences.getInstance();
|
final sp = await SharedPreferences.getInstance();
|
||||||
if (sp.getBool('sound_on') != false) {
|
if (sp.getBool('sound_on') != false) {
|
||||||
FlutterTts flutterTts = FlutterTts();
|
FlutterTts flutterTts = FlutterTts();
|
||||||
|
|
@ -323,7 +326,7 @@ class FrontCard extends StatelessWidget {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: MnemoText(
|
child: MnemoText(
|
||||||
card.mnemo,
|
card.mnemo?.isNotEmpty == true ? card.mnemo : ' ',
|
||||||
textStyle: TextStyle(
|
textStyle: TextStyle(
|
||||||
fontSize: 20 * textScale,
|
fontSize: 20 * textScale,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
|
|
|
||||||
|
|
@ -983,6 +983,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.1.0"
|
version: "4.1.0"
|
||||||
|
reorderables:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: reorderables
|
||||||
|
sha256: "004a886e4878df1ee27321831c838bc1c976311f4ca6a74ce7d561e506540a77"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.6.0"
|
||||||
retrofit:
|
retrofit:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ dependencies:
|
||||||
firebase_analytics: ^10.10.7
|
firebase_analytics: ^10.10.7
|
||||||
in_app_purchase: ^3.2.0
|
in_app_purchase: ^3.2.0
|
||||||
dot_navigation_bar: ^1.0.2
|
dot_navigation_bar: ^1.0.2
|
||||||
|
reorderables: ^0.6.0
|
||||||
story: ^1.1.0
|
story: ^1.1.0
|
||||||
flutter_screenutil: ^5.9.0
|
flutter_screenutil: ^5.9.0
|
||||||
pie_chart: ^5.4.0
|
pie_chart: ^5.4.0
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue