forked from firka/firka
firka: refactor file structure
This commit is contained in:
@@ -3,21 +3,21 @@ import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:firka/helpers/api/model/all_lessons.dart';
|
||||
import 'package:firka/helpers/api/model/class_group.dart';
|
||||
import 'package:firka/helpers/api/model/homework.dart';
|
||||
import 'package:firka/helpers/api/model/timetable.dart';
|
||||
import 'package:firka/helpers/db/models/generic_cache_model.dart';
|
||||
import 'package:firka/helpers/db/models/timetable_cache_model.dart';
|
||||
import 'package:firka/api/model/all_lessons.dart';
|
||||
import 'package:firka/api/model/class_group.dart';
|
||||
import 'package:firka/api/model/homework.dart';
|
||||
import 'package:firka/api/model/timetable.dart';
|
||||
import 'package:firka/data/models/generic_cache_model.dart';
|
||||
import 'package:firka/data/models/timetable_cache_model.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:isar_community/isar.dart';
|
||||
|
||||
import '../../../main.dart';
|
||||
import '../../db/models/token_model.dart';
|
||||
import '../../db/util.dart';
|
||||
import '../../debug_helper.dart';
|
||||
import '../../active_account_helper.dart';
|
||||
import '../../watch_sync_helper.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/data/models/token_model.dart';
|
||||
import 'package:firka/data/util.dart';
|
||||
import 'package:firka/core/debug_helper.dart';
|
||||
import 'package:firka/services/active_account_helper.dart';
|
||||
import 'package:firka/services/watch_sync_helper.dart';
|
||||
import '../consts.dart';
|
||||
import '../exceptions/token.dart';
|
||||
import '../model/grade.dart';
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:firka/helpers/api/model/class_group.dart';
|
||||
import 'package:firka/helpers/api/model/homework.dart';
|
||||
import 'package:firka/helpers/api/model/notice_board.dart';
|
||||
import 'package:firka/helpers/api/model/omission.dart';
|
||||
import 'package:firka/helpers/api/model/test.dart';
|
||||
import 'package:firka/helpers/api/model/timetable.dart';
|
||||
import 'package:firka/api/model/class_group.dart';
|
||||
import 'package:firka/api/model/homework.dart';
|
||||
import 'package:firka/api/model/notice_board.dart';
|
||||
import 'package:firka/api/model/omission.dart';
|
||||
import 'package:firka/api/model/test.dart';
|
||||
import 'package:firka/api/model/timetable.dart';
|
||||
|
||||
import '../model/grade.dart';
|
||||
import '../model/student.dart';
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:firka/helpers/api/model/timetable.dart';
|
||||
import 'package:firka/api/model/timetable.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
|
||||
@@ -4,7 +4,7 @@ import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:firka/main.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
|
||||
class Constants {
|
||||
static String get clientId {
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:firka/helpers/api/model/generic.dart';
|
||||
import 'package:firka/api/model/generic.dart';
|
||||
|
||||
class ClassGroup {
|
||||
final String uid;
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:firka/helpers/api/model/generic.dart';
|
||||
import 'package:firka/helpers/api/model/subject.dart';
|
||||
import 'package:firka/api/model/generic.dart';
|
||||
import 'package:firka/api/model/subject.dart';
|
||||
|
||||
class Grade {
|
||||
final String uid;
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:firka/helpers/api/model/subject.dart';
|
||||
import 'package:firka/api/model/subject.dart';
|
||||
|
||||
import 'generic.dart';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:firka/helpers/api/model/generic.dart';
|
||||
import 'package:firka/api/model/generic.dart';
|
||||
|
||||
class NoticeBoardItem {
|
||||
final String uid;
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:firka/helpers/api/model/generic.dart';
|
||||
import 'package:firka/helpers/api/model/subject.dart';
|
||||
import 'package:firka/api/model/generic.dart';
|
||||
import 'package:firka/api/model/subject.dart';
|
||||
|
||||
class Omission {
|
||||
final String uid;
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:firka/helpers/api/model/guardian.dart';
|
||||
import 'package:firka/helpers/api/model/institution.dart';
|
||||
import 'package:firka/helpers/json_helper.dart';
|
||||
import 'package:firka/api/model/guardian.dart';
|
||||
import 'package:firka/api/model/institution.dart';
|
||||
import 'package:firka/core/json_helper.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class Student {
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:firka/helpers/api/model/subject.dart';
|
||||
import 'package:firka/api/model/subject.dart';
|
||||
|
||||
import 'generic.dart';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:firka/helpers/api/model/generic.dart';
|
||||
import 'package:firka/helpers/api/model/subject.dart';
|
||||
import 'package:firka/api/model/generic.dart';
|
||||
import 'package:firka/api/model/subject.dart';
|
||||
|
||||
class Lesson {
|
||||
final String uid;
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:firka/helpers/api/exceptions/token.dart';
|
||||
import 'package:firka/helpers/api/resp/token_grant.dart';
|
||||
import 'package:firka/helpers/db/models/token_model.dart';
|
||||
import 'package:firka/api/exceptions/token.dart';
|
||||
import 'package:firka/api/resp/token_grant.dart';
|
||||
import 'package:firka/data/models/token_model.dart';
|
||||
|
||||
import '../../main.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'consts.dart';
|
||||
|
||||
Future<TokenGrantResponse> getAccessToken(String code) async {
|
||||
66
firka/lib/app/app_state.dart
Normal file
66
firka/lib/app/app_state.dart
Normal file
@@ -0,0 +1,66 @@
|
||||
import 'dart:typed_data';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:firka/api/client/kreta_client.dart';
|
||||
import 'package:firka/data/models/token_model.dart';
|
||||
import 'package:firka/core/state/update_notifier.dart';
|
||||
import 'package:firka/core/settings.dart';
|
||||
import 'package:firka/l10n/app_localizations.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:isar_community/isar.dart';
|
||||
import 'dart:io';
|
||||
|
||||
late final Logger logger;
|
||||
|
||||
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||
late AppInitialization initData;
|
||||
bool initDone = false;
|
||||
|
||||
final dio = Dio();
|
||||
final isBeta = true;
|
||||
|
||||
final ValueNotifier<bool> isLightMode = ValueNotifier<bool>(true);
|
||||
final UpdateNotifier globalUpdate = UpdateNotifier();
|
||||
|
||||
class DeviceInfo {
|
||||
String model;
|
||||
|
||||
String versionRelease;
|
||||
String versionSdkInt;
|
||||
|
||||
DeviceInfo(this.model, this.versionRelease, this.versionSdkInt);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "DeviceInfo(model = \"$model\", versionRelease = \"$versionRelease\""
|
||||
", versionSdkInt = \"$versionSdkInt\"";
|
||||
}
|
||||
}
|
||||
|
||||
class AppInitialization {
|
||||
final Isar isar;
|
||||
final Directory appDir;
|
||||
final PackageInfo packageInfo;
|
||||
final DeviceInfo devInfo;
|
||||
late KretaClient client;
|
||||
List<TokenModel> tokens;
|
||||
bool hasWatchListener = false;
|
||||
Uint8List? profilePicture;
|
||||
SettingsStore settings;
|
||||
UpdateNotifier settingsUpdateNotifier = UpdateNotifier();
|
||||
UpdateNotifier profilePictureUpdateNotifier = UpdateNotifier();
|
||||
AppLocalizations l10n;
|
||||
final GlobalKey<NavigatorState> navigatorKey;
|
||||
|
||||
AppInitialization({
|
||||
required this.isar,
|
||||
required this.appDir,
|
||||
required this.devInfo,
|
||||
required this.packageInfo,
|
||||
required this.tokens,
|
||||
required this.settings,
|
||||
required this.l10n,
|
||||
required this.navigatorKey,
|
||||
});
|
||||
}
|
||||
399
firka/lib/app/initialization.dart
Normal file
399
firka/lib/app/initialization.dart
Normal file
@@ -0,0 +1,399 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:ui' as ui;
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/services/active_account_helper.dart';
|
||||
import 'package:firka/api/client/kreta_client.dart';
|
||||
import 'package:firka/data/models/app_settings_model.dart';
|
||||
import 'package:firka/data/models/generic_cache_model.dart';
|
||||
import 'package:firka/data/models/homework_cache_model.dart';
|
||||
import 'package:firka/data/models/timetable_cache_model.dart';
|
||||
import 'package:firka/data/models/token_model.dart';
|
||||
import 'package:firka/services/live_activity_service.dart';
|
||||
import 'package:firka/core/settings.dart';
|
||||
import 'package:firka/services/watch_sync_helper.dart';
|
||||
import 'package:firka/l10n/app_localizations_de.dart';
|
||||
import 'package:firka/l10n/app_localizations_en.dart';
|
||||
import 'package:firka/l10n/app_localizations_hu.dart';
|
||||
import 'package:firka/core/swear_generator.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:isar_community/isar.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
Isar? isarInit;
|
||||
|
||||
Future<Isar> initDB() async {
|
||||
if (isarInit != null) return isarInit!;
|
||||
final dir = await getApplicationDocumentsDirectory();
|
||||
|
||||
isarInit = await Isar.open(
|
||||
[
|
||||
TokenModelSchema,
|
||||
GenericCacheModelSchema,
|
||||
TimetableCacheModelSchema,
|
||||
HomeworkCacheModelSchema,
|
||||
AppSettingsModelSchema,
|
||||
HomeworkDoneModelSchema,
|
||||
],
|
||||
inspector: true,
|
||||
directory: dir.path,
|
||||
);
|
||||
|
||||
return isarInit!;
|
||||
}
|
||||
|
||||
Future<void> initLang(AppInitialization data) async {
|
||||
String? languageCode;
|
||||
|
||||
switch ((data.settings.group("settings").subGroup("application")["language"]
|
||||
as SettingsItemsRadio)
|
||||
.activeIndex) {
|
||||
case 1: // hu
|
||||
data.l10n = AppLocalizationsHu();
|
||||
languageCode = 'hu';
|
||||
break;
|
||||
case 2: // en
|
||||
data.l10n = AppLocalizationsEn();
|
||||
languageCode = 'en';
|
||||
break;
|
||||
case 3: // de
|
||||
data.l10n = AppLocalizationsDe();
|
||||
languageCode = 'de';
|
||||
break;
|
||||
default: // auto
|
||||
switch (ui.PlatformDispatcher.instance.locale.languageCode) {
|
||||
case 'hu':
|
||||
data.l10n = AppLocalizationsHu();
|
||||
languageCode = 'hu';
|
||||
break;
|
||||
case 'en':
|
||||
data.l10n = AppLocalizationsEn();
|
||||
languageCode = 'en';
|
||||
break;
|
||||
case 'de':
|
||||
data.l10n = AppLocalizationsDe();
|
||||
languageCode = 'de';
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (languageCode != null && Platform.isIOS) {
|
||||
try {
|
||||
await LiveActivityService.updateLanguagePreference(languageCode);
|
||||
} catch (e) {
|
||||
logger.warning('Failed to update language preference on backend: $e');
|
||||
}
|
||||
|
||||
try {
|
||||
await WatchSyncHelper.sendLanguageToWatch();
|
||||
} catch (e) {
|
||||
logger.warning('Failed to send language to Watch: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void initTheme(AppInitialization data) {
|
||||
final brightness =
|
||||
SchedulerBinding.instance.platformDispatcher.platformBrightness;
|
||||
|
||||
switch ((data.settings.group("settings").subGroup("customization")["theme"]
|
||||
as SettingsItemsRadio)
|
||||
.activeIndex) {
|
||||
case 1:
|
||||
appStyle = lightStyle;
|
||||
isLightMode.value = true;
|
||||
break;
|
||||
case 2:
|
||||
appStyle = darkStyle;
|
||||
isLightMode.value = false;
|
||||
break;
|
||||
default:
|
||||
if (brightness == Brightness.dark) {
|
||||
appStyle = darkStyle;
|
||||
isLightMode.value = false;
|
||||
} else {
|
||||
appStyle = lightStyle;
|
||||
isLightMode.value = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _initData(AppInitialization init) async {
|
||||
await init.settings.load(init.isar.appSettingsModels);
|
||||
await initLang(init);
|
||||
initTheme(init);
|
||||
init.settings = SettingsStore(init.l10n);
|
||||
await init.settings.load(init.isar.appSettingsModels);
|
||||
|
||||
var dispatcher = SchedulerBinding.instance.platformDispatcher;
|
||||
|
||||
dispatcher.onPlatformBrightnessChanged = () {
|
||||
globalUpdate.update();
|
||||
initTheme(init);
|
||||
};
|
||||
|
||||
dispatcher.onLocaleChanged = () {
|
||||
final languageSetting =
|
||||
init.settings.group("settings").subGroup("application")["language"]
|
||||
as SettingsItemsRadio;
|
||||
final isAutoLanguage = languageSetting.activeIndex == 0;
|
||||
if (!isAutoLanguage) {
|
||||
return;
|
||||
}
|
||||
|
||||
final previousLocale = init.l10n.localeName;
|
||||
unawaited(() async {
|
||||
await initLang(init);
|
||||
final nextLocale = init.l10n.localeName;
|
||||
if (previousLocale != nextLocale) {
|
||||
logger.info(
|
||||
"[Init] System locale changed in auto mode: $previousLocale -> $nextLocale",
|
||||
);
|
||||
}
|
||||
globalUpdate.update();
|
||||
}());
|
||||
};
|
||||
|
||||
resetOldTimeTableCache(init.isar);
|
||||
resetOldHomeworkCache(init.isar);
|
||||
|
||||
var didRunFreshInstallCleanup = false;
|
||||
if (Platform.isIOS) {
|
||||
try {
|
||||
didRunFreshInstallCleanup =
|
||||
await WatchSyncHelper.runFreshInstallCleanupIfNeeded(isar: init.isar);
|
||||
if (didRunFreshInstallCleanup) {
|
||||
logger.info(
|
||||
'[Init] Fresh-install cleanup completed; skipping startup iCloud recovery on this launch',
|
||||
);
|
||||
} else {
|
||||
await WatchSyncHelper.checkAndRecoverFromiCloud(
|
||||
isar: init.isar,
|
||||
tokens: init.tokens,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
logger.warning('[Init] iCloud bootstrap/recovery failed: $e');
|
||||
}
|
||||
}
|
||||
|
||||
final allTokens = await init.isar.tokenModels.where().findAll();
|
||||
init.tokens = allTokens;
|
||||
|
||||
if (allTokens.isNotEmpty) {
|
||||
final token = pickActiveToken(tokens: allTokens, settings: init.settings);
|
||||
if (token == null) {
|
||||
logger.warning(
|
||||
"[Init] Tokens disappeared during initialization; skipping client setup",
|
||||
);
|
||||
return;
|
||||
}
|
||||
logger.fine("Initializing kréta client as: ${token.studentId}");
|
||||
init.client = KretaClient(token, init.isar);
|
||||
|
||||
if (Platform.isIOS) {
|
||||
final expiryDate = token.expiryDate;
|
||||
if (expiryDate != null && expiryDate.isAfter(DateTime.now())) {
|
||||
KretaClient.clearReauthFlag();
|
||||
}
|
||||
|
||||
unawaited(() async {
|
||||
try {
|
||||
await WatchSyncHelper.saveTokenToiCloud(token);
|
||||
} catch (e) {
|
||||
logger.warning('[Init] Failed to sync active token to iCloud: $e');
|
||||
}
|
||||
|
||||
try {
|
||||
await WatchSyncHelper.sendTokenModelToWatch(token);
|
||||
} catch (e) {
|
||||
logger.warning('[Init] Failed to sync active token to Watch: $e');
|
||||
}
|
||||
}());
|
||||
}
|
||||
}
|
||||
|
||||
final dataDir = await getApplicationDocumentsDirectory();
|
||||
var pfpFile = File(p.join(dataDir.path, "profile.webp"));
|
||||
|
||||
if (await pfpFile.exists()) {
|
||||
init.profilePicture = await pfpFile.readAsBytes();
|
||||
}
|
||||
}
|
||||
|
||||
Future<AppInitialization> initializeApp() async {
|
||||
if (initDone) {
|
||||
await _initData(initData);
|
||||
return initData;
|
||||
}
|
||||
final isar = await initDB();
|
||||
final tokens = await isar.tokenModels.where().findAll();
|
||||
|
||||
logger.finest('Token count: ${tokens.length}');
|
||||
|
||||
var devInfoFetched = false;
|
||||
var devInfo = DeviceInfo("SM-A705FN", "11", "30");
|
||||
|
||||
try {
|
||||
if (Platform.isAndroid) {
|
||||
const channel = MethodChannel("firka.app/main");
|
||||
final rawInfo = ((await channel.invokeMethod("get_info")) as String)
|
||||
.split(";");
|
||||
|
||||
devInfo = DeviceInfo(rawInfo[0], rawInfo[1], rawInfo[2]);
|
||||
devInfoFetched = true;
|
||||
}
|
||||
} catch (e) {
|
||||
if (e is Error) {
|
||||
logger.shout("Error in initializeApp()", e.toString(), e.stackTrace);
|
||||
} else {
|
||||
logger.shout("Error in initializeApp()", e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
logger.fine("Fetched device info: ${devInfoFetched ? "yes" : "no"}");
|
||||
logger.fine("Using device info: ${devInfo.toString()}");
|
||||
|
||||
var init = AppInitialization(
|
||||
isar: isar,
|
||||
appDir: await getApplicationDocumentsDirectory(),
|
||||
devInfo: devInfo,
|
||||
packageInfo: await PackageInfo.fromPlatform(),
|
||||
tokens: tokens,
|
||||
settings: SettingsStore(AppLocalizationsHu()),
|
||||
l10n: AppLocalizationsHu(),
|
||||
navigatorKey: navigatorKey,
|
||||
);
|
||||
|
||||
if (Platform.isIOS) {
|
||||
try {
|
||||
await LiveActivityService.initialize().timeout(
|
||||
const Duration(seconds: 8),
|
||||
);
|
||||
} on TimeoutException catch (e, st) {
|
||||
logger.warning('LiveActivity init timed out: $e', e, st);
|
||||
} catch (e, st) {
|
||||
logger.severe('Failed to initialize LiveActivity: $e', e, st);
|
||||
}
|
||||
}
|
||||
|
||||
await _initData(init);
|
||||
|
||||
init.settingsUpdateNotifier.addListener(() {
|
||||
logger.finest("Settings updated");
|
||||
});
|
||||
|
||||
return init;
|
||||
}
|
||||
|
||||
Future<void> setupLogging() async {
|
||||
final jwtPattern = RegExp(
|
||||
r'([A-Za-z0-9-_]+)\.([A-Za-z0-9-_]+)\.([A-Za-z0-9-_]+)',
|
||||
);
|
||||
final omPattern = RegExp(r'(\d{3})(\d{6})([A-Za-z0-9]?)');
|
||||
final refreshTokenPattern = RegExp(
|
||||
r'"(?=.{21,}$)([A-Z0-9]+-[A-Z0-9_\-.~+]*)"',
|
||||
);
|
||||
|
||||
final docs = await getApplicationDocumentsDirectory();
|
||||
|
||||
Future<void> deleteOldLogFiles() async {
|
||||
final docs = await getApplicationDocumentsDirectory();
|
||||
final dir = Directory(docs.path);
|
||||
if (!dir.existsSync()) return;
|
||||
|
||||
final now = DateTime.now();
|
||||
final cutoff = now.subtract(Duration(days: 30));
|
||||
|
||||
final logFileRegex = RegExp(r'^(\d{4})_(\d{2})_(\d{2})\.log$');
|
||||
|
||||
for (final entity in dir.listSync()) {
|
||||
if (entity is! File) continue;
|
||||
final name = entity.uri.pathSegments.last;
|
||||
final m = logFileRegex.firstMatch(name);
|
||||
if (m == null) continue;
|
||||
|
||||
try {
|
||||
final y = int.parse(m.group(1)!);
|
||||
final mo = int.parse(m.group(2)!);
|
||||
final d = int.parse(m.group(3)!);
|
||||
final fileDate = DateTime(y, mo, d);
|
||||
if (fileDate.isBefore(
|
||||
DateTime(cutoff.year, cutoff.month, cutoff.day),
|
||||
)) {
|
||||
logger.info("Removing old log file: $name");
|
||||
await entity.delete();
|
||||
}
|
||||
} catch (_) {
|
||||
// ignore parse/delete errors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String logFilePathForDate(DateTime dt) {
|
||||
final fileName = "${DateFormat("yyyy_MM_dd").format(dt)}.log";
|
||||
return Directory(docs.path).uri.resolve(fileName).toFilePath();
|
||||
}
|
||||
|
||||
File fileForDate(DateTime dt) {
|
||||
final path = logFilePathForDate(dt);
|
||||
final file = File(path);
|
||||
if (!file.existsSync()) file.createSync(recursive: true);
|
||||
return file;
|
||||
}
|
||||
|
||||
String censorLog(String msg) {
|
||||
return msg
|
||||
.replaceAll(jwtPattern, '***')
|
||||
.replaceAllMapped(omPattern, (match) {
|
||||
return "${match.group(1)}******${match.group(3)}";
|
||||
})
|
||||
.replaceAll(refreshTokenPattern, '"***"');
|
||||
}
|
||||
|
||||
hierarchicalLoggingEnabled = true;
|
||||
logger.level = Level.ALL;
|
||||
|
||||
DateTime currentDate = DateTime.now();
|
||||
IOSink sink = fileForDate(currentDate).openWrite(mode: FileMode.append);
|
||||
|
||||
logger.onRecord.listen((record) {
|
||||
final now = DateTime.now();
|
||||
if (now.year != currentDate.year ||
|
||||
now.month != currentDate.month ||
|
||||
now.day != currentDate.day) {
|
||||
sink.flush();
|
||||
sink.close();
|
||||
currentDate = now;
|
||||
sink = fileForDate(currentDate).openWrite(mode: FileMode.append);
|
||||
}
|
||||
|
||||
final censored = censorLog(record.message);
|
||||
final timestamp = DateFormat('yyyy-MM-dd HH:mm:ss.SSS').format(now);
|
||||
final level = record.level.name;
|
||||
final line = '[$timestamp] [$level] [$censored]';
|
||||
sink.writeln(line);
|
||||
|
||||
debugPrint(
|
||||
"[Firka] [${record.level.name}] ${kDebugMode ? record.message : censored}",
|
||||
);
|
||||
});
|
||||
|
||||
unawaited(deleteOldLogFiles());
|
||||
|
||||
try {
|
||||
logger.finest('loading dirty words');
|
||||
await loadDirtyWords();
|
||||
logger.finest('loaded dirty words');
|
||||
} catch (e, st) {
|
||||
logger.severe('Failed to load dirty words: $e', e, st);
|
||||
}
|
||||
}
|
||||
180
firka/lib/app/initialization_screen.dart
Normal file
180
firka/lib/app/initialization_screen.dart
Normal file
@@ -0,0 +1,180 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/app/initialization.dart';
|
||||
import 'package:firka/core/firka_bundle.dart';
|
||||
import 'package:firka/services/watch_sync_helper.dart';
|
||||
import 'package:firka/l10n/app_localizations.dart';
|
||||
import 'package:firka/ui/phone/screens/debug/debug_screen.dart';
|
||||
import 'package:firka/ui/phone/screens/home/home_screen.dart';
|
||||
import 'package:firka/ui/phone/screens/login/login_screen.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
|
||||
class InitializationScreen extends StatelessWidget {
|
||||
InitializationScreen({super.key});
|
||||
|
||||
final Future<AppInitialization> _init = initializeApp().timeout(
|
||||
const Duration(seconds: 20),
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder<AppInitialization>(
|
||||
future: _init,
|
||||
builder: (context, snapshot) {
|
||||
// Check if initialization is complete
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
if (snapshot.hasError) {
|
||||
logger.shout(
|
||||
"Error in InitializationScreen",
|
||||
snapshot.error.toString(),
|
||||
snapshot.stackTrace,
|
||||
);
|
||||
|
||||
FlutterNativeSplash.remove();
|
||||
|
||||
// Handle initialization error
|
||||
return MaterialApp(
|
||||
key: ValueKey('errorPage'),
|
||||
home: DefaultAssetBundle(
|
||||
bundle: FirkaBundle(),
|
||||
child: Scaffold(
|
||||
body: Center(
|
||||
child: Text(
|
||||
'Error initializing app: ${snapshot.error}',
|
||||
style: TextStyle(color: Colors.red),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Initialization successful, determine which screen to show
|
||||
Widget screen;
|
||||
|
||||
assert(snapshot.data != null);
|
||||
initData = snapshot.data!;
|
||||
initDone = true;
|
||||
|
||||
FlutterNativeSplash.remove();
|
||||
|
||||
WatchSyncHelper.initialize();
|
||||
if (Platform.isIOS) {
|
||||
unawaited(() async {
|
||||
try {
|
||||
await WatchSyncHelper.sendLanguageToWatch();
|
||||
} catch (e) {
|
||||
logger.warning(
|
||||
'[Init] Failed to publish language to Watch after sync init: $e',
|
||||
);
|
||||
}
|
||||
}());
|
||||
}
|
||||
|
||||
if (!initData.hasWatchListener) {
|
||||
initData.hasWatchListener = true;
|
||||
|
||||
WatchSyncHelper.onWatchMessage = (msg) {
|
||||
logger.finest("WatchOS IPC [Watch -> Phone]: ${msg["id"]}");
|
||||
|
||||
switch (msg["id"]) {
|
||||
case "ping":
|
||||
if (initData.tokens.isNotEmpty) {
|
||||
logger.finest("WatchOS IPC [Phone -> Watch]: pong");
|
||||
const watchChannel = MethodChannel('app.firka/watch_sync');
|
||||
watchChannel.invokeMethod('sendMessageToWatch', {
|
||||
"id": "pong",
|
||||
});
|
||||
navigatorKey.currentState?.push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => HomeScreen(
|
||||
initData,
|
||||
true,
|
||||
model: msg["model"] as String? ?? "unknown",
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (snapshot.data!.tokens.isEmpty) {
|
||||
screen = LoginScreen(initData, key: ValueKey('loginScreen'));
|
||||
} else {
|
||||
screen = HomeScreen(initData, false, key: ValueKey('homeScreen'));
|
||||
}
|
||||
|
||||
return MaterialApp(
|
||||
title: 'Firka',
|
||||
key: ValueKey('firkaApp'),
|
||||
navigatorKey: navigatorKey,
|
||||
theme: ThemeData(
|
||||
primarySwatch: Colors.lightGreen,
|
||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||
),
|
||||
localizationsDelegates: [
|
||||
AppLocalizations.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
home: DefaultAssetBundle(
|
||||
bundle: FirkaBundle(),
|
||||
child: ValueListenableBuilder<bool>(
|
||||
valueListenable: isLightMode,
|
||||
builder: (context, isLight, _) {
|
||||
final overlay = SystemUiOverlayStyle(
|
||||
statusBarColor: Colors.transparent,
|
||||
statusBarIconBrightness: isLight
|
||||
? Brightness.dark
|
||||
: Brightness.light,
|
||||
statusBarBrightness: isLight
|
||||
? Brightness.light
|
||||
: Brightness.dark,
|
||||
systemStatusBarContrastEnforced: false,
|
||||
);
|
||||
|
||||
SystemChrome.setSystemUIOverlayStyle(overlay);
|
||||
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: overlay,
|
||||
child: screen,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
routes: {
|
||||
'/login': (context) => DefaultAssetBundle(
|
||||
bundle: FirkaBundle(),
|
||||
child: LoginScreen(initData, key: ValueKey('loginScreen')),
|
||||
),
|
||||
'/home': (context) => DefaultAssetBundle(
|
||||
bundle: FirkaBundle(),
|
||||
child: HomeScreen(initData, false, key: ValueKey('homeScreen')),
|
||||
),
|
||||
'/debug': (context) => DefaultAssetBundle(
|
||||
bundle: FirkaBundle(),
|
||||
child: DebugScreen(initData, key: ValueKey('debugScreen')),
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return MaterialApp(
|
||||
home: DefaultAssetBundle(
|
||||
bundle: FirkaBundle(),
|
||||
child: Scaffold(
|
||||
backgroundColor: const Color(0xFF7CA120),
|
||||
body: Container(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:firka/helpers/api/model/grade.dart';
|
||||
import 'package:firka/api/model/grade.dart';
|
||||
|
||||
double calculateAverage(List<Grade> sortedGrades) {
|
||||
double totalWeight = 0.0;
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../l10n/app_localizations.dart';
|
||||
import 'api/model/timetable.dart';
|
||||
import 'debug_helper.dart';
|
||||
import 'package:firka/api/model/timetable.dart';
|
||||
import 'package:firka/core/debug_helper.dart';
|
||||
import 'package:firka/l10n/app_localizations.dart';
|
||||
|
||||
extension TimetableExtension on Iterable<Lesson> {
|
||||
List<Lesson> getAllSeqs(Lesson reference) {
|
||||
@@ -3,7 +3,7 @@ import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:brotli/brotli.dart';
|
||||
import 'package:firka/main.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class FirkaBundle extends CachingAssetBundle {
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:firka/main.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:firka/main.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:image/image.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
@@ -2,16 +2,19 @@ import 'dart:collection';
|
||||
import 'dart:core';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:firka/helpers/db/models/app_settings_model.dart';
|
||||
import 'package:firka/helpers/live_activity_service.dart';
|
||||
import 'package:firka/data/models/app_settings_model.dart';
|
||||
import 'package:firka/services/live_activity_service.dart';
|
||||
import 'package:firka/l10n/app_localizations.dart';
|
||||
import 'package:firka/ui/widget/firka_icon.dart';
|
||||
import 'package:firka/ui/shared/firka_icon.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:isar_community/isar.dart';
|
||||
import 'package:majesticons_flutter/majesticons_flutter.dart';
|
||||
|
||||
import '../main.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/app/initialization.dart';
|
||||
import 'package:firka/app/initialization_screen.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
const bellRing = 1001;
|
||||
const rounding1 = 1002;
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:firka/main.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
abstract class FirkaState<T extends StatefulWidget> extends State<T> {
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:firka/helpers/api/model/grade.dart';
|
||||
import 'package:firka/helpers/api/model/timetable.dart';
|
||||
import 'package:firka/api/model/grade.dart';
|
||||
import 'package:firka/api/model/timetable.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:isar_community/isar.dart';
|
||||
|
||||
import '../../debug_helper.dart';
|
||||
import '../util.dart';
|
||||
import 'package:firka/core/debug_helper.dart';
|
||||
import 'package:firka/data/util.dart';
|
||||
|
||||
part 'homework_cache_model.g.dart';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:isar_community/isar.dart';
|
||||
|
||||
import '../../debug_helper.dart';
|
||||
import '../util.dart';
|
||||
import 'package:firka/core/debug_helper.dart';
|
||||
import 'package:firka/data/util.dart';
|
||||
|
||||
part 'timetable_cache_model.g.dart';
|
||||
|
||||
@@ -2,12 +2,11 @@ import 'dart:convert';
|
||||
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart';
|
||||
import 'package:firka/helpers/api/resp/token_grant.dart';
|
||||
import 'package:firka/helpers/extensions.dart';
|
||||
import 'package:firka/api/resp/token_grant.dart';
|
||||
import 'package:firka/core/debug_helper.dart';
|
||||
import 'package:firka/core/extensions.dart';
|
||||
import 'package:isar_community/isar.dart';
|
||||
|
||||
import '../../debug_helper.dart';
|
||||
|
||||
part 'token_model.g.dart';
|
||||
|
||||
@collection
|
||||
@@ -3,7 +3,7 @@ import 'dart:math';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:isar_community/isar.dart';
|
||||
|
||||
import '../debug_helper.dart';
|
||||
import 'package:firka/core/debug_helper.dart';
|
||||
|
||||
class DatedCacheEntry {
|
||||
Id? cacheKey;
|
||||
@@ -1,18 +1,18 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:firka/helpers/api/client/kreta_client.dart';
|
||||
import 'package:firka/helpers/api/model/grade.dart';
|
||||
import 'package:firka/helpers/api/model/timetable.dart';
|
||||
import 'package:firka/helpers/db/ios_widget_helper.dart';
|
||||
import 'package:firka/helpers/debug_helper.dart';
|
||||
import 'package:firka/helpers/settings.dart';
|
||||
import 'package:firka/main.dart';
|
||||
import 'package:firka/api/client/kreta_client.dart';
|
||||
import 'package:firka/api/model/grade.dart';
|
||||
import 'package:firka/api/model/timetable.dart';
|
||||
import 'package:firka/core/debug_helper.dart';
|
||||
import 'package:firka/data/ios_widget_helper.dart';
|
||||
import 'package:firka/core/settings.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
import '../../ui/model/style.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
|
||||
class WidgetCacheHelper {
|
||||
static Map<String, dynamic> toJson(FirkaStyle style, List<Lesson> timetable) {
|
||||
@@ -1,843 +0,0 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'app_settings_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// IsarCollectionGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types
|
||||
|
||||
extension GetAppSettingsModelCollection on Isar {
|
||||
IsarCollection<AppSettingsModel> get appSettingsModels => this.collection();
|
||||
}
|
||||
|
||||
const AppSettingsModelSchema = CollectionSchema(
|
||||
name: r'AppSettingsModel',
|
||||
id: -638838212012723081,
|
||||
properties: {
|
||||
r'valueBool': PropertySchema(
|
||||
id: 0,
|
||||
name: r'valueBool',
|
||||
type: IsarType.bool,
|
||||
),
|
||||
r'valueDouble': PropertySchema(
|
||||
id: 1,
|
||||
name: r'valueDouble',
|
||||
type: IsarType.double,
|
||||
),
|
||||
r'valueIndex': PropertySchema(
|
||||
id: 2,
|
||||
name: r'valueIndex',
|
||||
type: IsarType.long,
|
||||
),
|
||||
r'valueString': PropertySchema(
|
||||
id: 3,
|
||||
name: r'valueString',
|
||||
type: IsarType.string,
|
||||
),
|
||||
},
|
||||
|
||||
estimateSize: _appSettingsModelEstimateSize,
|
||||
serialize: _appSettingsModelSerialize,
|
||||
deserialize: _appSettingsModelDeserialize,
|
||||
deserializeProp: _appSettingsModelDeserializeProp,
|
||||
idName: r'id',
|
||||
indexes: {},
|
||||
links: {},
|
||||
embeddedSchemas: {},
|
||||
|
||||
getId: _appSettingsModelGetId,
|
||||
getLinks: _appSettingsModelGetLinks,
|
||||
attach: _appSettingsModelAttach,
|
||||
version: '3.3.0',
|
||||
);
|
||||
|
||||
int _appSettingsModelEstimateSize(
|
||||
AppSettingsModel object,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
var bytesCount = offsets.last;
|
||||
{
|
||||
final value = object.valueString;
|
||||
if (value != null) {
|
||||
bytesCount += 3 + value.length * 3;
|
||||
}
|
||||
}
|
||||
return bytesCount;
|
||||
}
|
||||
|
||||
void _appSettingsModelSerialize(
|
||||
AppSettingsModel object,
|
||||
IsarWriter writer,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
writer.writeBool(offsets[0], object.valueBool);
|
||||
writer.writeDouble(offsets[1], object.valueDouble);
|
||||
writer.writeLong(offsets[2], object.valueIndex);
|
||||
writer.writeString(offsets[3], object.valueString);
|
||||
}
|
||||
|
||||
AppSettingsModel _appSettingsModelDeserialize(
|
||||
Id id,
|
||||
IsarReader reader,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
final object = AppSettingsModel();
|
||||
object.id = id;
|
||||
object.valueBool = reader.readBoolOrNull(offsets[0]);
|
||||
object.valueDouble = reader.readDoubleOrNull(offsets[1]);
|
||||
object.valueIndex = reader.readLongOrNull(offsets[2]);
|
||||
object.valueString = reader.readStringOrNull(offsets[3]);
|
||||
return object;
|
||||
}
|
||||
|
||||
P _appSettingsModelDeserializeProp<P>(
|
||||
IsarReader reader,
|
||||
int propertyId,
|
||||
int offset,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
switch (propertyId) {
|
||||
case 0:
|
||||
return (reader.readBoolOrNull(offset)) as P;
|
||||
case 1:
|
||||
return (reader.readDoubleOrNull(offset)) as P;
|
||||
case 2:
|
||||
return (reader.readLongOrNull(offset)) as P;
|
||||
case 3:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
default:
|
||||
throw IsarError('Unknown property with id $propertyId');
|
||||
}
|
||||
}
|
||||
|
||||
Id _appSettingsModelGetId(AppSettingsModel object) {
|
||||
return object.id ?? Isar.autoIncrement;
|
||||
}
|
||||
|
||||
List<IsarLinkBase<dynamic>> _appSettingsModelGetLinks(AppSettingsModel object) {
|
||||
return [];
|
||||
}
|
||||
|
||||
void _appSettingsModelAttach(
|
||||
IsarCollection<dynamic> col,
|
||||
Id id,
|
||||
AppSettingsModel object,
|
||||
) {
|
||||
object.id = id;
|
||||
}
|
||||
|
||||
extension AppSettingsModelQueryWhereSort
|
||||
on QueryBuilder<AppSettingsModel, AppSettingsModel, QWhere> {
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterWhere> anyId() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(const IdWhereClause.any());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension AppSettingsModelQueryWhere
|
||||
on QueryBuilder<AppSettingsModel, AppSettingsModel, QWhereClause> {
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterWhereClause> idEqualTo(
|
||||
Id id,
|
||||
) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(IdWhereClause.between(lower: id, upper: id));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterWhereClause>
|
||||
idNotEqualTo(Id id) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
if (query.whereSort == Sort.asc) {
|
||||
return query
|
||||
.addWhereClause(
|
||||
IdWhereClause.lessThan(upper: id, includeUpper: false),
|
||||
)
|
||||
.addWhereClause(
|
||||
IdWhereClause.greaterThan(lower: id, includeLower: false),
|
||||
);
|
||||
} else {
|
||||
return query
|
||||
.addWhereClause(
|
||||
IdWhereClause.greaterThan(lower: id, includeLower: false),
|
||||
)
|
||||
.addWhereClause(
|
||||
IdWhereClause.lessThan(upper: id, includeUpper: false),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterWhereClause>
|
||||
idGreaterThan(Id id, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.greaterThan(lower: id, includeLower: include),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterWhereClause>
|
||||
idLessThan(Id id, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.lessThan(upper: id, includeUpper: include),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterWhereClause> idBetween(
|
||||
Id lowerId,
|
||||
Id upperId, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.between(
|
||||
lower: lowerId,
|
||||
includeLower: includeLower,
|
||||
upper: upperId,
|
||||
includeUpper: includeUpper,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension AppSettingsModelQueryFilter
|
||||
on QueryBuilder<AppSettingsModel, AppSettingsModel, QFilterCondition> {
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
idIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNull(property: r'id'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
idIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNotNull(property: r'id'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
idEqualTo(Id? value) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(property: r'id', value: value),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
idGreaterThan(Id? value, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'id',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
idLessThan(Id? value, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'id',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
idBetween(
|
||||
Id? lower,
|
||||
Id? upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.between(
|
||||
property: r'id',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
valueBoolIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNull(property: r'valueBool'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
valueBoolIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNotNull(property: r'valueBool'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
valueBoolEqualTo(bool? value) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(property: r'valueBool', value: value),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
valueDoubleIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNull(property: r'valueDouble'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
valueDoubleIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNotNull(property: r'valueDouble'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
valueDoubleEqualTo(double? value, {double epsilon = Query.epsilon}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(
|
||||
property: r'valueDouble',
|
||||
value: value,
|
||||
|
||||
epsilon: epsilon,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
valueDoubleGreaterThan(
|
||||
double? value, {
|
||||
bool include = false,
|
||||
double epsilon = Query.epsilon,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'valueDouble',
|
||||
value: value,
|
||||
|
||||
epsilon: epsilon,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
valueDoubleLessThan(
|
||||
double? value, {
|
||||
bool include = false,
|
||||
double epsilon = Query.epsilon,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'valueDouble',
|
||||
value: value,
|
||||
|
||||
epsilon: epsilon,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
valueDoubleBetween(
|
||||
double? lower,
|
||||
double? upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
double epsilon = Query.epsilon,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.between(
|
||||
property: r'valueDouble',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
|
||||
epsilon: epsilon,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
valueIndexIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNull(property: r'valueIndex'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
valueIndexIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNotNull(property: r'valueIndex'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
valueIndexEqualTo(int? value) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(property: r'valueIndex', value: value),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
valueIndexGreaterThan(int? value, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'valueIndex',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
valueIndexLessThan(int? value, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'valueIndex',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
valueIndexBetween(
|
||||
int? lower,
|
||||
int? upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.between(
|
||||
property: r'valueIndex',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
valueStringIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNull(property: r'valueString'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
valueStringIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNotNull(property: r'valueString'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
valueStringEqualTo(String? value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(
|
||||
property: r'valueString',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
valueStringGreaterThan(
|
||||
String? value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'valueString',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
valueStringLessThan(
|
||||
String? value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'valueString',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
valueStringBetween(
|
||||
String? lower,
|
||||
String? upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.between(
|
||||
property: r'valueString',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
valueStringStartsWith(String value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.startsWith(
|
||||
property: r'valueString',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
valueStringEndsWith(String value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.endsWith(
|
||||
property: r'valueString',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
valueStringContains(String value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.contains(
|
||||
property: r'valueString',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
valueStringMatches(String pattern, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.matches(
|
||||
property: r'valueString',
|
||||
wildcard: pattern,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
valueStringIsEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(property: r'valueString', value: ''),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterFilterCondition>
|
||||
valueStringIsNotEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(property: r'valueString', value: ''),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension AppSettingsModelQueryObject
|
||||
on QueryBuilder<AppSettingsModel, AppSettingsModel, QFilterCondition> {}
|
||||
|
||||
extension AppSettingsModelQueryLinks
|
||||
on QueryBuilder<AppSettingsModel, AppSettingsModel, QFilterCondition> {}
|
||||
|
||||
extension AppSettingsModelQuerySortBy
|
||||
on QueryBuilder<AppSettingsModel, AppSettingsModel, QSortBy> {
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterSortBy>
|
||||
sortByValueBool() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'valueBool', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterSortBy>
|
||||
sortByValueBoolDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'valueBool', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterSortBy>
|
||||
sortByValueDouble() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'valueDouble', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterSortBy>
|
||||
sortByValueDoubleDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'valueDouble', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterSortBy>
|
||||
sortByValueIndex() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'valueIndex', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterSortBy>
|
||||
sortByValueIndexDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'valueIndex', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterSortBy>
|
||||
sortByValueString() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'valueString', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterSortBy>
|
||||
sortByValueStringDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'valueString', Sort.desc);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension AppSettingsModelQuerySortThenBy
|
||||
on QueryBuilder<AppSettingsModel, AppSettingsModel, QSortThenBy> {
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterSortBy> thenById() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'id', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterSortBy>
|
||||
thenByIdDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'id', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterSortBy>
|
||||
thenByValueBool() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'valueBool', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterSortBy>
|
||||
thenByValueBoolDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'valueBool', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterSortBy>
|
||||
thenByValueDouble() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'valueDouble', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterSortBy>
|
||||
thenByValueDoubleDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'valueDouble', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterSortBy>
|
||||
thenByValueIndex() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'valueIndex', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterSortBy>
|
||||
thenByValueIndexDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'valueIndex', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterSortBy>
|
||||
thenByValueString() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'valueString', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QAfterSortBy>
|
||||
thenByValueStringDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'valueString', Sort.desc);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension AppSettingsModelQueryWhereDistinct
|
||||
on QueryBuilder<AppSettingsModel, AppSettingsModel, QDistinct> {
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QDistinct>
|
||||
distinctByValueBool() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'valueBool');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QDistinct>
|
||||
distinctByValueDouble() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'valueDouble');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QDistinct>
|
||||
distinctByValueIndex() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'valueIndex');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, AppSettingsModel, QDistinct>
|
||||
distinctByValueString({bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'valueString', caseSensitive: caseSensitive);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension AppSettingsModelQueryProperty
|
||||
on QueryBuilder<AppSettingsModel, AppSettingsModel, QQueryProperty> {
|
||||
QueryBuilder<AppSettingsModel, int, QQueryOperations> idProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'id');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, bool?, QQueryOperations> valueBoolProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'valueBool');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, double?, QQueryOperations>
|
||||
valueDoubleProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'valueDouble');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, int?, QQueryOperations> valueIndexProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'valueIndex');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AppSettingsModel, String?, QQueryOperations>
|
||||
valueStringProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'valueString');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,505 +0,0 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'generic_cache_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// IsarCollectionGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types
|
||||
|
||||
extension GetGenericCacheModelCollection on Isar {
|
||||
IsarCollection<GenericCacheModel> get genericCacheModels => this.collection();
|
||||
}
|
||||
|
||||
const GenericCacheModelSchema = CollectionSchema(
|
||||
name: r'GenericCacheModel',
|
||||
id: 3174486726793780620,
|
||||
properties: {
|
||||
r'cacheData': PropertySchema(
|
||||
id: 0,
|
||||
name: r'cacheData',
|
||||
type: IsarType.string,
|
||||
),
|
||||
},
|
||||
|
||||
estimateSize: _genericCacheModelEstimateSize,
|
||||
serialize: _genericCacheModelSerialize,
|
||||
deserialize: _genericCacheModelDeserialize,
|
||||
deserializeProp: _genericCacheModelDeserializeProp,
|
||||
idName: r'cacheKey',
|
||||
indexes: {},
|
||||
links: {},
|
||||
embeddedSchemas: {},
|
||||
|
||||
getId: _genericCacheModelGetId,
|
||||
getLinks: _genericCacheModelGetLinks,
|
||||
attach: _genericCacheModelAttach,
|
||||
version: '3.3.0',
|
||||
);
|
||||
|
||||
int _genericCacheModelEstimateSize(
|
||||
GenericCacheModel object,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
var bytesCount = offsets.last;
|
||||
{
|
||||
final value = object.cacheData;
|
||||
if (value != null) {
|
||||
bytesCount += 3 + value.length * 3;
|
||||
}
|
||||
}
|
||||
return bytesCount;
|
||||
}
|
||||
|
||||
void _genericCacheModelSerialize(
|
||||
GenericCacheModel object,
|
||||
IsarWriter writer,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
writer.writeString(offsets[0], object.cacheData);
|
||||
}
|
||||
|
||||
GenericCacheModel _genericCacheModelDeserialize(
|
||||
Id id,
|
||||
IsarReader reader,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
final object = GenericCacheModel();
|
||||
object.cacheData = reader.readStringOrNull(offsets[0]);
|
||||
object.cacheKey = id;
|
||||
return object;
|
||||
}
|
||||
|
||||
P _genericCacheModelDeserializeProp<P>(
|
||||
IsarReader reader,
|
||||
int propertyId,
|
||||
int offset,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
switch (propertyId) {
|
||||
case 0:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
default:
|
||||
throw IsarError('Unknown property with id $propertyId');
|
||||
}
|
||||
}
|
||||
|
||||
Id _genericCacheModelGetId(GenericCacheModel object) {
|
||||
return object.cacheKey ?? Isar.autoIncrement;
|
||||
}
|
||||
|
||||
List<IsarLinkBase<dynamic>> _genericCacheModelGetLinks(
|
||||
GenericCacheModel object,
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
void _genericCacheModelAttach(
|
||||
IsarCollection<dynamic> col,
|
||||
Id id,
|
||||
GenericCacheModel object,
|
||||
) {
|
||||
object.cacheKey = id;
|
||||
}
|
||||
|
||||
extension GenericCacheModelQueryWhereSort
|
||||
on QueryBuilder<GenericCacheModel, GenericCacheModel, QWhere> {
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterWhere>
|
||||
anyCacheKey() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(const IdWhereClause.any());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension GenericCacheModelQueryWhere
|
||||
on QueryBuilder<GenericCacheModel, GenericCacheModel, QWhereClause> {
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterWhereClause>
|
||||
cacheKeyEqualTo(Id cacheKey) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.between(lower: cacheKey, upper: cacheKey),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterWhereClause>
|
||||
cacheKeyNotEqualTo(Id cacheKey) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
if (query.whereSort == Sort.asc) {
|
||||
return query
|
||||
.addWhereClause(
|
||||
IdWhereClause.lessThan(upper: cacheKey, includeUpper: false),
|
||||
)
|
||||
.addWhereClause(
|
||||
IdWhereClause.greaterThan(lower: cacheKey, includeLower: false),
|
||||
);
|
||||
} else {
|
||||
return query
|
||||
.addWhereClause(
|
||||
IdWhereClause.greaterThan(lower: cacheKey, includeLower: false),
|
||||
)
|
||||
.addWhereClause(
|
||||
IdWhereClause.lessThan(upper: cacheKey, includeUpper: false),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterWhereClause>
|
||||
cacheKeyGreaterThan(Id cacheKey, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.greaterThan(lower: cacheKey, includeLower: include),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterWhereClause>
|
||||
cacheKeyLessThan(Id cacheKey, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.lessThan(upper: cacheKey, includeUpper: include),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterWhereClause>
|
||||
cacheKeyBetween(
|
||||
Id lowerCacheKey,
|
||||
Id upperCacheKey, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.between(
|
||||
lower: lowerCacheKey,
|
||||
includeLower: includeLower,
|
||||
upper: upperCacheKey,
|
||||
includeUpper: includeUpper,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension GenericCacheModelQueryFilter
|
||||
on QueryBuilder<GenericCacheModel, GenericCacheModel, QFilterCondition> {
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterFilterCondition>
|
||||
cacheDataIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNull(property: r'cacheData'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterFilterCondition>
|
||||
cacheDataIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNotNull(property: r'cacheData'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterFilterCondition>
|
||||
cacheDataEqualTo(String? value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(
|
||||
property: r'cacheData',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterFilterCondition>
|
||||
cacheDataGreaterThan(
|
||||
String? value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'cacheData',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterFilterCondition>
|
||||
cacheDataLessThan(
|
||||
String? value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'cacheData',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterFilterCondition>
|
||||
cacheDataBetween(
|
||||
String? lower,
|
||||
String? upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.between(
|
||||
property: r'cacheData',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterFilterCondition>
|
||||
cacheDataStartsWith(String value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.startsWith(
|
||||
property: r'cacheData',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterFilterCondition>
|
||||
cacheDataEndsWith(String value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.endsWith(
|
||||
property: r'cacheData',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterFilterCondition>
|
||||
cacheDataContains(String value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.contains(
|
||||
property: r'cacheData',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterFilterCondition>
|
||||
cacheDataMatches(String pattern, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.matches(
|
||||
property: r'cacheData',
|
||||
wildcard: pattern,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterFilterCondition>
|
||||
cacheDataIsEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(property: r'cacheData', value: ''),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterFilterCondition>
|
||||
cacheDataIsNotEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(property: r'cacheData', value: ''),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterFilterCondition>
|
||||
cacheKeyIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNull(property: r'cacheKey'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterFilterCondition>
|
||||
cacheKeyIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNotNull(property: r'cacheKey'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterFilterCondition>
|
||||
cacheKeyEqualTo(Id? value) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(property: r'cacheKey', value: value),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterFilterCondition>
|
||||
cacheKeyGreaterThan(Id? value, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'cacheKey',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterFilterCondition>
|
||||
cacheKeyLessThan(Id? value, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'cacheKey',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterFilterCondition>
|
||||
cacheKeyBetween(
|
||||
Id? lower,
|
||||
Id? upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.between(
|
||||
property: r'cacheKey',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension GenericCacheModelQueryObject
|
||||
on QueryBuilder<GenericCacheModel, GenericCacheModel, QFilterCondition> {}
|
||||
|
||||
extension GenericCacheModelQueryLinks
|
||||
on QueryBuilder<GenericCacheModel, GenericCacheModel, QFilterCondition> {}
|
||||
|
||||
extension GenericCacheModelQuerySortBy
|
||||
on QueryBuilder<GenericCacheModel, GenericCacheModel, QSortBy> {
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterSortBy>
|
||||
sortByCacheData() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'cacheData', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterSortBy>
|
||||
sortByCacheDataDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'cacheData', Sort.desc);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension GenericCacheModelQuerySortThenBy
|
||||
on QueryBuilder<GenericCacheModel, GenericCacheModel, QSortThenBy> {
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterSortBy>
|
||||
thenByCacheData() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'cacheData', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterSortBy>
|
||||
thenByCacheDataDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'cacheData', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterSortBy>
|
||||
thenByCacheKey() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'cacheKey', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QAfterSortBy>
|
||||
thenByCacheKeyDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'cacheKey', Sort.desc);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension GenericCacheModelQueryWhereDistinct
|
||||
on QueryBuilder<GenericCacheModel, GenericCacheModel, QDistinct> {
|
||||
QueryBuilder<GenericCacheModel, GenericCacheModel, QDistinct>
|
||||
distinctByCacheData({bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'cacheData', caseSensitive: caseSensitive);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension GenericCacheModelQueryProperty
|
||||
on QueryBuilder<GenericCacheModel, GenericCacheModel, QQueryProperty> {
|
||||
QueryBuilder<GenericCacheModel, int, QQueryOperations> cacheKeyProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'cacheKey');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<GenericCacheModel, String?, QQueryOperations>
|
||||
cacheDataProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'cacheData');
|
||||
});
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,552 +0,0 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'timetable_cache_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// IsarCollectionGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types
|
||||
|
||||
extension GetTimetableCacheModelCollection on Isar {
|
||||
IsarCollection<TimetableCacheModel> get timetableCacheModels =>
|
||||
this.collection();
|
||||
}
|
||||
|
||||
const TimetableCacheModelSchema = CollectionSchema(
|
||||
name: r'TimetableCacheModel',
|
||||
id: -8626340955125680275,
|
||||
properties: {
|
||||
r'values': PropertySchema(
|
||||
id: 0,
|
||||
name: r'values',
|
||||
type: IsarType.stringList,
|
||||
),
|
||||
},
|
||||
|
||||
estimateSize: _timetableCacheModelEstimateSize,
|
||||
serialize: _timetableCacheModelSerialize,
|
||||
deserialize: _timetableCacheModelDeserialize,
|
||||
deserializeProp: _timetableCacheModelDeserializeProp,
|
||||
idName: r'cacheKey',
|
||||
indexes: {},
|
||||
links: {},
|
||||
embeddedSchemas: {},
|
||||
|
||||
getId: _timetableCacheModelGetId,
|
||||
getLinks: _timetableCacheModelGetLinks,
|
||||
attach: _timetableCacheModelAttach,
|
||||
version: '3.3.0',
|
||||
);
|
||||
|
||||
int _timetableCacheModelEstimateSize(
|
||||
TimetableCacheModel object,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
var bytesCount = offsets.last;
|
||||
{
|
||||
final list = object.values;
|
||||
if (list != null) {
|
||||
bytesCount += 3 + list.length * 3;
|
||||
{
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
final value = list[i];
|
||||
bytesCount += value.length * 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return bytesCount;
|
||||
}
|
||||
|
||||
void _timetableCacheModelSerialize(
|
||||
TimetableCacheModel object,
|
||||
IsarWriter writer,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
writer.writeStringList(offsets[0], object.values);
|
||||
}
|
||||
|
||||
TimetableCacheModel _timetableCacheModelDeserialize(
|
||||
Id id,
|
||||
IsarReader reader,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
final object = TimetableCacheModel();
|
||||
object.cacheKey = id;
|
||||
object.values = reader.readStringList(offsets[0]);
|
||||
return object;
|
||||
}
|
||||
|
||||
P _timetableCacheModelDeserializeProp<P>(
|
||||
IsarReader reader,
|
||||
int propertyId,
|
||||
int offset,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
switch (propertyId) {
|
||||
case 0:
|
||||
return (reader.readStringList(offset)) as P;
|
||||
default:
|
||||
throw IsarError('Unknown property with id $propertyId');
|
||||
}
|
||||
}
|
||||
|
||||
Id _timetableCacheModelGetId(TimetableCacheModel object) {
|
||||
return object.cacheKey ?? Isar.autoIncrement;
|
||||
}
|
||||
|
||||
List<IsarLinkBase<dynamic>> _timetableCacheModelGetLinks(
|
||||
TimetableCacheModel object,
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
void _timetableCacheModelAttach(
|
||||
IsarCollection<dynamic> col,
|
||||
Id id,
|
||||
TimetableCacheModel object,
|
||||
) {
|
||||
object.cacheKey = id;
|
||||
}
|
||||
|
||||
extension TimetableCacheModelQueryWhereSort
|
||||
on QueryBuilder<TimetableCacheModel, TimetableCacheModel, QWhere> {
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterWhere>
|
||||
anyCacheKey() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(const IdWhereClause.any());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension TimetableCacheModelQueryWhere
|
||||
on QueryBuilder<TimetableCacheModel, TimetableCacheModel, QWhereClause> {
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterWhereClause>
|
||||
cacheKeyEqualTo(Id cacheKey) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.between(lower: cacheKey, upper: cacheKey),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterWhereClause>
|
||||
cacheKeyNotEqualTo(Id cacheKey) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
if (query.whereSort == Sort.asc) {
|
||||
return query
|
||||
.addWhereClause(
|
||||
IdWhereClause.lessThan(upper: cacheKey, includeUpper: false),
|
||||
)
|
||||
.addWhereClause(
|
||||
IdWhereClause.greaterThan(lower: cacheKey, includeLower: false),
|
||||
);
|
||||
} else {
|
||||
return query
|
||||
.addWhereClause(
|
||||
IdWhereClause.greaterThan(lower: cacheKey, includeLower: false),
|
||||
)
|
||||
.addWhereClause(
|
||||
IdWhereClause.lessThan(upper: cacheKey, includeUpper: false),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterWhereClause>
|
||||
cacheKeyGreaterThan(Id cacheKey, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.greaterThan(lower: cacheKey, includeLower: include),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterWhereClause>
|
||||
cacheKeyLessThan(Id cacheKey, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.lessThan(upper: cacheKey, includeUpper: include),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterWhereClause>
|
||||
cacheKeyBetween(
|
||||
Id lowerCacheKey,
|
||||
Id upperCacheKey, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.between(
|
||||
lower: lowerCacheKey,
|
||||
includeLower: includeLower,
|
||||
upper: upperCacheKey,
|
||||
includeUpper: includeUpper,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension TimetableCacheModelQueryFilter
|
||||
on
|
||||
QueryBuilder<
|
||||
TimetableCacheModel,
|
||||
TimetableCacheModel,
|
||||
QFilterCondition
|
||||
> {
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterFilterCondition>
|
||||
cacheKeyIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNull(property: r'cacheKey'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterFilterCondition>
|
||||
cacheKeyIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNotNull(property: r'cacheKey'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterFilterCondition>
|
||||
cacheKeyEqualTo(Id? value) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(property: r'cacheKey', value: value),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterFilterCondition>
|
||||
cacheKeyGreaterThan(Id? value, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'cacheKey',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterFilterCondition>
|
||||
cacheKeyLessThan(Id? value, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'cacheKey',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterFilterCondition>
|
||||
cacheKeyBetween(
|
||||
Id? lower,
|
||||
Id? upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.between(
|
||||
property: r'cacheKey',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterFilterCondition>
|
||||
valuesIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNull(property: r'values'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterFilterCondition>
|
||||
valuesIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNotNull(property: r'values'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterFilterCondition>
|
||||
valuesElementEqualTo(String value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(
|
||||
property: r'values',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterFilterCondition>
|
||||
valuesElementGreaterThan(
|
||||
String value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'values',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterFilterCondition>
|
||||
valuesElementLessThan(
|
||||
String value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'values',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterFilterCondition>
|
||||
valuesElementBetween(
|
||||
String lower,
|
||||
String upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.between(
|
||||
property: r'values',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterFilterCondition>
|
||||
valuesElementStartsWith(String value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.startsWith(
|
||||
property: r'values',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterFilterCondition>
|
||||
valuesElementEndsWith(String value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.endsWith(
|
||||
property: r'values',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterFilterCondition>
|
||||
valuesElementContains(String value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.contains(
|
||||
property: r'values',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterFilterCondition>
|
||||
valuesElementMatches(String pattern, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.matches(
|
||||
property: r'values',
|
||||
wildcard: pattern,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterFilterCondition>
|
||||
valuesElementIsEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(property: r'values', value: ''),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterFilterCondition>
|
||||
valuesElementIsNotEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(property: r'values', value: ''),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterFilterCondition>
|
||||
valuesLengthEqualTo(int length) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.listLength(r'values', length, true, length, true);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterFilterCondition>
|
||||
valuesIsEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.listLength(r'values', 0, true, 0, true);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterFilterCondition>
|
||||
valuesIsNotEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.listLength(r'values', 0, false, 999999, true);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterFilterCondition>
|
||||
valuesLengthLessThan(int length, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.listLength(r'values', 0, true, length, include);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterFilterCondition>
|
||||
valuesLengthGreaterThan(int length, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.listLength(r'values', length, include, 999999, true);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterFilterCondition>
|
||||
valuesLengthBetween(
|
||||
int lower,
|
||||
int upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.listLength(
|
||||
r'values',
|
||||
lower,
|
||||
includeLower,
|
||||
upper,
|
||||
includeUpper,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension TimetableCacheModelQueryObject
|
||||
on
|
||||
QueryBuilder<
|
||||
TimetableCacheModel,
|
||||
TimetableCacheModel,
|
||||
QFilterCondition
|
||||
> {}
|
||||
|
||||
extension TimetableCacheModelQueryLinks
|
||||
on
|
||||
QueryBuilder<
|
||||
TimetableCacheModel,
|
||||
TimetableCacheModel,
|
||||
QFilterCondition
|
||||
> {}
|
||||
|
||||
extension TimetableCacheModelQuerySortBy
|
||||
on QueryBuilder<TimetableCacheModel, TimetableCacheModel, QSortBy> {}
|
||||
|
||||
extension TimetableCacheModelQuerySortThenBy
|
||||
on QueryBuilder<TimetableCacheModel, TimetableCacheModel, QSortThenBy> {
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterSortBy>
|
||||
thenByCacheKey() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'cacheKey', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QAfterSortBy>
|
||||
thenByCacheKeyDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'cacheKey', Sort.desc);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension TimetableCacheModelQueryWhereDistinct
|
||||
on QueryBuilder<TimetableCacheModel, TimetableCacheModel, QDistinct> {
|
||||
QueryBuilder<TimetableCacheModel, TimetableCacheModel, QDistinct>
|
||||
distinctByValues() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'values');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension TimetableCacheModelQueryProperty
|
||||
on QueryBuilder<TimetableCacheModel, TimetableCacheModel, QQueryProperty> {
|
||||
QueryBuilder<TimetableCacheModel, int, QQueryOperations> cacheKeyProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'cacheKey');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<TimetableCacheModel, List<String>?, QQueryOperations>
|
||||
valuesProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'values');
|
||||
});
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,359 +1,12 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:ui' as ui;
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:firka/helpers/api/client/kreta_client.dart';
|
||||
import 'package:firka/helpers/db/models/app_settings_model.dart';
|
||||
import 'package:firka/helpers/db/models/generic_cache_model.dart';
|
||||
import 'package:firka/helpers/db/models/timetable_cache_model.dart';
|
||||
import 'package:firka/helpers/db/models/token_model.dart';
|
||||
import 'package:firka/helpers/firka_bundle.dart';
|
||||
import 'package:firka/helpers/settings.dart';
|
||||
import 'package:firka/helpers/swear_generator.dart';
|
||||
import 'package:firka/l10n/app_localizations_hu.dart';
|
||||
import 'package:firka/ui/model/style.dart';
|
||||
import 'package:firka/ui/phone/pages/error/error_page.dart';
|
||||
import 'package:firka/ui/phone/screens/debug/debug_screen.dart';
|
||||
import 'package:firka/ui/phone/screens/home/home_screen.dart';
|
||||
import 'package:firka/ui/phone/screens/login/login_screen.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:isar_community/isar.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
import 'helpers/db/models/homework_cache_model.dart';
|
||||
import 'helpers/update_notifier.dart';
|
||||
import 'helpers/live_activity_service.dart';
|
||||
import 'helpers/active_account_helper.dart';
|
||||
import 'helpers/watch_sync_helper.dart';
|
||||
import 'l10n/app_localizations.dart';
|
||||
import 'l10n/app_localizations_de.dart';
|
||||
import 'l10n/app_localizations_en.dart';
|
||||
|
||||
late final Logger logger;
|
||||
|
||||
Isar? isarInit;
|
||||
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||
late AppInitialization initData;
|
||||
bool initDone = false;
|
||||
|
||||
final dio = Dio();
|
||||
final isBeta = true;
|
||||
|
||||
class DeviceInfo {
|
||||
String model;
|
||||
|
||||
String versionRelease;
|
||||
String versionSdkInt;
|
||||
|
||||
DeviceInfo(this.model, this.versionRelease, this.versionSdkInt);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "DeviceInfo(model = \"$model\", versionRelease = \"$versionRelease\""
|
||||
", versionSdkInt = \"$versionSdkInt\"";
|
||||
}
|
||||
}
|
||||
|
||||
class AppInitialization {
|
||||
final Isar isar;
|
||||
final Directory appDir;
|
||||
final PackageInfo packageInfo;
|
||||
final DeviceInfo devInfo;
|
||||
late KretaClient client;
|
||||
List<TokenModel> tokens;
|
||||
bool hasWatchListener = false;
|
||||
Uint8List? profilePicture;
|
||||
SettingsStore settings;
|
||||
UpdateNotifier settingsUpdateNotifier = UpdateNotifier();
|
||||
UpdateNotifier profilePictureUpdateNotifier = UpdateNotifier();
|
||||
AppLocalizations l10n;
|
||||
final GlobalKey<NavigatorState> navigatorKey;
|
||||
|
||||
AppInitialization({
|
||||
required this.isar,
|
||||
required this.appDir,
|
||||
required this.devInfo,
|
||||
required this.packageInfo,
|
||||
required this.tokens,
|
||||
required this.settings,
|
||||
required this.l10n,
|
||||
required this.navigatorKey,
|
||||
});
|
||||
}
|
||||
|
||||
Future<Isar> initDB() async {
|
||||
if (isarInit != null) return isarInit!;
|
||||
final dir = await getApplicationDocumentsDirectory();
|
||||
|
||||
isarInit = await Isar.open(
|
||||
[
|
||||
TokenModelSchema,
|
||||
GenericCacheModelSchema,
|
||||
TimetableCacheModelSchema,
|
||||
HomeworkCacheModelSchema,
|
||||
AppSettingsModelSchema,
|
||||
HomeworkDoneModelSchema,
|
||||
],
|
||||
inspector: true,
|
||||
directory: dir.path,
|
||||
);
|
||||
|
||||
return isarInit!;
|
||||
}
|
||||
|
||||
Future<void> initLang(AppInitialization data) async {
|
||||
String? languageCode;
|
||||
|
||||
switch ((data.settings.group("settings").subGroup("application")["language"]
|
||||
as SettingsItemsRadio)
|
||||
.activeIndex) {
|
||||
case 1: // hu
|
||||
data.l10n = AppLocalizationsHu();
|
||||
languageCode = 'hu';
|
||||
break;
|
||||
case 2: // en
|
||||
data.l10n = AppLocalizationsEn();
|
||||
languageCode = 'en';
|
||||
break;
|
||||
case 3: // de
|
||||
data.l10n = AppLocalizationsDe();
|
||||
languageCode = 'de';
|
||||
break;
|
||||
default: // auto
|
||||
switch (ui.PlatformDispatcher.instance.locale.languageCode) {
|
||||
case 'hu':
|
||||
data.l10n = AppLocalizationsHu();
|
||||
languageCode = 'hu';
|
||||
break;
|
||||
case 'en':
|
||||
data.l10n = AppLocalizationsEn();
|
||||
languageCode = 'en';
|
||||
break;
|
||||
case 'de':
|
||||
data.l10n = AppLocalizationsDe();
|
||||
languageCode = 'de';
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (languageCode != null && Platform.isIOS) {
|
||||
try {
|
||||
await LiveActivityService.updateLanguagePreference(languageCode);
|
||||
} catch (e) {
|
||||
logger.warning('Failed to update language preference on backend: $e');
|
||||
}
|
||||
|
||||
try {
|
||||
await WatchSyncHelper.sendLanguageToWatch();
|
||||
} catch (e) {
|
||||
logger.warning('Failed to send language to Watch: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void initTheme(AppInitialization data) {
|
||||
final brightness =
|
||||
SchedulerBinding.instance.platformDispatcher.platformBrightness;
|
||||
|
||||
switch ((data.settings.group("settings").subGroup("customization")["theme"]
|
||||
as SettingsItemsRadio)
|
||||
.activeIndex) {
|
||||
case 1:
|
||||
appStyle = lightStyle;
|
||||
isLightMode.value = true;
|
||||
break;
|
||||
case 2:
|
||||
appStyle = darkStyle;
|
||||
isLightMode.value = false;
|
||||
break;
|
||||
default:
|
||||
if (brightness == Brightness.dark) {
|
||||
appStyle = darkStyle;
|
||||
isLightMode.value = false;
|
||||
} else {
|
||||
appStyle = lightStyle;
|
||||
isLightMode.value = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _initData(AppInitialization init) async {
|
||||
await init.settings.load(init.isar.appSettingsModels);
|
||||
await initLang(init);
|
||||
initTheme(init);
|
||||
init.settings = SettingsStore(init.l10n);
|
||||
await init.settings.load(init.isar.appSettingsModels);
|
||||
|
||||
var dispatcher = SchedulerBinding.instance.platformDispatcher;
|
||||
|
||||
dispatcher.onPlatformBrightnessChanged = () {
|
||||
globalUpdate.update();
|
||||
initTheme(init);
|
||||
};
|
||||
|
||||
dispatcher.onLocaleChanged = () {
|
||||
final languageSetting =
|
||||
init.settings.group("settings").subGroup("application")["language"]
|
||||
as SettingsItemsRadio;
|
||||
final isAutoLanguage = languageSetting.activeIndex == 0;
|
||||
if (!isAutoLanguage) {
|
||||
return;
|
||||
}
|
||||
|
||||
final previousLocale = init.l10n.localeName;
|
||||
unawaited(() async {
|
||||
await initLang(init);
|
||||
final nextLocale = init.l10n.localeName;
|
||||
if (previousLocale != nextLocale) {
|
||||
logger.info(
|
||||
"[Init] System locale changed in auto mode: $previousLocale -> $nextLocale",
|
||||
);
|
||||
}
|
||||
globalUpdate.update();
|
||||
}());
|
||||
};
|
||||
|
||||
resetOldTimeTableCache(init.isar);
|
||||
resetOldHomeworkCache(init.isar);
|
||||
|
||||
var didRunFreshInstallCleanup = false;
|
||||
if (Platform.isIOS) {
|
||||
try {
|
||||
didRunFreshInstallCleanup =
|
||||
await WatchSyncHelper.runFreshInstallCleanupIfNeeded(isar: init.isar);
|
||||
if (didRunFreshInstallCleanup) {
|
||||
logger.info(
|
||||
'[Init] Fresh-install cleanup completed; skipping startup iCloud recovery on this launch',
|
||||
);
|
||||
} else {
|
||||
await WatchSyncHelper.checkAndRecoverFromiCloud(
|
||||
isar: init.isar,
|
||||
tokens: init.tokens,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
logger.warning('[Init] iCloud bootstrap/recovery failed: $e');
|
||||
}
|
||||
}
|
||||
|
||||
final allTokens = await init.isar.tokenModels.where().findAll();
|
||||
init.tokens = allTokens;
|
||||
|
||||
if (allTokens.isNotEmpty) {
|
||||
final token = pickActiveToken(tokens: allTokens, settings: init.settings);
|
||||
if (token == null) {
|
||||
logger.warning(
|
||||
"[Init] Tokens disappeared during initialization; skipping client setup",
|
||||
);
|
||||
return;
|
||||
}
|
||||
logger.fine("Initializing kréta client as: ${token.studentId}");
|
||||
init.client = KretaClient(token, init.isar);
|
||||
|
||||
if (Platform.isIOS) {
|
||||
final expiryDate = token.expiryDate;
|
||||
if (expiryDate != null && expiryDate.isAfter(DateTime.now())) {
|
||||
KretaClient.clearReauthFlag();
|
||||
}
|
||||
|
||||
unawaited(() async {
|
||||
try {
|
||||
await WatchSyncHelper.saveTokenToiCloud(token);
|
||||
} catch (e) {
|
||||
logger.warning('[Init] Failed to sync active token to iCloud: $e');
|
||||
}
|
||||
|
||||
try {
|
||||
await WatchSyncHelper.sendTokenModelToWatch(token);
|
||||
} catch (e) {
|
||||
logger.warning('[Init] Failed to sync active token to Watch: $e');
|
||||
}
|
||||
}());
|
||||
}
|
||||
}
|
||||
|
||||
final dataDir = await getApplicationDocumentsDirectory();
|
||||
var pfpFile = File(p.join(dataDir.path, "profile.webp"));
|
||||
|
||||
if (await pfpFile.exists()) {
|
||||
init.profilePicture = await pfpFile.readAsBytes();
|
||||
}
|
||||
}
|
||||
|
||||
Future<AppInitialization> initializeApp() async {
|
||||
if (initDone) {
|
||||
await _initData(initData);
|
||||
return initData;
|
||||
}
|
||||
final isar = await initDB();
|
||||
final tokens = await isar.tokenModels.where().findAll();
|
||||
|
||||
logger.finest('Token count: ${tokens.length}');
|
||||
|
||||
var devInfoFetched = false;
|
||||
var devInfo = DeviceInfo("SM-A705FN", "11", "30");
|
||||
|
||||
try {
|
||||
if (Platform.isAndroid) {
|
||||
const channel = MethodChannel("firka.app/main");
|
||||
final rawInfo = ((await channel.invokeMethod("get_info")) as String)
|
||||
.split(";");
|
||||
|
||||
devInfo = DeviceInfo(rawInfo[0], rawInfo[1], rawInfo[2]);
|
||||
devInfoFetched = true;
|
||||
}
|
||||
} catch (e) {
|
||||
if (e is Error) {
|
||||
logger.shout("Error in initializeApp()", e.toString(), e.stackTrace);
|
||||
} else {
|
||||
logger.shout("Error in initializeApp()", e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
logger.fine("Fetched device info: ${devInfoFetched ? "yes" : "no"}");
|
||||
logger.fine("Using device info: ${devInfo.toString()}");
|
||||
|
||||
var init = AppInitialization(
|
||||
isar: isar,
|
||||
appDir: await getApplicationDocumentsDirectory(),
|
||||
devInfo: devInfo,
|
||||
packageInfo: await PackageInfo.fromPlatform(),
|
||||
tokens: tokens,
|
||||
settings: SettingsStore(AppLocalizationsHu()),
|
||||
l10n: AppLocalizationsHu(),
|
||||
navigatorKey: navigatorKey,
|
||||
);
|
||||
|
||||
if (Platform.isIOS) {
|
||||
try {
|
||||
await LiveActivityService.initialize().timeout(
|
||||
const Duration(seconds: 8),
|
||||
);
|
||||
} on TimeoutException catch (e, st) {
|
||||
logger.warning('LiveActivity init timed out: $e', e, st);
|
||||
} catch (e, st) {
|
||||
logger.severe('Failed to initialize LiveActivity: $e', e, st);
|
||||
}
|
||||
}
|
||||
|
||||
await _initData(init);
|
||||
|
||||
init.settingsUpdateNotifier.addListener(() {
|
||||
logger.finest("Settings updated");
|
||||
});
|
||||
|
||||
return init;
|
||||
}
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/app/initialization.dart';
|
||||
import 'package:firka/app/initialization_screen.dart';
|
||||
import 'package:firka/ui/phone/pages/error/error_page.dart';
|
||||
|
||||
void main() async {
|
||||
logger = Logger("Firka");
|
||||
@@ -374,113 +27,8 @@ void main() async {
|
||||
logger.severe("Failed to load .env: $e", e, st);
|
||||
}
|
||||
|
||||
{
|
||||
final jwtPattern = RegExp(
|
||||
r'([A-Za-z0-9-_]+)\.([A-Za-z0-9-_]+)\.([A-Za-z0-9-_]+)',
|
||||
);
|
||||
final omPattern = RegExp(r'(\d{3})(\d{6})([A-Za-z0-9]?)');
|
||||
final refreshTokenPattern = RegExp(
|
||||
r'"(?=.{21,}$)([A-Z0-9]+-[A-Z0-9_\-.~+]*)"',
|
||||
);
|
||||
await setupLogging();
|
||||
|
||||
final docs = await getApplicationDocumentsDirectory();
|
||||
|
||||
Future<void> deleteOldLogFiles() async {
|
||||
final docs = await getApplicationDocumentsDirectory();
|
||||
final dir = Directory(docs.path);
|
||||
if (!dir.existsSync()) return;
|
||||
|
||||
final now = DateTime.now();
|
||||
final cutoff = now.subtract(Duration(days: 30));
|
||||
|
||||
final logFileRegex = RegExp(r'^(\d{4})_(\d{2})_(\d{2})\.log$');
|
||||
|
||||
for (final entity in dir.listSync()) {
|
||||
if (entity is! File) continue;
|
||||
final name = entity.uri.pathSegments.last;
|
||||
final m = logFileRegex.firstMatch(name);
|
||||
if (m == null) continue;
|
||||
|
||||
try {
|
||||
final y = int.parse(m.group(1)!);
|
||||
final mo = int.parse(m.group(2)!);
|
||||
final d = int.parse(m.group(3)!);
|
||||
final fileDate = DateTime(y, mo, d);
|
||||
if (fileDate.isBefore(
|
||||
DateTime(cutoff.year, cutoff.month, cutoff.day),
|
||||
)) {
|
||||
logger.info("Removing old log file: $name");
|
||||
await entity.delete();
|
||||
}
|
||||
} catch (_) {
|
||||
// ignore parse/delete errors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String logFilePathForDate(DateTime dt) {
|
||||
final fileName = "${DateFormat("yyyy_MM_dd").format(dt)}.log";
|
||||
return Directory(docs.path).uri.resolve(fileName).toFilePath();
|
||||
}
|
||||
|
||||
File fileForDate(DateTime dt) {
|
||||
final path = logFilePathForDate(dt);
|
||||
final file = File(path);
|
||||
if (!file.existsSync()) file.createSync(recursive: true);
|
||||
return file;
|
||||
}
|
||||
|
||||
String censorLog(String msg) {
|
||||
return msg
|
||||
.replaceAll(jwtPattern, '***')
|
||||
.replaceAllMapped(omPattern, (match) {
|
||||
return "${match.group(1)}******${match.group(3)}";
|
||||
})
|
||||
.replaceAll(refreshTokenPattern, '"***"');
|
||||
}
|
||||
|
||||
hierarchicalLoggingEnabled = true;
|
||||
logger.level = Level.ALL;
|
||||
|
||||
DateTime currentDate = DateTime.now();
|
||||
IOSink sink = fileForDate(currentDate).openWrite(mode: FileMode.append);
|
||||
|
||||
logger.onRecord.listen((record) {
|
||||
final now = DateTime.now();
|
||||
if (now.year != currentDate.year ||
|
||||
now.month != currentDate.month ||
|
||||
now.day != currentDate.day) {
|
||||
sink.flush();
|
||||
sink.close();
|
||||
currentDate = now;
|
||||
sink = fileForDate(currentDate).openWrite(mode: FileMode.append);
|
||||
}
|
||||
|
||||
final censored = censorLog(record.message);
|
||||
final timestamp = DateFormat('yyyy-MM-dd HH:mm:ss.SSS').format(now);
|
||||
final level = record.level.name;
|
||||
final line = '[$timestamp] [$level] [$censored]';
|
||||
sink.writeln(line);
|
||||
|
||||
debugPrint(
|
||||
"[Firka] [${record.level.name}] ${kDebugMode ? record.message : censored}",
|
||||
);
|
||||
});
|
||||
|
||||
(() async {
|
||||
await deleteOldLogFiles();
|
||||
})();
|
||||
}
|
||||
|
||||
try {
|
||||
logger.finest('loading dirty words');
|
||||
await loadDirtyWords();
|
||||
logger.finest('loaded dirty words');
|
||||
} catch (e, st) {
|
||||
logger.severe('Failed to load dirty words: $e', e, st);
|
||||
}
|
||||
|
||||
// Run App Initialization
|
||||
runApp(InitializationScreen());
|
||||
},
|
||||
(error, stackTrace) {
|
||||
@@ -498,174 +46,3 @@ void main() async {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
final ValueNotifier<bool> isLightMode = ValueNotifier<bool>(true);
|
||||
final UpdateNotifier globalUpdate = UpdateNotifier();
|
||||
|
||||
class InitializationScreen extends StatelessWidget {
|
||||
InitializationScreen({super.key});
|
||||
|
||||
final Future<AppInitialization> _init = initializeApp().timeout(
|
||||
const Duration(seconds: 20),
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder<AppInitialization>(
|
||||
future: _init,
|
||||
builder: (context, snapshot) {
|
||||
// Check if initialization is complete
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
if (snapshot.hasError) {
|
||||
logger.shout(
|
||||
"Error in InitializationScreen",
|
||||
snapshot.error.toString(),
|
||||
snapshot.stackTrace,
|
||||
);
|
||||
|
||||
FlutterNativeSplash.remove();
|
||||
|
||||
// Handle initialization error
|
||||
return MaterialApp(
|
||||
key: ValueKey('errorPage'),
|
||||
home: DefaultAssetBundle(
|
||||
bundle: FirkaBundle(),
|
||||
child: Scaffold(
|
||||
body: Center(
|
||||
child: Text(
|
||||
'Error initializing app: ${snapshot.error}',
|
||||
style: TextStyle(color: Colors.red),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Initialization successful, determine which screen to show
|
||||
Widget screen;
|
||||
|
||||
assert(snapshot.data != null);
|
||||
initData = snapshot.data!;
|
||||
initDone = true;
|
||||
|
||||
FlutterNativeSplash.remove();
|
||||
|
||||
WatchSyncHelper.initialize();
|
||||
if (Platform.isIOS) {
|
||||
unawaited(() async {
|
||||
try {
|
||||
await WatchSyncHelper.sendLanguageToWatch();
|
||||
} catch (e) {
|
||||
logger.warning(
|
||||
'[Init] Failed to publish language to Watch after sync init: $e',
|
||||
);
|
||||
}
|
||||
}());
|
||||
}
|
||||
|
||||
if (!initData.hasWatchListener) {
|
||||
initData.hasWatchListener = true;
|
||||
|
||||
WatchSyncHelper.onWatchMessage = (msg) {
|
||||
logger.finest("WatchOS IPC [Watch -> Phone]: ${msg["id"]}");
|
||||
|
||||
switch (msg["id"]) {
|
||||
case "ping":
|
||||
if (initData.tokens.isNotEmpty) {
|
||||
logger.finest("WatchOS IPC [Phone -> Watch]: pong");
|
||||
const watchChannel = MethodChannel('app.firka/watch_sync');
|
||||
watchChannel.invokeMethod('sendMessageToWatch', {
|
||||
"id": "pong",
|
||||
});
|
||||
navigatorKey.currentState?.push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => HomeScreen(
|
||||
initData,
|
||||
true,
|
||||
model: msg["model"] as String? ?? "unknown",
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (snapshot.data!.tokens.isEmpty) {
|
||||
screen = LoginScreen(initData, key: ValueKey('loginScreen'));
|
||||
} else {
|
||||
screen = HomeScreen(initData, false, key: ValueKey('homeScreen'));
|
||||
}
|
||||
|
||||
return MaterialApp(
|
||||
title: 'Firka',
|
||||
key: ValueKey('firkaApp'),
|
||||
navigatorKey: navigatorKey,
|
||||
// Use the global navigator key
|
||||
theme: ThemeData(
|
||||
primarySwatch: Colors.lightGreen,
|
||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||
),
|
||||
localizationsDelegates: [
|
||||
AppLocalizations.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
home: DefaultAssetBundle(
|
||||
bundle: FirkaBundle(),
|
||||
child: ValueListenableBuilder<bool>(
|
||||
valueListenable: isLightMode,
|
||||
builder: (context, isLight, _) {
|
||||
final overlay = SystemUiOverlayStyle(
|
||||
statusBarColor: Colors.transparent,
|
||||
statusBarIconBrightness: isLight
|
||||
? Brightness.dark
|
||||
: Brightness.light,
|
||||
statusBarBrightness: isLight
|
||||
? Brightness.light
|
||||
: Brightness.dark,
|
||||
systemStatusBarContrastEnforced: false,
|
||||
);
|
||||
|
||||
// Ensure system is updated immediately
|
||||
SystemChrome.setSystemUIOverlayStyle(overlay);
|
||||
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: overlay,
|
||||
child: screen,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
routes: {
|
||||
'/login': (context) => DefaultAssetBundle(
|
||||
bundle: FirkaBundle(),
|
||||
child: LoginScreen(initData, key: ValueKey('loginScreen')),
|
||||
),
|
||||
'/home': (context) => DefaultAssetBundle(
|
||||
bundle: FirkaBundle(),
|
||||
child: HomeScreen(initData, false, key: ValueKey('homeScreen')),
|
||||
),
|
||||
'/debug': (context) => DefaultAssetBundle(
|
||||
bundle: FirkaBundle(),
|
||||
child: DebugScreen(initData, key: ValueKey('debugScreen')),
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return MaterialApp(
|
||||
home: DefaultAssetBundle(
|
||||
bundle: FirkaBundle(),
|
||||
child: Scaffold(
|
||||
backgroundColor: const Color(0xFF7CA120),
|
||||
body: Container(), // Covered by native splash
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'db/models/token_model.dart';
|
||||
import 'package:firka/data/models/token_model.dart';
|
||||
|
||||
int resolveActiveAccountIndex(dynamic settings) {
|
||||
try {
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:firka/helpers/api/model/timetable.dart';
|
||||
import 'package:firka/api/model/timetable.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
class LiveActivityManager {
|
||||
@@ -1,21 +1,21 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:firka/helpers/api/client/kreta_client.dart';
|
||||
import 'package:firka/helpers/api/client/live_activity_backend_client.dart';
|
||||
import 'package:firka/helpers/api/model/generic.dart';
|
||||
import 'package:firka/helpers/api/model/timetable.dart';
|
||||
import 'package:firka/helpers/db/models/app_settings_model.dart';
|
||||
import 'package:firka/helpers/db/widget.dart';
|
||||
import 'package:firka/helpers/live_activity_manager.dart';
|
||||
import 'package:firka/helpers/active_account_helper.dart';
|
||||
import 'package:firka/helpers/settings.dart';
|
||||
import 'package:firka/api/client/kreta_client.dart';
|
||||
import 'package:firka/api/client/live_activity_backend_client.dart';
|
||||
import 'package:firka/api/model/generic.dart';
|
||||
import 'package:firka/api/model/timetable.dart';
|
||||
import 'package:firka/data/models/app_settings_model.dart';
|
||||
import 'package:firka/data/widget.dart';
|
||||
import 'package:firka/services/live_activity_manager.dart';
|
||||
import 'package:firka/services/active_account_helper.dart';
|
||||
import 'package:firka/core/settings.dart';
|
||||
import 'package:firka/ui/phone/screens/live_activity/live_activity_consent_screen.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../main.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
|
||||
/// Service that coordinates LiveActivity functionality
|
||||
/// Handles timetable synchronization, device token management, and activity updates
|
||||
@@ -7,10 +7,10 @@ import 'package:flutter/services.dart';
|
||||
import 'package:isar_community/isar.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../main.dart';
|
||||
import 'active_account_helper.dart';
|
||||
import 'api/client/kreta_client.dart';
|
||||
import 'db/models/token_model.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/services/active_account_helper.dart';
|
||||
import 'package:firka/api/client/kreta_client.dart';
|
||||
import 'package:firka/data/models/token_model.dart';
|
||||
|
||||
/// Helper class for Watch ↔ iPhone token sync
|
||||
class WatchSyncHelper {
|
||||
@@ -1,28 +1,28 @@
|
||||
import 'package:firka/helpers/api/model/generic.dart';
|
||||
import 'package:firka/helpers/api/model/grade.dart';
|
||||
import 'package:firka/helpers/api/model/homework.dart';
|
||||
import 'package:firka/helpers/api/model/subject.dart';
|
||||
import 'package:firka/helpers/db/models/homework_cache_model.dart';
|
||||
import 'package:firka/helpers/debug_helper.dart';
|
||||
import 'package:firka/helpers/extensions.dart';
|
||||
import 'package:firka/helpers/settings.dart';
|
||||
import 'package:firka/helpers/ui/firka_shadow.dart';
|
||||
import 'package:firka/ui/widget/firka_icon.dart';
|
||||
import 'package:firka/api/model/generic.dart';
|
||||
import 'package:firka/api/model/grade.dart';
|
||||
import 'package:firka/api/model/homework.dart';
|
||||
import 'package:firka/api/model/subject.dart';
|
||||
import 'package:firka/data/models/homework_cache_model.dart';
|
||||
import 'package:firka/core/debug_helper.dart';
|
||||
import 'package:firka/core/extensions.dart';
|
||||
import 'package:firka/core/settings.dart';
|
||||
import 'package:firka/ui/components/firka_shadow.dart';
|
||||
import 'package:firka/ui/shared/firka_icon.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_html/flutter_html.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:majesticons_flutter/majesticons_flutter.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../../main.dart';
|
||||
import '../../ui/model/style.dart';
|
||||
import '../../ui/phone/screens/home/home_screen.dart';
|
||||
import '../../ui/phone/widgets/lesson.dart';
|
||||
import '../../ui/widget/class_icon.dart';
|
||||
import '../api/model/timetable.dart';
|
||||
import 'firka_card.dart';
|
||||
import 'grade.dart';
|
||||
import '../../helpers/api/model/test.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:firka/ui/phone/screens/home/home_screen.dart';
|
||||
import 'package:firka/ui/phone/widgets/lesson.dart';
|
||||
import 'package:firka/ui/shared/class_icon.dart';
|
||||
import 'package:firka/api/model/timetable.dart';
|
||||
import 'package:firka/ui/components/firka_card.dart';
|
||||
import 'package:firka/ui/components/grade.dart';
|
||||
import 'package:firka/api/model/test.dart';
|
||||
|
||||
Future<void> showLessonBottomSheet(
|
||||
BuildContext context,
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:firka/helpers/ui/firka_shadow.dart';
|
||||
import 'package:firka/ui/components/firka_shadow.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../main.dart';
|
||||
import '../../ui/model/style.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
|
||||
enum Attach { none, bottom, top }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:firka/main.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../ui/model/style.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
|
||||
class FirkaShadow extends StatelessWidget {
|
||||
final Widget child;
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:firka/helpers/api/model/grade.dart';
|
||||
import 'package:firka/api/model/grade.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../ui/model/style.dart';
|
||||
import 'grade_helpers.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:firka/ui/components/grade_helpers.dart';
|
||||
|
||||
class GradeWidget extends StatelessWidget {
|
||||
final Grade grade;
|
||||
@@ -1,11 +1,11 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:firka/helpers/settings.dart';
|
||||
import 'package:firka/main.dart';
|
||||
import 'package:firka/core/settings.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
|
||||
import '../../ui/model/style.dart';
|
||||
import '../api/model/grade.dart';
|
||||
import '../api/model/subject.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:firka/api/model/grade.dart';
|
||||
import 'package:firka/api/model/subject.dart';
|
||||
|
||||
int roundGrade(double grade) {
|
||||
final rounding = initData.settings
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:firka/ui/model/style.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:firka/helpers/image_preloader.dart';
|
||||
import 'package:firka/helpers/firka_bundle.dart';
|
||||
import 'package:firka/helpers/swear_generator.dart';
|
||||
import 'package:firka/core/image_preloader.dart';
|
||||
import 'package:firka/core/firka_bundle.dart';
|
||||
import 'package:firka/core/swear_generator.dart';
|
||||
|
||||
class ErrorPage extends StatelessWidget {
|
||||
final String exception;
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import 'package:firka/helpers/db/models/app_settings_model.dart';
|
||||
import 'package:firka/helpers/settings.dart';
|
||||
import 'package:firka/helpers/ui/firka_shadow.dart';
|
||||
import 'package:firka/main.dart';
|
||||
import 'package:firka/ui/model/style.dart';
|
||||
import 'package:firka/data/models/app_settings_model.dart';
|
||||
import 'package:firka/core/settings.dart';
|
||||
import 'package:firka/ui/components/firka_shadow.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:firka/ui/phone/screens/settings/settings_screen.dart';
|
||||
import 'package:firka/ui/widget/firka_icon.dart';
|
||||
import 'package:firka/ui/shared/firka_icon.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:majesticons_flutter/majesticons_flutter.dart';
|
||||
|
||||
import '../../../../helpers/firka_bundle.dart';
|
||||
import '../../screens/debug/debug_screen.dart';
|
||||
import '../../screens/home/home_screen.dart';
|
||||
import 'package:firka/core/firka_bundle.dart';
|
||||
import 'package:firka/ui/phone/screens/debug/debug_screen.dart';
|
||||
import 'package:firka/ui/phone/screens/home/home_screen.dart';
|
||||
|
||||
void showExtrasBottomSheet(BuildContext context, AppInitialization data) {
|
||||
Widget Function(double) debugBtn = (_) => const SizedBox();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:firka/ui/model/style.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
void showErrorBottomSheet(BuildContext context, String err) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:firka/helpers/settings.dart';
|
||||
import 'package:firka/main.dart';
|
||||
import 'package:firka/ui/model/style.dart';
|
||||
import 'package:firka/core/settings.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:firka/ui/phone/widgets/login_webview.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import 'package:firka/helpers/debug_helper.dart';
|
||||
import 'package:firka/helpers/ui/firka_card.dart';
|
||||
import 'package:firka/helpers/watch_sync_helper.dart';
|
||||
import 'package:firka/main.dart';
|
||||
import 'package:firka/core/debug_helper.dart';
|
||||
import 'package:firka/ui/components/firka_card.dart';
|
||||
import 'package:firka/services/watch_sync_helper.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
|
||||
import '../../../model/style.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
|
||||
void showWearBottomSheet(
|
||||
BuildContext context,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:firka/main.dart';
|
||||
import 'package:firka/ui/model/style.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:firka/ui/phone/pages/extras/main_reauth.dart';
|
||||
import 'package:firka/ui/widget/firka_icon.dart';
|
||||
import 'package:firka/ui/shared/firka_icon.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:majesticons_flutter/majesticons_flutter.dart';
|
||||
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import 'package:firka/helpers/api/client/kreta_client.dart';
|
||||
import 'package:firka/helpers/api/model/generic.dart';
|
||||
import 'package:firka/helpers/average_helper.dart';
|
||||
import 'package:firka/helpers/ui/firka_card.dart';
|
||||
import 'package:firka/helpers/ui/grade_helpers.dart';
|
||||
import 'package:firka/api/client/kreta_client.dart';
|
||||
import 'package:firka/api/model/generic.dart';
|
||||
import 'package:firka/core/average_helper.dart';
|
||||
import 'package:firka/ui/components/firka_card.dart';
|
||||
import 'package:firka/ui/components/grade_helpers.dart';
|
||||
import 'package:firka/ui/phone/widgets/grade_chart.dart';
|
||||
import 'package:firka/ui/widget/grade_small_card.dart';
|
||||
import 'package:firka/ui/shared/grade_small_card.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../helpers/api/consts.dart';
|
||||
import '../../../../helpers/api/model/class_group.dart';
|
||||
import '../../../../helpers/api/model/grade.dart';
|
||||
import '../../../../helpers/api/model/subject.dart';
|
||||
import '../../../../helpers/api/model/timetable.dart';
|
||||
import '../../../../helpers/debug_helper.dart';
|
||||
import '../../../../helpers/firka_state.dart';
|
||||
import '../../../../helpers/update_notifier.dart';
|
||||
import '../../../../main.dart';
|
||||
import '../../../model/style.dart';
|
||||
import '../../../widget/delayed_spinner.dart';
|
||||
import 'package:firka/api/consts.dart';
|
||||
import 'package:firka/api/model/class_group.dart';
|
||||
import 'package:firka/api/model/grade.dart';
|
||||
import 'package:firka/api/model/subject.dart';
|
||||
import 'package:firka/api/model/timetable.dart';
|
||||
import 'package:firka/core/debug_helper.dart';
|
||||
import 'package:firka/core/state/firka_state.dart';
|
||||
import 'package:firka/core/state/update_notifier.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:firka/ui/shared/delayed_spinner.dart';
|
||||
|
||||
class HomeGradesScreen extends StatefulWidget {
|
||||
final AppInitialization data;
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import 'package:firka/helpers/api/model/grade.dart';
|
||||
import 'package:firka/helpers/extensions.dart';
|
||||
import 'package:firka/helpers/ui/common_bottom_sheets.dart';
|
||||
import 'package:firka/helpers/ui/firka_card.dart';
|
||||
import 'package:firka/helpers/ui/grade.dart';
|
||||
import 'package:firka/api/model/grade.dart';
|
||||
import 'package:firka/core/extensions.dart';
|
||||
import 'package:firka/ui/components/common_bottom_sheets.dart';
|
||||
import 'package:firka/ui/components/firka_card.dart';
|
||||
import 'package:firka/ui/components/grade.dart';
|
||||
import 'package:firka/ui/phone/pages/home/home_grades.dart';
|
||||
import 'package:firka/ui/widget/class_icon.dart';
|
||||
import 'package:firka/ui/widget/firka_icon.dart';
|
||||
import 'package:firka/ui/shared/class_icon.dart';
|
||||
import 'package:firka/ui/shared/firka_icon.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:majesticons_flutter/majesticons_flutter.dart';
|
||||
|
||||
import '../../../../helpers/firka_state.dart';
|
||||
import '../../../../helpers/update_notifier.dart';
|
||||
import '../../../../main.dart';
|
||||
import '../../../model/style.dart';
|
||||
import 'package:firka/core/state/firka_state.dart';
|
||||
import 'package:firka/core/state/update_notifier.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
|
||||
class HomeGradesSubjectScreen extends StatefulWidget {
|
||||
final AppInitialization data;
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:firka/helpers/api/client/kreta_stream.dart';
|
||||
import 'package:firka/helpers/api/model/grade.dart';
|
||||
import 'package:firka/helpers/extensions.dart';
|
||||
import 'package:firka/helpers/ui/common_bottom_sheets.dart';
|
||||
import 'package:firka/api/client/kreta_stream.dart';
|
||||
import 'package:firka/api/model/grade.dart';
|
||||
import 'package:firka/core/extensions.dart';
|
||||
import 'package:firka/ui/components/common_bottom_sheets.dart';
|
||||
import 'package:firka/ui/phone/screens/message/message_screen.dart';
|
||||
import 'package:firka/ui/phone/widgets/home_main_starting_soon.dart';
|
||||
import 'package:firka/ui/phone/widgets/homework.dart';
|
||||
import 'package:firka/ui/phone/widgets/info_board_item.dart';
|
||||
import 'package:firka/ui/phone/widgets/lesson_small.dart';
|
||||
import 'package:firka/ui/widget/delayed_spinner.dart';
|
||||
import 'package:firka/ui/shared/delayed_spinner.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:majesticons_flutter/majesticons_flutter.dart';
|
||||
|
||||
import '../../../../helpers/api/model/homework.dart';
|
||||
import '../../../../helpers/api/model/notice_board.dart';
|
||||
import '../../../../helpers/api/model/student.dart';
|
||||
import '../../../../helpers/api/model/test.dart';
|
||||
import '../../../../helpers/api/model/timetable.dart';
|
||||
import '../../../../helpers/debug_helper.dart';
|
||||
import '../../../../helpers/firka_state.dart';
|
||||
import '../../../../helpers/ui/firka_card.dart';
|
||||
import '../../../../helpers/ui/grade.dart';
|
||||
import '../../../../helpers/update_notifier.dart';
|
||||
import '../../../../main.dart';
|
||||
import '../../../model/style.dart';
|
||||
import '../../../widget/firka_icon.dart';
|
||||
import 'package:firka/api/model/homework.dart';
|
||||
import 'package:firka/api/model/notice_board.dart';
|
||||
import 'package:firka/api/model/student.dart';
|
||||
import 'package:firka/api/model/test.dart';
|
||||
import 'package:firka/api/model/timetable.dart';
|
||||
import 'package:firka/core/debug_helper.dart';
|
||||
import 'package:firka/core/state/firka_state.dart';
|
||||
import 'package:firka/ui/components/firka_card.dart';
|
||||
import 'package:firka/ui/components/grade.dart';
|
||||
import 'package:firka/core/state/update_notifier.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:firka/ui/shared/firka_icon.dart';
|
||||
import '../../widgets/home_main_welcome.dart';
|
||||
import '../../widgets/lesson_big.dart';
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:firka/ui/phone/screens/home/home_screen.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../helpers/firka_state.dart';
|
||||
import '../../../../helpers/update_notifier.dart';
|
||||
import '../../../model/style.dart';
|
||||
import 'package:firka/core/state/firka_state.dart';
|
||||
import 'package:firka/core/state/update_notifier.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
|
||||
class PageWithSubPages extends StatefulWidget {
|
||||
final int pageIndex;
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:carousel_slider/carousel_slider.dart';
|
||||
import 'package:firka/helpers/api/client/kreta_client.dart';
|
||||
import 'package:firka/helpers/api/client/kreta_stream.dart';
|
||||
import 'package:firka/helpers/api/model/test.dart';
|
||||
import 'package:firka/helpers/api/model/timetable.dart';
|
||||
import 'package:firka/helpers/debug_helper.dart';
|
||||
import 'package:firka/helpers/extensions.dart';
|
||||
import 'package:firka/helpers/settings.dart';
|
||||
import 'package:firka/ui/model/style.dart';
|
||||
import 'package:firka/api/client/kreta_client.dart';
|
||||
import 'package:firka/api/client/kreta_stream.dart';
|
||||
import 'package:firka/api/model/test.dart';
|
||||
import 'package:firka/api/model/timetable.dart';
|
||||
import 'package:firka/core/debug_helper.dart';
|
||||
import 'package:firka/core/extensions.dart';
|
||||
import 'package:firka/core/settings.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:firka/ui/phone/screens/settings/settings_screen.dart';
|
||||
import 'package:firka/ui/phone/widgets/bubble_test.dart';
|
||||
import 'package:firka/ui/widget/delayed_spinner.dart';
|
||||
import 'package:firka/ui/shared/delayed_spinner.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:majesticons_flutter/majesticons_flutter.dart';
|
||||
import 'package:transparent_pointer/transparent_pointer.dart';
|
||||
|
||||
import '../../../../helpers/api/consts.dart';
|
||||
import '../../../../helpers/firka_state.dart';
|
||||
import '../../../../helpers/update_notifier.dart';
|
||||
import '../../../../main.dart';
|
||||
import '../../../widget/firka_icon.dart';
|
||||
import 'package:firka/api/consts.dart';
|
||||
import 'package:firka/core/state/firka_state.dart';
|
||||
import 'package:firka/core/state/update_notifier.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/ui/shared/firka_icon.dart';
|
||||
import '../../widgets/bottom_tt_icon.dart';
|
||||
import '../../widgets/tt_day.dart';
|
||||
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import 'package:firka/helpers/api/consts.dart';
|
||||
import 'package:firka/helpers/api/model/omission.dart';
|
||||
import 'package:firka/helpers/api/model/timetable.dart';
|
||||
import 'package:firka/helpers/debug_helper.dart';
|
||||
import 'package:firka/helpers/extensions.dart';
|
||||
import 'package:firka/helpers/settings.dart';
|
||||
import 'package:firka/ui/model/style.dart';
|
||||
import 'package:firka/ui/widget/delayed_spinner.dart';
|
||||
import 'package:firka/api/consts.dart';
|
||||
import 'package:firka/api/model/omission.dart';
|
||||
import 'package:firka/api/model/timetable.dart';
|
||||
import 'package:firka/core/debug_helper.dart';
|
||||
import 'package:firka/core/extensions.dart';
|
||||
import 'package:firka/core/settings.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:firka/ui/shared/delayed_spinner.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
||||
import 'package:majesticons_flutter/majesticons_flutter.dart';
|
||||
import 'package:transparent_pointer/transparent_pointer.dart';
|
||||
|
||||
import '../../../../helpers/api/model/test.dart';
|
||||
import '../../../../helpers/firka_state.dart';
|
||||
import '../../../../helpers/update_notifier.dart';
|
||||
import '../../../../main.dart';
|
||||
import '../../../widget/firka_icon.dart';
|
||||
import 'package:firka/api/model/test.dart';
|
||||
import 'package:firka/core/state/firka_state.dart';
|
||||
import 'package:firka/core/state/update_notifier.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/ui/shared/firka_icon.dart';
|
||||
import '../../screens/settings/settings_screen.dart';
|
||||
|
||||
class HomeTimetableMonthlyScreen extends StatefulWidget {
|
||||
|
||||
@@ -2,19 +2,19 @@
|
||||
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:firka/helpers/db/models/token_model.dart';
|
||||
import 'package:firka/helpers/extensions.dart';
|
||||
import 'package:firka/helpers/icon_helper.dart';
|
||||
import 'package:firka/helpers/profile_picture.dart';
|
||||
import 'package:firka/main.dart';
|
||||
import 'package:firka/data/models/token_model.dart';
|
||||
import 'package:firka/core/extensions.dart';
|
||||
import 'package:firka/core/icon_helper.dart';
|
||||
import 'package:firka/core/profile_picture.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/ui/phone/screens/login/login_screen.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
|
||||
import '../../../../helpers/debug_helper.dart';
|
||||
import '../../../../helpers/firka_bundle.dart';
|
||||
import '../../../../helpers/firka_state.dart';
|
||||
import '../../../widget/firka_icon.dart';
|
||||
import 'package:firka/core/debug_helper.dart';
|
||||
import 'package:firka/core/firka_bundle.dart';
|
||||
import 'package:firka/core/state/firka_state.dart';
|
||||
import 'package:firka/ui/shared/firka_icon.dart';
|
||||
|
||||
class DebugScreen extends StatefulWidget {
|
||||
final AppInitialization data;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:firka/helpers/db/models/app_settings_model.dart';
|
||||
import 'package:firka/helpers/settings.dart';
|
||||
import 'package:firka/helpers/ui/firka_button.dart';
|
||||
import 'package:firka/ui/model/style.dart';
|
||||
import 'package:firka/data/models/app_settings_model.dart';
|
||||
import 'package:firka/core/settings.dart';
|
||||
import 'package:firka/ui/components/firka_button.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:firka/ui/phone/screens/home/home_screen.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../helpers/firka_state.dart';
|
||||
import '../../../../main.dart';
|
||||
import 'package:firka/core/state/firka_state.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
|
||||
class BetaScreen extends StatefulWidget {
|
||||
final AppInitialization data;
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:firka/helpers/api/client/kreta_client.dart';
|
||||
import 'package:firka/helpers/api/client/kreta_stream.dart';
|
||||
import 'package:firka/helpers/api/exceptions/token.dart';
|
||||
import 'package:firka/helpers/active_account_helper.dart';
|
||||
import 'package:firka/helpers/extensions.dart';
|
||||
import 'package:firka/helpers/live_activity_service.dart';
|
||||
import 'package:firka/helpers/settings.dart';
|
||||
import 'package:firka/helpers/update_notifier.dart';
|
||||
import 'package:firka/helpers/watch_sync_helper.dart';
|
||||
import 'package:firka/main.dart';
|
||||
import 'package:firka/ui/model/style.dart';
|
||||
import 'package:firka/api/client/kreta_client.dart';
|
||||
import 'package:firka/api/client/kreta_stream.dart';
|
||||
import 'package:firka/api/exceptions/token.dart';
|
||||
import 'package:firka/services/active_account_helper.dart';
|
||||
import 'package:firka/core/extensions.dart';
|
||||
import 'package:firka/services/live_activity_service.dart';
|
||||
import 'package:firka/core/settings.dart';
|
||||
import 'package:firka/core/state/update_notifier.dart';
|
||||
import 'package:firka/services/watch_sync_helper.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:firka/ui/phone/pages/extras/main_wear_pair.dart';
|
||||
import 'package:firka/ui/phone/pages/extras/reauth_toast.dart';
|
||||
import 'package:firka/ui/phone/pages/home/home_grades.dart';
|
||||
@@ -26,13 +26,13 @@ import 'package:home_widget/home_widget.dart';
|
||||
import 'package:majesticons_flutter/majesticons_flutter.dart';
|
||||
import 'package:smart_scroll/smart_scroll.dart';
|
||||
|
||||
import '../../../../helpers/db/widget.dart';
|
||||
import '../../../../helpers/debug_helper.dart';
|
||||
import '../../../../helpers/firka_bundle.dart';
|
||||
import '../../../../helpers/firka_state.dart';
|
||||
import '../../../../helpers/image_preloader.dart';
|
||||
import '../../../widget/delayed_spinner.dart';
|
||||
import '../../../widget/firka_icon.dart';
|
||||
import 'package:firka/data/widget.dart';
|
||||
import 'package:firka/core/debug_helper.dart';
|
||||
import 'package:firka/core/firka_bundle.dart';
|
||||
import 'package:firka/core/state/firka_state.dart';
|
||||
import 'package:firka/core/image_preloader.dart';
|
||||
import 'package:firka/ui/shared/delayed_spinner.dart';
|
||||
import 'package:firka/ui/shared/firka_icon.dart';
|
||||
import '../../pages/extras/extras.dart';
|
||||
import '../../pages/extras/main_error.dart';
|
||||
import '../../pages/home/home_grades_subject.dart';
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import 'package:firka/main.dart';
|
||||
import 'package:firka/ui/model/style.dart';
|
||||
import 'package:firka/ui/widget/firka_icon.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:firka/ui/shared/firka_icon.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:majesticons_flutter/majesticons_flutter.dart';
|
||||
|
||||
import '../../../../helpers/firka_state.dart';
|
||||
import 'package:firka/core/state/firka_state.dart';
|
||||
|
||||
class FullPrivacyPolicyScreen extends StatefulWidget {
|
||||
final AppInitialization data;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:firka/helpers/ui/firka_button.dart';
|
||||
import 'package:firka/helpers/ui/firka_card.dart';
|
||||
import 'package:firka/main.dart';
|
||||
import 'package:firka/ui/model/style.dart';
|
||||
import 'package:firka/ui/components/firka_button.dart';
|
||||
import 'package:firka/ui/components/firka_card.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:firka/ui/phone/screens/live_activity/full_privacy_policy_screen.dart';
|
||||
import 'package:firka/ui/widget/firka_icon.dart';
|
||||
import 'package:firka/ui/shared/firka_icon.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:majesticons_flutter/majesticons_flutter.dart';
|
||||
|
||||
import '../../../../helpers/firka_state.dart';
|
||||
import 'package:firka/core/state/firka_state.dart';
|
||||
|
||||
class LiveActivityConsentScreen extends StatefulWidget {
|
||||
final AppInitialization data;
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:carousel_slider/carousel_slider.dart';
|
||||
import 'package:firka/helpers/firka_bundle.dart';
|
||||
import 'package:firka/main.dart';
|
||||
import 'package:firka/core/firka_bundle.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/ui/phone/widgets/login_webview.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import '../../../../helpers/firka_state.dart';
|
||||
import '../../../../helpers/image_preloader.dart';
|
||||
import '../../../model/style.dart';
|
||||
import '../../../widget/delayed_spinner.dart';
|
||||
import 'package:firka/core/state/firka_state.dart';
|
||||
import 'package:firka/core/image_preloader.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:firka/ui/shared/delayed_spinner.dart';
|
||||
|
||||
// TODO: Replace these with actual privacy policy URLs
|
||||
const String _privacyUrlHungarian =
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import 'package:firka/helpers/extensions.dart';
|
||||
import 'package:firka/main.dart';
|
||||
import 'package:firka/core/extensions.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:majesticons_flutter/majesticons_flutter.dart';
|
||||
|
||||
import '../../../../helpers/api/model/notice_board.dart';
|
||||
import '../../../../helpers/firka_bundle.dart';
|
||||
import '../../../model/style.dart';
|
||||
import '../../../widget/firka_icon.dart';
|
||||
import 'package:firka/api/model/notice_board.dart';
|
||||
import 'package:firka/core/firka_bundle.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:firka/ui/shared/firka_icon.dart';
|
||||
|
||||
class MessageScreen extends StatelessWidget {
|
||||
final AppInitialization data;
|
||||
|
||||
@@ -2,15 +2,15 @@ import 'dart:collection';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart';
|
||||
import 'package:firka/helpers/db/models/app_settings_model.dart';
|
||||
import 'package:firka/helpers/db/models/token_model.dart';
|
||||
import 'package:firka/helpers/image_preloader.dart';
|
||||
import 'package:firka/helpers/ui/firka_button.dart';
|
||||
import 'package:firka/helpers/ui/firka_card.dart';
|
||||
import 'package:firka/main.dart';
|
||||
import 'package:firka/ui/model/style.dart';
|
||||
import 'package:firka/data/models/app_settings_model.dart';
|
||||
import 'package:firka/data/models/token_model.dart';
|
||||
import 'package:firka/core/image_preloader.dart';
|
||||
import 'package:firka/ui/components/firka_button.dart';
|
||||
import 'package:firka/ui/components/firka_card.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:firka/ui/phone/screens/login/login_screen.dart';
|
||||
import 'package:firka/ui/widget/firka_icon.dart';
|
||||
import 'package:firka/ui/shared/firka_icon.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -20,13 +20,14 @@ import 'package:path/path.dart' as p;
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
import '../../../../helpers/db/widget.dart';
|
||||
import '../../../../helpers/firka_bundle.dart';
|
||||
import '../../../../helpers/firka_state.dart';
|
||||
import '../../../../helpers/api/client/kreta_client.dart';
|
||||
import '../../../../helpers/settings.dart';
|
||||
import '../../../../helpers/live_activity_service.dart';
|
||||
import '../../../../helpers/watch_sync_helper.dart';
|
||||
import 'package:firka/data/widget.dart';
|
||||
import 'package:firka/core/firka_bundle.dart';
|
||||
import 'package:firka/app/initialization_screen.dart';
|
||||
import 'package:firka/core/state/firka_state.dart';
|
||||
import 'package:firka/api/client/kreta_client.dart';
|
||||
import 'package:firka/core/settings.dart';
|
||||
import 'package:firka/services/live_activity_service.dart';
|
||||
import 'package:firka/services/watch_sync_helper.dart';
|
||||
import '../../widgets/login_webview.dart';
|
||||
|
||||
class SettingsScreen extends StatefulWidget {
|
||||
|
||||
@@ -2,8 +2,8 @@ import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../model/style.dart';
|
||||
import '../../widget/firka_icon.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:firka/ui/shared/firka_icon.dart';
|
||||
|
||||
class BottomNavIconWidget extends StatelessWidget {
|
||||
final void Function() onTap;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:firka/helpers/extensions.dart';
|
||||
import 'package:firka/core/extensions.dart';
|
||||
import 'package:firka/l10n/app_localizations.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../model/style.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
|
||||
class BottomTimeTableNavIconWidget extends StatelessWidget {
|
||||
final AppLocalizations l10n;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:firka/ui/widget/firka_icon.dart';
|
||||
import 'package:firka/ui/shared/firka_icon.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:majesticons_flutter/majesticons_flutter.dart';
|
||||
|
||||
import '../../model/style.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
|
||||
class BubbleTest extends StatelessWidget {
|
||||
const BubbleTest({super.key});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:firka/helpers/api/model/grade.dart';
|
||||
import 'package:firka/helpers/ui/grade_helpers.dart';
|
||||
import 'package:firka/ui/model/style.dart';
|
||||
import 'package:firka/api/model/grade.dart';
|
||||
import 'package:firka/ui/components/grade_helpers.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import 'package:firka/helpers/ui/firka_card.dart';
|
||||
import 'package:firka/ui/components/firka_card.dart';
|
||||
import 'package:firka/l10n/app_localizations.dart';
|
||||
import 'package:firka/ui/model/style.dart';
|
||||
import 'package:firka/ui/widget/counter_digit.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:firka/ui/shared/counter_digit.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../helpers/api/model/timetable.dart';
|
||||
import 'package:firka/api/model/timetable.dart';
|
||||
|
||||
class StartingSoonWidget extends StatelessWidget {
|
||||
final AppLocalizations l10n;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import 'package:confetti/confetti.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:firka/helpers/extensions.dart';
|
||||
import 'package:firka/core/extensions.dart';
|
||||
import 'package:firka/l10n/app_localizations.dart';
|
||||
import 'package:firka/ui/widget/firka_icon.dart';
|
||||
import 'package:firka/ui/shared/firka_icon.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:majesticons_flutter/majesticons_flutter.dart';
|
||||
|
||||
import '../../../helpers/api/model/student.dart';
|
||||
import '../../../helpers/api/model/timetable.dart';
|
||||
import '../../model/style.dart';
|
||||
import 'package:firka/api/model/student.dart';
|
||||
import 'package:firka/api/model/timetable.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
|
||||
class WelcomeWidget extends StatefulWidget {
|
||||
final AppLocalizations l10n;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import 'package:firka/helpers/api/model/homework.dart';
|
||||
import 'package:firka/helpers/db/models/homework_cache_model.dart';
|
||||
import 'package:firka/helpers/ui/common_bottom_sheets.dart';
|
||||
import 'package:firka/helpers/ui/firka_card.dart';
|
||||
import 'package:firka/main.dart';
|
||||
import 'package:firka/ui/model/style.dart';
|
||||
import 'package:firka/ui/widget/firka_icon.dart';
|
||||
import 'package:firka/api/model/homework.dart';
|
||||
import 'package:firka/data/models/homework_cache_model.dart';
|
||||
import 'package:firka/ui/components/common_bottom_sheets.dart';
|
||||
import 'package:firka/ui/components/firka_card.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:firka/ui/shared/firka_icon.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:majesticons_flutter/majesticons_flutter.dart';
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:firka/helpers/ui/firka_card.dart';
|
||||
import 'package:firka/ui/model/style.dart';
|
||||
import 'package:firka/ui/components/firka_card.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../helpers/api/model/notice_board.dart';
|
||||
import 'package:firka/api/model/notice_board.dart';
|
||||
|
||||
// TODO: Finish
|
||||
class InfoBoardItemWidget extends StatelessWidget {
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import 'package:firka/helpers/extensions.dart';
|
||||
import 'package:firka/helpers/settings.dart';
|
||||
import 'package:firka/helpers/ui/firka_card.dart';
|
||||
import 'package:firka/main.dart';
|
||||
import 'package:firka/ui/model/style.dart';
|
||||
import 'package:firka/core/extensions.dart';
|
||||
import 'package:firka/core/settings.dart';
|
||||
import 'package:firka/ui/components/firka_card.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:majesticons_flutter/majesticons_flutter.dart';
|
||||
|
||||
import '../../../helpers/api/model/test.dart';
|
||||
import '../../../helpers/api/model/timetable.dart';
|
||||
import '../../../helpers/debug_helper.dart';
|
||||
import '../../../helpers/ui/common_bottom_sheets.dart';
|
||||
import '../../widget/class_icon.dart';
|
||||
import '../../widget/firka_icon.dart';
|
||||
import 'package:firka/api/model/test.dart';
|
||||
import 'package:firka/api/model/timetable.dart';
|
||||
import 'package:firka/core/debug_helper.dart';
|
||||
import 'package:firka/ui/components/common_bottom_sheets.dart';
|
||||
import 'package:firka/ui/shared/class_icon.dart';
|
||||
import 'package:firka/ui/shared/firka_icon.dart';
|
||||
import 'bubble_test.dart';
|
||||
|
||||
class LessonWidget extends StatelessWidget {
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import 'package:firka/helpers/api/model/test.dart';
|
||||
import 'package:firka/helpers/extensions.dart';
|
||||
import 'package:firka/helpers/ui/firka_card.dart';
|
||||
import 'package:firka/api/model/test.dart';
|
||||
import 'package:firka/core/extensions.dart';
|
||||
import 'package:firka/ui/components/firka_card.dart';
|
||||
import 'package:firka/l10n/app_localizations.dart';
|
||||
import 'package:firka/ui/model/style.dart';
|
||||
import 'package:firka/ui/widget/firka_icon.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:firka/ui/shared/firka_icon.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:majesticons_flutter/majesticons_flutter.dart';
|
||||
|
||||
import '../../../helpers/api/model/timetable.dart';
|
||||
import '../../widget/class_icon.dart';
|
||||
import 'package:firka/api/model/timetable.dart';
|
||||
import 'package:firka/ui/shared/class_icon.dart';
|
||||
|
||||
class LessonBigWidget extends StatelessWidget {
|
||||
final AppLocalizations l10n;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import 'package:firka/helpers/extensions.dart';
|
||||
import 'package:firka/helpers/ui/firka_card.dart';
|
||||
import 'package:firka/core/extensions.dart';
|
||||
import 'package:firka/ui/components/firka_card.dart';
|
||||
import 'package:firka/l10n/app_localizations.dart';
|
||||
import 'package:firka/ui/model/style.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../helpers/api/model/timetable.dart';
|
||||
import '../../widget/class_icon.dart';
|
||||
import 'package:firka/api/model/timetable.dart';
|
||||
import 'package:firka/ui/shared/class_icon.dart';
|
||||
|
||||
class LessonSmallWidget extends StatelessWidget {
|
||||
final AppLocalizations l10n;
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:firka/helpers/db/models/app_settings_model.dart';
|
||||
import 'package:firka/helpers/live_activity_service.dart';
|
||||
import 'package:firka/main.dart';
|
||||
import 'package:firka/data/models/app_settings_model.dart';
|
||||
import 'package:firka/services/live_activity_service.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:isar_community/isar.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
import '../../../helpers/api/client/kreta_client.dart';
|
||||
import '../../../helpers/watch_sync_helper.dart';
|
||||
import '../../../helpers/api/consts.dart';
|
||||
import '../../../helpers/api/token_grant.dart';
|
||||
import '../../../helpers/db/models/token_model.dart';
|
||||
import '../../../helpers/firka_bundle.dart';
|
||||
import '../../../helpers/firka_state.dart';
|
||||
import '../../../helpers/settings.dart';
|
||||
import '../../../ui/model/style.dart';
|
||||
import 'package:firka/api/client/kreta_client.dart';
|
||||
import 'package:firka/services/watch_sync_helper.dart';
|
||||
import 'package:firka/api/consts.dart';
|
||||
import 'package:firka/api/token_grant.dart';
|
||||
import 'package:firka/data/models/token_model.dart';
|
||||
import 'package:firka/core/firka_bundle.dart';
|
||||
import 'package:firka/app/initialization_screen.dart';
|
||||
import 'package:firka/core/state/firka_state.dart';
|
||||
import 'package:firka/core/settings.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import '../pages/error/error_page.dart';
|
||||
|
||||
class LoginWebviewWidget extends StatefulWidget {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:firka/helpers/ui/firka_card.dart';
|
||||
import 'package:firka/ui/components/firka_card.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../helpers/api/model/notice_board.dart';
|
||||
import 'package:firka/api/model/notice_board.dart';
|
||||
|
||||
// TODO: Finish
|
||||
class NoticeBoardItemWidget extends StatelessWidget {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:firka/helpers/api/model/test.dart';
|
||||
import 'package:firka/helpers/api/model/timetable.dart';
|
||||
import 'package:firka/helpers/extensions.dart';
|
||||
import 'package:firka/helpers/ui/firka_card.dart';
|
||||
import 'package:firka/main.dart';
|
||||
import 'package:firka/ui/model/style.dart';
|
||||
import 'package:firka/api/model/test.dart';
|
||||
import 'package:firka/api/model/timetable.dart';
|
||||
import 'package:firka/core/extensions.dart';
|
||||
import 'package:firka/ui/components/firka_card.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:firka/helpers/icon_helper.dart';
|
||||
import 'package:firka/core/icon_helper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'firka_icon.dart';
|
||||
import 'package:firka/ui/shared/firka_icon.dart';
|
||||
|
||||
class ClassIconWidget extends StatelessWidget {
|
||||
final String _uid;
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:firka/ui/model/style.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CounterDigitWidget extends StatelessWidget {
|
||||
@@ -2,8 +2,8 @@ import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../helpers/firka_state.dart';
|
||||
import '../model/style.dart';
|
||||
import 'package:firka/core/state/firka_state.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
|
||||
class DelayedSpinnerWidget extends StatefulWidget {
|
||||
const DelayedSpinnerWidget({super.key});
|
||||
@@ -1,11 +1,11 @@
|
||||
import 'package:firka/helpers/api/model/grade.dart';
|
||||
import 'package:firka/helpers/api/model/subject.dart';
|
||||
import 'package:firka/helpers/ui/firka_card.dart';
|
||||
import 'package:firka/helpers/ui/grade_helpers.dart';
|
||||
import 'package:firka/ui/widget/class_icon.dart';
|
||||
import 'package:firka/api/model/grade.dart';
|
||||
import 'package:firka/api/model/subject.dart';
|
||||
import 'package:firka/ui/components/firka_card.dart';
|
||||
import 'package:firka/ui/components/grade_helpers.dart';
|
||||
import 'package:firka/ui/shared/class_icon.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../model/style.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
|
||||
class GradeSmallCard extends FirkaCard {
|
||||
final List<Grade> grades;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user