diff --git a/.metadata b/.metadata index 8973d6c..b70aa44 100644 --- a/.metadata +++ b/.metadata @@ -4,8 +4,8 @@ # This file should be version controlled and should not be manually edited. version: - revision: "761747bfc538b5af34aa0d3fac380f1bc331ec49" - channel: "stable" + revision: "3e493a3e4d0a5c99fa7da51faae354e95a9a1abe" + channel: "beta" project_type: app @@ -13,11 +13,11 @@ project_type: app migration: platforms: - platform: root - create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 - base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 - - platform: android - create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 - base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + create_revision: 3e493a3e4d0a5c99fa7da51faae354e95a9a1abe + base_revision: 3e493a3e4d0a5c99fa7da51faae354e95a9a1abe + - platform: web + create_revision: 3e493a3e4d0a5c99fa7da51faae354e95a9a1abe + base_revision: 3e493a3e4d0a5c99fa7da51faae354e95a9a1abe # User provided section diff --git a/README.md b/README.md index 02b65df..51d9e51 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,215 @@ -# mnemo_cards +# Mnemo Cards - Мобильное приложение для изучения языков -Mnemo +Полнофункциональная система для изучения языков, включающая мобильное приложение, веб-сервер, телеграм-бот и веб-игры. -## Getting Started +## 🏗️ Структура проекта -This project is a starting point for a Flutter application. +Проект состоит из нескольких взаимосвязанных компонентов: -A few resources to get you started if this is your first Flutter project: +### 📱 Мобильное приложение (`mnemo_cards/`) +Основное Flutter приложение для iOS и Android с функциями: +- Изучение языков через карточки +- Интеграция с Firebase (Firestore, Analytics, Crashlytics) +- Система покупок (YooKassa, In-App Purchase) +- Реклама (Yandex Mobile Ads) +- Локальные уведомления +- Интеграция с веб-играми через WebView +- Поддержка Telegram Web App -- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) +**Технологии:** Flutter, BLoC, Auto Route, Firebase, WebView -For help getting started with Flutter development, view the -[online documentation](https://docs.flutter.dev/), which offers tutorials, -samples, guidance on mobile development, and a full API reference. +### 🌐 Веб-приложение (`mnemo_cards_web/`) +Веб-версия Flutter приложения с функциями: +- Изучение языков через карточки в браузере +- Интеграция с Firebase (Firestore, Analytics, Crashlytics) +- Адаптивный дизайн для различных устройств +- Синхронизация данных с мобильным приложением +- Поддержка PWA (Progressive Web App) +- Интеграция с веб-играми + +**Технологии:** Flutter Web, BLoC, Auto Route, Firebase, PWA + +### 🔧 Общие компоненты (`mnemo_cards_common/`) +Общие DTO и утилиты для мобильного приложения: +- Модели данных +- Сетевые утилиты +- JSON сериализация +- Криптографические функции + +### 🖥️ Backend сервер (`mnemo_cards_backend/`) +Dart сервер на базе Shelf framework: +- REST API для мобильного приложения +- База данных Isar +- Интеграция с Google APIs +- Система платежей YooKassa +- Периодические задачи +- Swagger документация + +**Технологии:** Dart, Shelf, Isar, Google APIs + +### 🔧 Backend общие компоненты (`mnemo_cards_common_backend/`) +Общие компоненты для backend: +- Модели данных +- Утилиты для работы с Isar +- Общие бизнес-логика + +### 🤖 Telegram бот (`mnemo_cards_telegram_bot/`) +Telegram бот для управления приложением: +- Административные функции +- Распространение APK файлов +- Управление пользователями +- Интеграция с основным backend + +**Технологии:** Dart, Teledart, Isar + +### 🎮 Веб-игры и WebView Bridge (`games/`) +Система веб-игр с безопасной коммуникацией через WebView: +- **Host приложение** - Flutter приложение-контейнер +- **Web приложения** - Flutter Web игры (bus_word_game, web_app1) +- **Bridge Core** - система безопасной коммуникации +- **Payloads** - типизированные сообщения между приложениями + +**Архитектура:** +- Изолированные payload'ы для каждого приложения +- JavaScript bridge для WebView коммуникации +- Типобезопасная обработка сообщений +- Поддержка сложных сценариев (авторизация, quiz, нативные диалоги) + +**Технологии:** Flutter Web, WebView, JavaScript Bridge, Melos + +## 🚀 Быстрый старт + +### Мобильное приложение +```bash +cd mnemo_cards +flutter pub get +flutter run +``` + +### Веб-приложение +```bash +cd mnemo_cards_web +flutter pub get +flutter run -d chrome +``` + +### Backend сервер +```bash +cd mnemo_cards_backend +dart pub get +dart run +``` + +### Telegram бот +```bash +cd mnemo_cards_telegram_bot +dart pub get +dart run +``` + +### Веб-игры +```bash +cd games +melos bootstrap +cd apps/host_app +flutter run +``` + +## 🔗 Зависимости между компонентами + +``` +mnemo_cards +├── mnemo_cards_common (DTO) +├── games/mnemo_cards_web_bridge (WebView интеграция) +└── Firebase/Backend API + +mnemo_cards_web +├── mnemo_cards_common (DTO) +├── mnemo_cards_frontend_common (общие компоненты) +├── games/payloads_shared (общие типы) +└── Firebase/Backend API + +mnemo_cards_backend +├── mnemo_cards_common (общие DTO) +├── mnemo_cards_common_backend (backend утилиты) +└── Isar Database + +mnemo_cards_telegram_bot +├── mnemo_cards_common_backend +└── Backend API + +games/ +├── bridge_core (WebView коммуникация) +├── payloads_shared (общие типы) +└── apps/ (веб-приложения) +``` + +## 📋 Основные функции + +### Мобильное приложение +- 📚 Изучение языков через карточки +- 🎮 Интеграция с веб-играми +- 💳 Система покупок и подписок +- 📊 Аналитика и статистика +- 🔔 Push уведомления +- 🌐 Telegram Web App поддержка + +### Веб-приложение +- 📚 Изучение языков через карточки в браузере +- 🎮 Интеграция с веб-играми +- 💳 Система покупок и подписок +- 📊 Аналитика и статистика +- 🔄 Синхронизация с мобильным приложением +- 📱 PWA поддержка для установки на устройство + +### Backend +- 🔐 Аутентификация и авторизация +- 💾 Управление данными пользователей +- 💰 Обработка платежей +- 📈 Аналитика и метрики +- 🔄 Периодические задачи +- 📚 API для мобильного приложения + +### Telegram бот +- 👨‍💼 Административные функции +- 📱 Распространение APK +- 👥 Управление пользователями +- 📊 Мониторинг системы + +### Веб-игры +- 🎯 Интерактивные игры для изучения языков +- 🔗 Безопасная интеграция с мобильным приложением +- 📊 Отслеживание прогресса +- 🎨 Современный UI/UX + +## 🛠️ Технологический стек + +- **Frontend:** Flutter, Flutter Web +- **Backend:** Dart, Shelf, Isar +- **База данных:** Isar (локальная), Firestore (облачная) +- **Аутентификация:** Firebase Auth, Google Sign-In +- **Платежи:** YooKassa, In-App Purchase +- **Аналитика:** Firebase Analytics, Crashlytics +- **Реклама:** Yandex Mobile Ads +- **Уведомления:** Firebase Messaging, Local Notifications +- **Коммуникация:** WebView Bridge, REST API +- **Мониторинг:** Telegram Bot + +## 📱 Поддерживаемые платформы + +- **iOS** - нативное приложение +- **Android** - нативное приложение +- **Web** - Flutter Web приложение и игры +- **Telegram** - Web App интеграция + +## 🔒 Безопасность + +- Шифрование данных +- Безопасная WebView коммуникация +- Защита от root/jailbreak +- Валидация платежей +- Изоляция payload'ов в веб-играх + +## 📄 Лицензия + +Проект является приватным и не предназначен для публикации. diff --git a/analysis_options.yaml b/analysis_options.yaml deleted file mode 100644 index 0cdae21..0000000 --- a/analysis_options.yaml +++ /dev/null @@ -1,9 +0,0 @@ -analyzer: - errors: - todo: info # чтобы отображать тудушки -# unused_import: error # Лишние импорты в проекте не нужны -# unused_local_variable: error # Неиспользуемые переменные в проекте не нужны -# missing_required_param: error # Не пропускаем обязательные параметры -# prefer_relative_imports: error # Относительные импорты в проекте -# directives_ordering: error # Следим за порядком импортов в проекте - unawaited_futures: error # Всегда резолвим Future \ No newline at end of file diff --git a/android/.gitignore b/android/.gitignore deleted file mode 100644 index 6f56801..0000000 --- a/android/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -gradle-wrapper.jar -/.gradle -/captures/ -/gradlew -/gradlew.bat -/local.properties -GeneratedPluginRegistrant.java - -# Remember to never publicly share your keystore. -# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app -key.properties -**/*.keystore -**/*.jks diff --git a/android/app/build.gradle b/android/app/build.gradle deleted file mode 100644 index 4540827..0000000 --- a/android/app/build.gradle +++ /dev/null @@ -1,83 +0,0 @@ -plugins { - id "com.android.application" - // START: FlutterFire Configuration - id 'com.google.gms.google-services' - id 'com.google.firebase.crashlytics' - // END: FlutterFire Configuration - id "kotlin-android" - // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. - id "dev.flutter.flutter-gradle-plugin" -} - -def localProperties = new Properties() -def localPropertiesFile = rootProject.file("local.properties") -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader("UTF-8") { reader -> - localProperties.load(reader) - } -} - -def flutterVersionCode = localProperties.getProperty("flutter.versionCode") -if (flutterVersionCode == null) { - flutterVersionCode = "1" -} - -def flutterVersionName = localProperties.getProperty("flutter.versionName") -if (flutterVersionName == null) { - flutterVersionName = "1.0" -} - -def keystoreProperties = new Properties() -def keystorePropertiesFile = rootProject.file('key.properties') -if (keystorePropertiesFile.exists()) { - keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) -} - -android { - namespace = "com.cinnabarflower.mnemo_cards" - compileSdk = flutter.compileSdkVersion - ndkVersion = flutter.ndkVersion - - compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 - } - - defaultConfig { - applicationId = "com.cinnabarflower.mnemo_cards" - // You can update the following values to match your application needs. - // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. - minSdk = 25 - targetSdk = flutter.targetSdkVersion - versionCode = flutterVersionCode.toInteger() - versionName = flutterVersionName - } - - signingConfigs { - release { - keyAlias keystoreProperties['keyAlias'] - keyPassword keystoreProperties['keyPassword'] - storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null - storePassword keystoreProperties['storePassword'] - } - debug { - keyAlias keystoreProperties['keyAlias'] - keyPassword keystoreProperties['keyPassword'] - storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null - storePassword keystoreProperties['storePassword'] - } - } - - buildTypes { - release { - signingConfig signingConfigs.release - } - debug { - signingConfig signingConfigs.release - } - } -} - -flutter { - source = "../.." -} diff --git a/android/app/google-services.json b/android/app/google-services.json deleted file mode 100644 index 1b69ccc..0000000 --- a/android/app/google-services.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "project_info": { - "project_number": "701767851968", - "project_id": "mnemo-cards", - "storage_bucket": "mnemo-cards.appspot.com" - }, - "client": [ - { - "client_info": { - "mobilesdk_app_id": "1:701767851968:android:6190df55346394732f7225", - "android_client_info": { - "package_name": "com.cinnabarflower.mnemo_cards" - } - }, - "oauth_client": [ - { - "client_id": "701767851968-3jgootslus3ie76t682j4v7glletloud.apps.googleusercontent.com", - "client_type": 1, - "android_info": { - "package_name": "com.cinnabarflower.mnemo_cards", - "certificate_hash": "6df4f259b9c53ac01472c17df69e8c91494f3947" - } - }, - { - "client_id": "701767851968-5e6v0b5humhj0iu22t21nb9dtgt4tjus.apps.googleusercontent.com", - "client_type": 1, - "android_info": { - "package_name": "com.cinnabarflower.mnemo_cards", - "certificate_hash": "49bb5b6863c9e6d8db43e7eb4d54796a300d87fe" - } - }, - { - "client_id": "701767851968-vqvjgf1u79jg924inm25bfuv4dg41t2u.apps.googleusercontent.com", - "client_type": 1, - "android_info": { - "package_name": "com.cinnabarflower.mnemo_cards", - "certificate_hash": "19f45c4abc59f7ce5f22e84376273ee1ee19bdc2" - } - }, - { - "client_id": "701767851968-bud97rud1d9qtqju96addn31nhm0oofu.apps.googleusercontent.com", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "AIzaSyBGn7PVVDX-o7WipivtuBjdoH5nYEPsHms" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "701767851968-bud97rud1d9qtqju96addn31nhm0oofu.apps.googleusercontent.com", - "client_type": 3 - }, - { - "client_id": "701767851968-8dqcmk706p08gujqbl2m9s4sq1aljibs.apps.googleusercontent.com", - "client_type": 2, - "ios_info": { - "bundle_id": "com.cinnabarflower.mnemoCards" - } - } - ] - } - } - } - ], - "configuration_version": "1" -} \ No newline at end of file diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml deleted file mode 100644 index 399f698..0000000 --- a/android/app/src/debug/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index cf05adf..0000000 --- a/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/android/app/src/main/kotlin/com/cinnabarflower/mnemo_cards/MainActivity.kt b/android/app/src/main/kotlin/com/cinnabarflower/mnemo_cards/MainActivity.kt deleted file mode 100644 index 840a9e0..0000000 --- a/android/app/src/main/kotlin/com/cinnabarflower/mnemo_cards/MainActivity.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.cinnabarflower.mnemo_cards - -import io.flutter.embedding.android.FlutterActivity - -class MainActivity: FlutterActivity() diff --git a/android/app/src/main/res/drawable-hdpi/android12splash.png b/android/app/src/main/res/drawable-hdpi/android12splash.png deleted file mode 100644 index a950af6..0000000 Binary files a/android/app/src/main/res/drawable-hdpi/android12splash.png and /dev/null differ diff --git a/android/app/src/main/res/drawable-hdpi/splash.png b/android/app/src/main/res/drawable-hdpi/splash.png deleted file mode 100644 index a950af6..0000000 Binary files a/android/app/src/main/res/drawable-hdpi/splash.png and /dev/null differ diff --git a/android/app/src/main/res/drawable-mdpi/android12splash.png b/android/app/src/main/res/drawable-mdpi/android12splash.png deleted file mode 100644 index b8e69a5..0000000 Binary files a/android/app/src/main/res/drawable-mdpi/android12splash.png and /dev/null differ diff --git a/android/app/src/main/res/drawable-mdpi/splash.png b/android/app/src/main/res/drawable-mdpi/splash.png deleted file mode 100644 index b8e69a5..0000000 Binary files a/android/app/src/main/res/drawable-mdpi/splash.png and /dev/null differ diff --git a/android/app/src/main/res/drawable-night-hdpi/android12splash.png b/android/app/src/main/res/drawable-night-hdpi/android12splash.png deleted file mode 100644 index a950af6..0000000 Binary files a/android/app/src/main/res/drawable-night-hdpi/android12splash.png and /dev/null differ diff --git a/android/app/src/main/res/drawable-night-mdpi/android12splash.png b/android/app/src/main/res/drawable-night-mdpi/android12splash.png deleted file mode 100644 index b8e69a5..0000000 Binary files a/android/app/src/main/res/drawable-night-mdpi/android12splash.png and /dev/null differ diff --git a/android/app/src/main/res/drawable-night-xhdpi/android12splash.png b/android/app/src/main/res/drawable-night-xhdpi/android12splash.png deleted file mode 100644 index aa97e95..0000000 Binary files a/android/app/src/main/res/drawable-night-xhdpi/android12splash.png and /dev/null differ diff --git a/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png b/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png deleted file mode 100644 index 1d18d62..0000000 Binary files a/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png and /dev/null differ diff --git a/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png b/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png deleted file mode 100644 index 6a284fd..0000000 Binary files a/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png and /dev/null differ diff --git a/android/app/src/main/res/drawable-v21/background.png b/android/app/src/main/res/drawable-v21/background.png deleted file mode 100644 index 3107d37..0000000 Binary files a/android/app/src/main/res/drawable-v21/background.png and /dev/null differ diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml deleted file mode 100644 index 3cc4948..0000000 --- a/android/app/src/main/res/drawable-v21/launch_background.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/android/app/src/main/res/drawable-xhdpi/android12splash.png b/android/app/src/main/res/drawable-xhdpi/android12splash.png deleted file mode 100644 index aa97e95..0000000 Binary files a/android/app/src/main/res/drawable-xhdpi/android12splash.png and /dev/null differ diff --git a/android/app/src/main/res/drawable-xhdpi/splash.png b/android/app/src/main/res/drawable-xhdpi/splash.png deleted file mode 100644 index aa97e95..0000000 Binary files a/android/app/src/main/res/drawable-xhdpi/splash.png and /dev/null differ diff --git a/android/app/src/main/res/drawable-xxhdpi/android12splash.png b/android/app/src/main/res/drawable-xxhdpi/android12splash.png deleted file mode 100644 index 1d18d62..0000000 Binary files a/android/app/src/main/res/drawable-xxhdpi/android12splash.png and /dev/null differ diff --git a/android/app/src/main/res/drawable-xxhdpi/splash.png b/android/app/src/main/res/drawable-xxhdpi/splash.png deleted file mode 100644 index 1d18d62..0000000 Binary files a/android/app/src/main/res/drawable-xxhdpi/splash.png and /dev/null differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/android12splash.png b/android/app/src/main/res/drawable-xxxhdpi/android12splash.png deleted file mode 100644 index 6a284fd..0000000 Binary files a/android/app/src/main/res/drawable-xxxhdpi/android12splash.png and /dev/null differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/splash.png b/android/app/src/main/res/drawable-xxxhdpi/splash.png deleted file mode 100644 index 6a284fd..0000000 Binary files a/android/app/src/main/res/drawable-xxxhdpi/splash.png and /dev/null differ diff --git a/android/app/src/main/res/drawable/background.png b/android/app/src/main/res/drawable/background.png deleted file mode 100644 index 3107d37..0000000 Binary files a/android/app/src/main/res/drawable/background.png and /dev/null differ diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml deleted file mode 100644 index 3cc4948..0000000 --- a/android/app/src/main/res/drawable/launch_background.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/android/app/src/main/res/mipmap-hdpi/launcher_icon.png b/android/app/src/main/res/mipmap-hdpi/launcher_icon.png deleted file mode 100644 index 6f591a2..0000000 Binary files a/android/app/src/main/res/mipmap-hdpi/launcher_icon.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-mdpi/launcher_icon.png b/android/app/src/main/res/mipmap-mdpi/launcher_icon.png deleted file mode 100644 index b725052..0000000 Binary files a/android/app/src/main/res/mipmap-mdpi/launcher_icon.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png b/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png deleted file mode 100644 index 07bdd0c..0000000 Binary files a/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png b/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png deleted file mode 100644 index e15861a..0000000 Binary files a/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png b/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png deleted file mode 100644 index 848e129..0000000 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png and /dev/null differ diff --git a/android/app/src/main/res/values-night-v31/styles.xml b/android/app/src/main/res/values-night-v31/styles.xml deleted file mode 100644 index ba4b2ae..0000000 --- a/android/app/src/main/res/values-night-v31/styles.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml deleted file mode 100644 index dbc9ea9..0000000 --- a/android/app/src/main/res/values-night/styles.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - diff --git a/android/app/src/main/res/values-v31/styles.xml b/android/app/src/main/res/values-v31/styles.xml deleted file mode 100644 index e437c38..0000000 --- a/android/app/src/main/res/values-v31/styles.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml deleted file mode 100644 index 0d1fa8f..0000000 --- a/android/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - diff --git a/android/app/src/main/res/xml/network_security_config.xml b/android/app/src/main/res/xml/network_security_config.xml deleted file mode 100644 index 8ec5168..0000000 --- a/android/app/src/main/res/xml/network_security_config.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml deleted file mode 100644 index 399f698..0000000 --- a/android/app/src/profile/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/android/build.gradle b/android/build.gradle deleted file mode 100644 index d2ffbff..0000000 --- a/android/build.gradle +++ /dev/null @@ -1,18 +0,0 @@ -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = "../build" -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(":app") -} - -tasks.register("clean", Delete) { - delete rootProject.buildDir -} diff --git a/android/gradle.properties b/android/gradle.properties deleted file mode 100644 index 3b5b324..0000000 --- a/android/gradle.properties +++ /dev/null @@ -1,3 +0,0 @@ -org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError -android.useAndroidX=true -android.enableJetifier=true diff --git a/android/key.properties b/android/key.properties deleted file mode 100644 index 1dde9f9..0000000 --- a/android/key.properties +++ /dev/null @@ -1,4 +0,0 @@ -storePassword=345234 -keyPassword=345234 -keyAlias=upload -storeFile=../../../../upload-keystore.jks \ No newline at end of file diff --git a/android/pepk.jar b/android/pepk.jar deleted file mode 100644 index 5ffdd4c..0000000 Binary files a/android/pepk.jar and /dev/null differ diff --git a/android/settings.gradle b/android/settings.gradle deleted file mode 100644 index 85ec48a..0000000 --- a/android/settings.gradle +++ /dev/null @@ -1,43 +0,0 @@ -pluginManagement { - def flutterSdkPath = { - def properties = new Properties() - file("local.properties").withInputStream { properties.load(it) } - def flutterSdkPath = properties.getProperty("flutter.sdk") - assert flutterSdkPath != null, "flutter.sdk not set in local.properties" - return flutterSdkPath - }() - - includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") - - repositories { - google() - mavenCentral() - gradlePluginPortal() - } - - // For yoo kassa - buildscript { - repositories { - mavenCentral() - maven { - url = uri("https://storage.googleapis.com/r8-releases/raw") - } - } - dependencies { - classpath("com.android.tools:r8:8.3.37") - } - } -} - -plugins { - id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "7.3.0" apply false - // START: FlutterFire Configuration - id "com.google.gms.google-services" version "4.3.15" apply false - id "com.google.firebase.crashlytics" version "2.9.9" apply false - - // END: FlutterFire Configuration - id "org.jetbrains.kotlin.android" version "1.9.10" apply false -} - -include ":app" diff --git a/build/app/outputs/flutter-apk/app-arm64-v8a-release.apk b/build/app/outputs/flutter-apk/app-arm64-v8a-release.apk deleted file mode 100644 index 7cc6506..0000000 Binary files a/build/app/outputs/flutter-apk/app-arm64-v8a-release.apk and /dev/null differ diff --git a/build_app.sh b/build_app.sh deleted file mode 100644 index 2487f69..0000000 --- a/build_app.sh +++ /dev/null @@ -1,3 +0,0 @@ -set -e -v -cp ~/mnemo_cards/build/app/outputs/flutter-apk/app-arm64-v8a-release.apk ~/mnemo_cards_telegram_bot/apks/admin_app.apk - diff --git a/cerdo_1024x512.png b/cerdo_1024x512.png deleted file mode 100644 index d7f024a..0000000 Binary files a/cerdo_1024x512.png and /dev/null differ diff --git a/cerdo_512.png b/cerdo_512.png deleted file mode 100644 index 12a16b8..0000000 Binary files a/cerdo_512.png and /dev/null differ diff --git a/devtools_options.yaml b/devtools_options.yaml deleted file mode 100644 index fa0b357..0000000 --- a/devtools_options.yaml +++ /dev/null @@ -1,3 +0,0 @@ -description: This file stores settings for Dart & Flutter DevTools. -documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states -extensions: diff --git a/firebase.json b/firebase.json deleted file mode 100644 index 4d12b0d..0000000 --- a/firebase.json +++ /dev/null @@ -1 +0,0 @@ -{"flutter":{"platforms":{"android":{"default":{"projectId":"mnemo-cards","appId":"1:701767851968:android:6190df55346394732f7225","fileOutput":"android/app/google-services.json"}},"ios":{"default":{"projectId":"mnemo-cards","appId":"1:701767851968:ios:5c9040634eac8c152f7225","uploadDebugSymbols":false,"fileOutput":"ios/Runner/GoogleService-Info.plist"}},"dart":{"lib/firebase_options.dart":{"projectId":"mnemo-cards","configurations":{"android":"1:701767851968:android:6190df55346394732f7225","ios":"1:701767851968:ios:5c9040634eac8c152f7225"}}}}}} \ No newline at end of file diff --git a/fonts/Nunito-VariableFont_wght.ttf b/fonts/Nunito-VariableFont_wght.ttf deleted file mode 100644 index 0a00f63..0000000 Binary files a/fonts/Nunito-VariableFont_wght.ttf and /dev/null differ diff --git a/funny_letters/build_web_debug.sh b/funny_letters/build_web_debug.sh new file mode 100644 index 0000000..d43634b --- /dev/null +++ b/funny_letters/build_web_debug.sh @@ -0,0 +1,2 @@ +flutter build web --profile --dart-define=Dart2jsOptimization=O0 + diff --git a/funny_letters/codegen.sh b/funny_letters/codegen.sh new file mode 100644 index 0000000..edcbc79 --- /dev/null +++ b/funny_letters/codegen.sh @@ -0,0 +1 @@ +flutter packages pub run build_runner build --delete-conflicting-outputs --verbose \ No newline at end of file diff --git a/funny_letters/lib/runner/ui/button.dart b/funny_letters/lib/runner/ui/button.dart new file mode 100644 index 0000000..727d798 --- /dev/null +++ b/funny_letters/lib/runner/ui/button.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +const Color borderGray = Color(0xffABABAB); + +class Button extends StatelessWidget { + final String asset; + final VoidCallback onTap; + + const Button(this.asset, this.onTap); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, + child: Container( + width: 110.w, + height: 60.h, + decoration: BoxDecoration( + color: Theme.of(context).scaffoldBackgroundColor, + border: Border.all(color: borderGray), + borderRadius: BorderRadius.circular(16.0), + ), + alignment: Alignment.center, + child: Image( + image: AssetImage(asset), + width: 29.w, + color: Theme.of(context).colorScheme.primary, + fit: BoxFit.scaleDown, + ), + ), + ); + } +} diff --git a/funny_letters/run_web.sh b/funny_letters/run_web.sh new file mode 100644 index 0000000..a363f6c --- /dev/null +++ b/funny_letters/run_web.sh @@ -0,0 +1 @@ +dhttpd --path build/web --host 0.0.0.0 --port 8888 \ No newline at end of file diff --git a/games/ads_example/assets/vast_player_new.html b/games/ads_example/assets/vast_player_new.html new file mode 100644 index 0000000..763f8b4 --- /dev/null +++ b/games/ads_example/assets/vast_player_new.html @@ -0,0 +1,405 @@ + + + + Vast-Player Example + + + + +
+
Загрузка рекламы...
+ +
Пропустить через: 5с
+
+ + + \ No newline at end of file diff --git a/games/mnemo_cards_game_api/PROJECT_STRUCTURE.md b/games/mnemo_cards_game_api/PROJECT_STRUCTURE.md new file mode 100644 index 0000000..d6d8990 --- /dev/null +++ b/games/mnemo_cards_game_api/PROJECT_STRUCTURE.md @@ -0,0 +1,148 @@ +# 📦 Flutter WebView Bridge — структура проекта + +Проект разделён на независимые пакеты (монорепозиторий с Melos), чтобы: + +- отделить зависимости между `host`, `web` и `payloads`; +- изолировать доступ к данным (web видит только нужное); +- упростить масштабирование и тестирование. + +--- + +## 🗂️ Общая структура + +``` +├── games/ +│ ├── bridge_core/ # Общие типы и сериализация +│ ├── payloads_shared/ # Payload'ы, общие для всех +│ ├── payloads_host/ # Только для host-приложения +│ ├── payloads_app1/ # Только для web_app1 +│ ├── host_app/ # Flutter Host App (с WebView) +│ ├── web_app1/ # Flutter Web-приложение 1 +├── melos.yaml # Конфигурация монорепозитория +``` + +--- + +## 📦 Описание пакетов + +### `bridge_core/` + +Содержит: + +- `BridgeMessage` — модель сообщения +- `BridgePayload` — абстракция типа данных +- `PayloadRegistry` — карта `"type"` → `fromJson` + +Используется **всеми** остальными пакетами. + +--- + +### `payloads_shared/` + +Содержит payload'ы, используемые и в `host`, и в web-приложениях. +Пример: `GetUserInfoPayload`, `PingPayload`. + +Импортируется и в `host_app`, и в `web_app1`, `web_app2`. + +--- + +### `payloads_host/` + +Содержит payload'ы, специфичные для хост-приложения. +Пример: `SecretAdminCommand`, `ShowNativeDialogPayload`. + +Импортируется **только в `host_app`**. + +--- + +### `payloads_app1/` + +Содержит payload'ы, специфичные для web-приложения 1. +Пример: `LoginRequestPayload`, `SubmitQuizPayload`. + +Импортируется **только в `web_app1`** и `host_app`. + +--- + +### `host_app/` + +Flutter-приложение с WebView. + +- Имеет доступ ко всем `payloads_*` пакетам +- Обрабатывает все события +- Реализует `BridgeWebViewController` + +--- + +### `web_app1/` + +Flutter Web-приложение, встроенное в WebView. + +- Импортирует только нужные `payloads_*` +- Регистрирует только свои payload'ы +- Не видит лишнего + +--- + +## 🔐 Разделение доступа + +| Пакет | Видит payloads_shared | Видит payloads_host | Видит payloads_app1 | +|---------------|------------------------|----------------------|----------------------| +| host_app | ✅ | ✅ | ✅ | +| web_app1 | ✅ | ❌ | ✅ | +| web_app2 | ✅ | ❌ | ❌ | + +--- + +## ⚙️ Melos + +Файл `melos.yaml` в корне: + +```yaml +name: flutter_webview_bridge_repo +packages: + - packages/** +``` + +Запуск: +```bash +melos bootstrap +``` + +--- + +## 📥 Регистрация payload'ов + +Каждый payload-пакет содержит: + +```dart +void registerPayloads() { + BridgePayload.register('type_name', (json) => MyPayload.fromJson(json)); +} +``` + +В `main()` у приложения: + +```dart +void main() { + registerPayloads(); + runApp(MyApp()); +} +``` + +--- + +## 📌 Пример использования + +```dart +// Web-приложение +GetUserInfoPayload(fields: ['name']).toMessage().sendToHost(); + +// Хост-приложение +hostBridge.registerHandler('get_user_info', (msg) { + final payload = GetUserInfoPayload.fromJson(msg.data); + ... +}); +``` + +--- \ No newline at end of file diff --git a/games/mnemo_cards_game_api/lib/src/app_binder.dart b/games/mnemo_cards_game_api/lib/src/app_binder.dart new file mode 100644 index 0000000..192c694 --- /dev/null +++ b/games/mnemo_cards_game_api/lib/src/app_binder.dart @@ -0,0 +1,25 @@ +import 'package:mnemo_cards_game_api/mnemo_cards_game_api.dart'; + +import 'game_data.dart'; + +abstract class AppBinder { + /// Initialize the JavaScript environment + Future init(); + + /// Dispose the JavaScript environment + Future dispose(); + + Future sendEvent(GameEvent event); + + Future get getGameData; + + set gameData(T settings); + + Stream get gameDataStream; + + set gameSettings(GameSettings settings); + + Future get getGameSettings; + + Stream get gameSettingsStream; +} diff --git a/games/mnemo_cards_game_api/lib/src/app_binder_impl.dart b/games/mnemo_cards_game_api/lib/src/app_binder_impl.dart new file mode 100644 index 0000000..9404633 --- /dev/null +++ b/games/mnemo_cards_game_api/lib/src/app_binder_impl.dart @@ -0,0 +1,268 @@ +// import 'dart:async'; +// import 'dart:convert'; +// import 'dart:convert'; +// import 'dart:typed_data'; +// import 'dart:js_interop' as js; +// +// import 'package:mnemo_cards_game_api/mnemo_cards_game_api.dart'; +// +// import 'app_binder.dart'; +// +// class AppBinderImpl implements AppBinder { +// final _messageHandlers = >{}; +// bool _musicEnabled = true; +// bool _soundEnabled = true; +// bool _isDarkMode = false; +// +// @override +// void initializeJavaScript() { +// print('[AppBinder] Initializing JavaScript environment'); +// +// // Set up the message handler for responses from the host app +// js.context['handleMessageFromHost'] = (String message) { +// print('[AppBinder] Received message from host: $message'); +// try { +// final data = jsonDecode(message); +// final type = data['type'] as String; +// print('[AppBinder] Message type: $type'); +// +// final completer = _messageHandlers[type]; +// if (completer != null) { +// print('[AppBinder] Found handler for message type: $type'); +// _messageHandlers.remove(type); +// completer.complete(data); +// } else { +// print( +// '[AppBinder] Warning: No handler found for message type: $type', +// ); +// } +// } catch (e) { +// print('[AppBinder] Error handling message from host: $e'); +// print('[AppBinder] Message was: $message'); +// } +// }; +// +// // Define the function that the host app will inject +// if (js.context['sendMessageToHost'] == null) { +// print('[AppBinder] Setting up default sendMessageToHost'); +// js.context['sendMessageToHost'] = (String message) { +// print( +// '[AppBinder] Warning: Using default sendMessageToHost implementation', +// ); +// print('[AppBinder] Message would have been: $message'); +// }; +// } else { +// print('[AppBinder] sendMessageToHost is already defined by host app'); +// } +// } +// +// Future _sendMessage(String type, Map data) async { +// print('[AppBinder] Sending message type: $type'); +// print('[AppBinder] Message data: $data'); +// +// final completer = Completer(); +// _messageHandlers[type] = completer; +// +// final request = jsonEncode({'type': type, 'data': data}); +// +// print('[AppBinder] Sending request to host: $request'); +// +// // Send message to host app +// js.context.callMethod('sendMessageToHost', [request]); +// +// print('[AppBinder] Waiting for response from host...'); +// final response = await completer.future; +// print('[AppBinder] Received response: $response'); +// +// return response; +// } +// +// @override +// Future playWord(String word, {String? lang}) async { +// if (!_soundEnabled) { +// print('[AppBinder] Sound is disabled, skipping word playback'); +// return; +// } +// +// print('[AppBinder] Playing word: $word, language: $lang'); +// try { +// final Map args = { +// 'word': word, +// if (lang != null) 'lang': lang, +// }; +// await _sendMessage('playWord', args); +// print('[AppBinder] Successfully played word: $word'); +// } catch (e) { +// print('[AppBinder] Error playing word: $e'); +// rethrow; +// } +// } +// +// @override +// Future loadFileBytes(String path) async { +// print('[AppBinder] Loading file bytes from: $path'); +// try { +// final result = await _sendMessage('loadFileBytes', {'path': path}); +// +// if (result['error'] != null) { +// print('[AppBinder] Error loading file bytes: ${result['error']}'); +// throw Exception(result['error']); +// } +// +// final bytes = base64Decode(result['data']); +// print('[AppBinder] Successfully loaded ${bytes.length} bytes'); +// return bytes; +// } catch (e) { +// print('[AppBinder] Error loading file bytes: $e'); +// rethrow; +// } +// } +// +// @override +// Future getCustomBackground(String word) async { +// print('[AppBinder] Getting custom background for word: $word'); +// try { +// final result = await _sendMessage('getCustomBackground', {'word': word}); +// final background = result['data'] as String?; +// print('[AppBinder] Custom background result: $background'); +// return background; +// } catch (e) { +// print('[AppBinder] Error getting custom background: $e'); +// rethrow; +// } +// } +// +// @override +// Future loadSettings() async { +// print('[AppBinder] Loading settings'); +// try { +// final result = await _sendMessage('loadSettings', {}); +// +// if (result['error'] != null) { +// print('[AppBinder] Error in settings response: ${result['error']}'); +// throw Exception(result['error']); +// } +// +// final data = result['data'] as Map; +// print('[AppBinder] Received settings: $data'); +// +// // Update local states +// _musicEnabled = data['musicEnabled'] ?? true; +// _soundEnabled = data['soundEnabled'] ?? true; +// _isDarkMode = data['isDarkMode'] ?? false; +// +// return GameSettings.fromJson(data); +// } catch (e) { +// print('[AppBinder] Error loading settings: $e'); +// rethrow; +// } +// } +// +// @override +// Future> loadWordsWithTranslations() async { +// print('[AppBinder] Loading words with translations'); +// try { +// final result = await _sendMessage('loadWordsWithTranslations', {}); +// +// if (result['error'] != null) { +// print('[AppBinder] Error in words response: ${result['error']}'); +// throw Exception(result['error']); +// } +// +// final List data = result['data'] as List; +// print('[AppBinder] Received ${data.length} words'); +// final words = data +// .map( +// (item) => +// WordWithTranslation.fromJson(item as Map), +// ) +// .toList(); +// print('[AppBinder] Words loaded: ${words.map((w) => w.word).join(', ')}'); +// return words; +// } catch (e) { +// print('[AppBinder] Error loading words with translations: $e'); +// rethrow; +// } +// } +// +// @override +// Future setMusicEnabled(bool enabled) async { +// print('[AppBinder] Setting music enabled: $enabled'); +// try { +// await _sendMessage('setMusicEnabled', {'enabled': enabled}); +// _musicEnabled = enabled; +// print('[AppBinder] Successfully set music enabled: $enabled'); +// } catch (e) { +// print('[AppBinder] Error setting music enabled: $e'); +// rethrow; +// } +// } +// +// @override +// Future setSoundEnabled(bool enabled) async { +// print('[AppBinder] Setting sound enabled: $enabled'); +// try { +// await _sendMessage('setSoundEnabled', {'enabled': enabled}); +// _soundEnabled = enabled; +// print('[AppBinder] Successfully set sound enabled: $enabled'); +// } catch (e) { +// print('[AppBinder] Error setting sound enabled: $e'); +// rethrow; +// } +// } +// +// @override +// Future isMusicEnabled() async { +// print('[AppBinder] Getting music enabled state'); +// try { +// final result = await _sendMessage('getMusicEnabled', {}); +// _musicEnabled = result['data']['enabled'] as bool; +// print('[AppBinder] Music enabled: $_musicEnabled'); +// return _musicEnabled; +// } catch (e) { +// print('[AppBinder] Error getting music enabled state: $e'); +// return _musicEnabled; // Return cached value on error +// } +// } +// +// @override +// Future isSoundEnabled() async { +// print('[AppBinder] Getting sound enabled state'); +// try { +// final result = await _sendMessage('getSoundEnabled', {}); +// _soundEnabled = result['data']['enabled'] as bool; +// print('[AppBinder] Sound enabled: $_soundEnabled'); +// return _soundEnabled; +// } catch (e) { +// print('[AppBinder] Error getting sound enabled state: $e'); +// return _soundEnabled; // Return cached value on error +// } +// } +// +// @override +// Future setDarkMode(bool enabled) async { +// print('[AppBinder] Setting dark mode: $enabled'); +// try { +// await _sendMessage('setDarkMode', {'enabled': enabled}); +// _isDarkMode = enabled; +// print('[AppBinder] Successfully set dark mode: $enabled'); +// } catch (e) { +// print('[AppBinder] Error setting dark mode: $e'); +// rethrow; +// } +// } +// +// @override +// Future isDarkMode() async { +// print('[AppBinder] Getting dark mode state'); +// try { +// final result = await _sendMessage('getDarkMode', {}); +// _isDarkMode = result['data']['enabled'] as bool; +// print('[AppBinder] Dark mode: $_isDarkMode'); +// return _isDarkMode; +// } catch (e) { +// print('[AppBinder] Error getting dark mode state: $e'); +// return _isDarkMode; // Return cached value on error +// } +// } +// } diff --git a/games/mnemo_cards_game_api/lib/src/funny_letters_game_data.dart b/games/mnemo_cards_game_api/lib/src/funny_letters_game_data.dart new file mode 100644 index 0000000..47471ab --- /dev/null +++ b/games/mnemo_cards_game_api/lib/src/funny_letters_game_data.dart @@ -0,0 +1,5 @@ +import 'package:mnemo_cards_game_api/src/game_data.dart'; + +class FunnyLettersGameData implements GameData { + +} \ No newline at end of file diff --git a/games/mnemo_cards_game_api/lib/src/game_data.dart b/games/mnemo_cards_game_api/lib/src/game_data.dart new file mode 100644 index 0000000..82b0b64 --- /dev/null +++ b/games/mnemo_cards_game_api/lib/src/game_data.dart @@ -0,0 +1 @@ +abstract class GameData {} diff --git a/games/mnemo_cards_game_api/lib/src/game_page.dart b/games/mnemo_cards_game_api/lib/src/game_page.dart new file mode 100644 index 0000000..3356a74 --- /dev/null +++ b/games/mnemo_cards_game_api/lib/src/game_page.dart @@ -0,0 +1,154 @@ +import 'dart:developer'; +import 'package:flutter/material.dart'; +// import 'package:flutter_screenutil/flutter_screenutil.dart'; +// import 'package:url_launcher/url_launcher.dart'; +import 'package:webview_flutter/webview_flutter.dart'; + +class WebViewPage extends StatefulWidget { + final String url; + final String? title; + final String? returnUrl; + final VoidCallback? onPop; + final bool safeArea; + final bool setupToken; + + const WebViewPage({ + required this.url, + this.onPop, + this.returnUrl, + this.title, + this.safeArea = false, + this.setupToken = false, + }); + + @override + State createState() => _WebViewPageState(); +} + +class _WebViewPageState extends State with WidgetsBindingObserver { + bool canPop = false; + late final WebViewController controller; + bool _isBlank = false; + + void _onPop() { + if (widget.onPop != null) { + widget.onPop!(); + } + } + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + + controller = WebViewController() + ..setJavaScriptMode(JavaScriptMode.unrestricted) + ..setBackgroundColor(Colors.white) + ..setNavigationDelegate( + NavigationDelegate( + onNavigationRequest: (r) { + final u = Uri.parse(r.url); + if (u.scheme != 'http' && u.scheme != 'https') { + _handleDeeplink(u); + return NavigationDecision.prevent; + } + return NavigationDecision.navigate; + }, + onPageStarted: (u) async { + // если вернулись на returnUrl + if (widget.returnUrl != null && u.contains(widget.returnUrl!)) { + _popAndClose(); + } + // подставляем токен + if (widget.setupToken) { + final token = await _userToken(); + if (token != null) { + final uri = Uri.parse(widget.url); + final host = '${uri.scheme}://${uri.host}'; + controller.runJavaScript(''' + window.getUnityToken = ()=>'$token'; + window.unityData={targetUrl:"$host"}; + function ExitGame(){Flutter.postMessage('exitGame');} + '''); + } + } + }, + ), + ) + ..loadRequest(Uri.parse(widget.url)); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + super.didChangeAppLifecycleState(state); + + if ((state == AppLifecycleState.paused || state == AppLifecycleState.inactive) && mounted) { + // при уходе в фон — грузим пустую страницу, чтобы сразу остановить звук + controller.loadRequest(Uri.parse('about:blank')); + _isBlank = true; + } + + if (state == AppLifecycleState.resumed && mounted) { + // при возврате — восстанавливаем исходный URL + if (_isBlank) { + controller.loadRequest(Uri.parse(widget.url)); + _isBlank = false; + } + } + } + + Future _userToken() async {} + + Future _handleDeeplink(Uri uri) async {} + + Future _popAndClose() async { + if (!canPop) { + setState(() => canPop = true); + + // очищаем WebView + await controller.loadRequest(Uri.parse('about:blank')); + await Future.wait([ + controller.clearCache(), + controller.clearLocalStorage(), + controller.removeJavaScriptChannel('Flutter'), + ]); + + _onPop(); + await Future.delayed(const Duration(milliseconds: 250)); + // AutoRouter.of(context).parent()?.maybePop(true); + } + } + + @override + void dispose() { + // на всякий случай — "убиваем" WebView перед уничтожением + controller.loadRequest(Uri.parse('about:blank')); + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return PopScope( + canPop: canPop, + onPopInvokedWithResult: (didPop, _) async { + if (!canPop) { + setState(() => canPop = true); + await controller.loadRequest(Uri.parse('about:blank')); + _onPop(); + await Future.delayed(const Duration(milliseconds: 250)); + // AutoRouter.of(context).back(); + } + }, + child: SafeArea( + child: Scaffold( + body: SafeArea( + top: widget.safeArea, + bottom: widget.safeArea, + child: WebViewWidget(controller: controller), + ), + ), + ), + ); + } +} diff --git a/games/mnemo_cards_game_api/lib/src/game_theme.dart b/games/mnemo_cards_game_api/lib/src/game_theme.dart new file mode 100644 index 0000000..6fad57d --- /dev/null +++ b/games/mnemo_cards_game_api/lib/src/game_theme.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; + +class GameSettings { + final bool musicEnabled; + final bool soundEnabled; + final GameThemeData themeData; + + GameSettings({ + this.musicEnabled = true, + this.soundEnabled = true, + required this.themeData, + }); + + factory GameSettings.fromJson(Map json) { + return GameSettings( + musicEnabled: json['musicEnabled'] ?? true, + soundEnabled: json['soundEnabled'] ?? true, + themeData: GameThemeData( + brightness: json['isDarkMode'] == true + ? Brightness.dark + : Brightness.light, + ), + ); + } + + Map toJson() => { + 'musicEnabled': musicEnabled, + 'soundEnabled': soundEnabled, + 'isDarkMode': themeData.brightness == Brightness.dark, + }; +} + +class GameThemeData { + final Brightness brightness; + + const GameThemeData({required this.brightness}); +} diff --git a/games/mnemo_cards_game_api/lib/src/theme.dart b/games/mnemo_cards_game_api/lib/src/theme.dart new file mode 100644 index 0000000..f33cb29 --- /dev/null +++ b/games/mnemo_cards_game_api/lib/src/theme.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; +import 'package:mnemo_cards_game_api/mnemo_cards_game_api.dart'; + +extension ThemeDataExtensions on GameThemeData { + ThemeData toMaterialTheme() { + return brightness == Brightness.light + ? ThemeData.light().copyWith( + colorScheme: ColorScheme.fromSwatch( + backgroundColor: Colors.white, + brightness: Brightness.light, + ), + ) + : ThemeData.dark().copyWith( + colorScheme: ColorScheme.fromSwatch( + backgroundColor: Colors.white, + brightness: Brightness.light, + ), + ); + } +} diff --git a/games/mnemo_cards_web_bridge/lib/src/web_app_bridget.dart b/games/mnemo_cards_web_bridge/lib/src/web_app_bridget.dart new file mode 100644 index 0000000..3d9706a --- /dev/null +++ b/games/mnemo_cards_web_bridge/lib/src/web_app_bridget.dart @@ -0,0 +1,16 @@ +abstract interface class WebAppBridget { + /// Returns the current URL of the web application. + String getCurrentUrl(); + + /// Navigates to the specified URL. + void navigateTo(String url); + + /// Reloads the current page. + void reloadPage(); + + /// Returns the title of the current page. + String getPageTitle(); + + /// Sets the title of the current page. + void setPageTitle(String title); +} \ No newline at end of file diff --git a/codegen.sh b/games/packages/payloads_shared/codegen.sh similarity index 100% rename from codegen.sh rename to games/packages/payloads_shared/codegen.sh diff --git a/games/packages/payloads_shared/lib/src/payloads/user_cards_payload.dart b/games/packages/payloads_shared/lib/src/payloads/user_cards_payload.dart new file mode 100644 index 0000000..bb56ded --- /dev/null +++ b/games/packages/payloads_shared/lib/src/payloads/user_cards_payload.dart @@ -0,0 +1,77 @@ +import 'package:bridge_core/bridge_core.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:copy_with_extension/copy_with_extension.dart'; + +part 'user_cards_payload.g.dart'; + +@CopyWith() +@JsonSerializable() +class UserCardsRequestPayload extends BridgePayload { + @JsonKey(name: 'pack_ids') + final List? packIds; + @JsonKey(name: 'card_ids') + final List? cardIds; + @JsonKey(name: 'original_words') + final List? originalWords; + @JsonKey(name: 'translation_words') + final List? translationWords; + + const UserCardsRequestPayload({ + this.packIds, + this.cardIds, + this.originalWords, + this.translationWords, + }); + + @override + Map toJson() => _$UserCardsRequestPayloadToJson(this); + + @override + String get type => 'user_cards_request_payload'; +} + +@CopyWith() +@JsonSerializable() +class UserCardsPayload extends BridgePayload { + @JsonKey(name: 'cards') + final List gameCards; + + UserCardsPayload({required this.gameCards}); + + @override + Map toJson() => _$UserCardsPayloadToJson(this); + + @override + String get type => 'user_cards_payload'; +} + +@JsonSerializable() +@CopyWith() +class GameCard { + final int id; + final String? image; + final String? mnemo; + final String? original; + final String? translation; + final String? transcription; + final String? imageBack; + final String? transcriptionMnemo; + final String? back; + + const GameCard({ + required this.id, + required this.mnemo, + required this.original, + required this.translation, + required this.transcription, + this.transcriptionMnemo, + this.image, + this.imageBack, + this.back, + }); + + factory GameCard.fromJson(Map json) => + _$GameCardFromJson(json); + + Map toJson() => _$GameCardToJson(this); +} diff --git a/icons/back.png b/icons/back.png deleted file mode 100644 index 078e97b..0000000 Binary files a/icons/back.png and /dev/null differ diff --git a/icons/cards.png b/icons/cards.png deleted file mode 100644 index 387b81c..0000000 Binary files a/icons/cards.png and /dev/null differ diff --git a/icons/chat.png b/icons/chat.png deleted file mode 100644 index 06990a2..0000000 Binary files a/icons/chat.png and /dev/null differ diff --git a/icons/clock.png b/icons/clock.png deleted file mode 100644 index c57fc40..0000000 Binary files a/icons/clock.png and /dev/null differ diff --git a/icons/crown.png b/icons/crown.png deleted file mode 100644 index 8b8f9d7..0000000 Binary files a/icons/crown.png and /dev/null differ diff --git a/icons/down.png b/icons/down.png deleted file mode 100644 index 581d80d..0000000 Binary files a/icons/down.png and /dev/null differ diff --git a/icons/exit.png b/icons/exit.png deleted file mode 100644 index 915ad08..0000000 Binary files a/icons/exit.png and /dev/null differ diff --git a/icons/gift.png b/icons/gift.png deleted file mode 100644 index f1cfa5d..0000000 Binary files a/icons/gift.png and /dev/null differ diff --git a/icons/google.png b/icons/google.png deleted file mode 100644 index d626325..0000000 Binary files a/icons/google.png and /dev/null differ diff --git a/icons/heart.png b/icons/heart.png deleted file mode 100644 index 89331b2..0000000 Binary files a/icons/heart.png and /dev/null differ diff --git a/icons/heart_fill.png b/icons/heart_fill.png deleted file mode 100644 index 842ad28..0000000 Binary files a/icons/heart_fill.png and /dev/null differ diff --git a/icons/mic_off.png b/icons/mic_off.png deleted file mode 100644 index e147ad5..0000000 Binary files a/icons/mic_off.png and /dev/null differ diff --git a/icons/mic_on.png b/icons/mic_on.png deleted file mode 100644 index 54150be..0000000 Binary files a/icons/mic_on.png and /dev/null differ diff --git a/icons/next.png b/icons/next.png deleted file mode 100644 index 66ed80b..0000000 Binary files a/icons/next.png and /dev/null differ diff --git a/icons/profile.png b/icons/profile.png deleted file mode 100644 index b4cd411..0000000 Binary files a/icons/profile.png and /dev/null differ diff --git a/icons/settings.png b/icons/settings.png deleted file mode 100644 index 7ac7dbe..0000000 Binary files a/icons/settings.png and /dev/null differ diff --git a/icons/shuffle.png b/icons/shuffle.png deleted file mode 100644 index 7ec9a54..0000000 Binary files a/icons/shuffle.png and /dev/null differ diff --git a/icons/skull.png b/icons/skull.png deleted file mode 100644 index 6ec7283..0000000 Binary files a/icons/skull.png and /dev/null differ diff --git a/icons/small_circle.png b/icons/small_circle.png deleted file mode 100644 index 058e19b..0000000 Binary files a/icons/small_circle.png and /dev/null differ diff --git a/icons/sound_off.png b/icons/sound_off.png deleted file mode 100644 index 40cd041..0000000 Binary files a/icons/sound_off.png and /dev/null differ diff --git a/icons/sound_on.png b/icons/sound_on.png deleted file mode 100644 index 263a078..0000000 Binary files a/icons/sound_on.png and /dev/null differ diff --git a/icons/tg.png b/icons/tg.png deleted file mode 100644 index c3097fb..0000000 Binary files a/icons/tg.png and /dev/null differ diff --git a/icons/view.png b/icons/view.png deleted file mode 100644 index 73be8f0..0000000 Binary files a/icons/view.png and /dev/null differ diff --git a/images_gen.sh b/images_gen.sh deleted file mode 100644 index 03500e2..0000000 --- a/images_gen.sh +++ /dev/null @@ -1,2 +0,0 @@ -dart run flutter_native_splash:create -dart run flutter_launcher_icons \ No newline at end of file diff --git a/ios/.gitignore b/ios/.gitignore deleted file mode 100644 index 7a7f987..0000000 --- a/ios/.gitignore +++ /dev/null @@ -1,34 +0,0 @@ -**/dgph -*.mode1v3 -*.mode2v3 -*.moved-aside -*.pbxuser -*.perspectivev3 -**/*sync/ -.sconsign.dblite -.tags* -**/.vagrant/ -**/DerivedData/ -Icon? -**/Pods/ -**/.symlinks/ -profile -xcuserdata -**/.generated/ -Flutter/App.framework -Flutter/Flutter.framework -Flutter/Flutter.podspec -Flutter/Generated.xcconfig -Flutter/ephemeral/ -Flutter/app.flx -Flutter/app.zip -Flutter/flutter_assets/ -Flutter/flutter_export_environment.sh -ServiceDefinitions.json -Runner/GeneratedPluginRegistrant.* - -# Exceptions to above rules. -!default.mode1v3 -!default.mode2v3 -!default.pbxuser -!default.perspectivev3 diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist deleted file mode 100644 index 163000d..0000000 --- a/ios/Flutter/AppFrameworkInfo.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - App - CFBundleIdentifier - io.flutter.flutter.app - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - App - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - MinimumOSVersion - 14.0 - - diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig deleted file mode 100644 index ec97fc6..0000000 --- a/ios/Flutter/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" -#include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig deleted file mode 100644 index c4855bf..0000000 --- a/ios/Flutter/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" -#include "Generated.xcconfig" diff --git a/ios/Podfile b/ios/Podfile deleted file mode 100644 index 003e055..0000000 --- a/ios/Podfile +++ /dev/null @@ -1,49 +0,0 @@ -# source 'https://github.com/CocoaPods/Specs.git' -# source 'https://git.yoomoney.ru/scm/sdk/cocoa-pod-specs.git' - - -# Uncomment this line to define a global platform for your project -platform :ios, '14.0' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_ios_podfile_setup - -target 'Runner' do - use_frameworks! :linkage => :static -# use_frameworks! - use_modular_headers! - - flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) - target 'RunnerTests' do - inherit! :search_paths - end -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - flutter_additional_ios_build_settings(target) - end -end diff --git a/ios/Podfile.lock b/ios/Podfile.lock deleted file mode 100644 index 1893d20..0000000 --- a/ios/Podfile.lock +++ /dev/null @@ -1,1503 +0,0 @@ -PODS: - - abseil/algorithm (1.20240116.2): - - abseil/algorithm/algorithm (= 1.20240116.2) - - abseil/algorithm/container (= 1.20240116.2) - - abseil/algorithm/algorithm (1.20240116.2): - - abseil/base/config - - abseil/xcprivacy - - abseil/algorithm/container (1.20240116.2): - - abseil/algorithm/algorithm - - abseil/base/core_headers - - abseil/base/nullability - - abseil/meta/type_traits - - abseil/xcprivacy - - abseil/base (1.20240116.2): - - abseil/base/atomic_hook (= 1.20240116.2) - - abseil/base/base (= 1.20240116.2) - - abseil/base/base_internal (= 1.20240116.2) - - abseil/base/config (= 1.20240116.2) - - abseil/base/core_headers (= 1.20240116.2) - - abseil/base/cycleclock_internal (= 1.20240116.2) - - abseil/base/dynamic_annotations (= 1.20240116.2) - - abseil/base/endian (= 1.20240116.2) - - abseil/base/errno_saver (= 1.20240116.2) - - abseil/base/fast_type_id (= 1.20240116.2) - - abseil/base/log_severity (= 1.20240116.2) - - abseil/base/malloc_internal (= 1.20240116.2) - - abseil/base/no_destructor (= 1.20240116.2) - - abseil/base/nullability (= 1.20240116.2) - - abseil/base/prefetch (= 1.20240116.2) - - abseil/base/pretty_function (= 1.20240116.2) - - abseil/base/raw_logging_internal (= 1.20240116.2) - - abseil/base/spinlock_wait (= 1.20240116.2) - - abseil/base/strerror (= 1.20240116.2) - - abseil/base/throw_delegate (= 1.20240116.2) - - abseil/base/atomic_hook (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/xcprivacy - - abseil/base/base (1.20240116.2): - - abseil/base/atomic_hook - - abseil/base/base_internal - - abseil/base/config - - abseil/base/core_headers - - abseil/base/cycleclock_internal - - abseil/base/dynamic_annotations - - abseil/base/log_severity - - abseil/base/nullability - - abseil/base/raw_logging_internal - - abseil/base/spinlock_wait - - abseil/meta/type_traits - - abseil/xcprivacy - - abseil/base/base_internal (1.20240116.2): - - abseil/base/config - - abseil/meta/type_traits - - abseil/xcprivacy - - abseil/base/config (1.20240116.2): - - abseil/xcprivacy - - abseil/base/core_headers (1.20240116.2): - - abseil/base/config - - abseil/xcprivacy - - abseil/base/cycleclock_internal (1.20240116.2): - - abseil/base/base_internal - - abseil/base/config - - abseil/xcprivacy - - abseil/base/dynamic_annotations (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/xcprivacy - - abseil/base/endian (1.20240116.2): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/base/nullability - - abseil/xcprivacy - - abseil/base/errno_saver (1.20240116.2): - - abseil/base/config - - abseil/xcprivacy - - abseil/base/fast_type_id (1.20240116.2): - - abseil/base/config - - abseil/xcprivacy - - abseil/base/log_severity (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/xcprivacy - - abseil/base/malloc_internal (1.20240116.2): - - abseil/base/base - - abseil/base/base_internal - - abseil/base/config - - abseil/base/core_headers - - abseil/base/dynamic_annotations - - abseil/base/raw_logging_internal - - abseil/xcprivacy - - abseil/base/no_destructor (1.20240116.2): - - abseil/base/config - - abseil/xcprivacy - - abseil/base/nullability (1.20240116.2): - - abseil/base/core_headers - - abseil/meta/type_traits - - abseil/xcprivacy - - abseil/base/prefetch (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/xcprivacy - - abseil/base/pretty_function (1.20240116.2): - - abseil/xcprivacy - - abseil/base/raw_logging_internal (1.20240116.2): - - abseil/base/atomic_hook - - abseil/base/config - - abseil/base/core_headers - - abseil/base/errno_saver - - abseil/base/log_severity - - abseil/xcprivacy - - abseil/base/spinlock_wait (1.20240116.2): - - abseil/base/base_internal - - abseil/base/core_headers - - abseil/base/errno_saver - - abseil/xcprivacy - - abseil/base/strerror (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/errno_saver - - abseil/xcprivacy - - abseil/base/throw_delegate (1.20240116.2): - - abseil/base/config - - abseil/base/raw_logging_internal - - abseil/xcprivacy - - abseil/cleanup/cleanup (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/cleanup/cleanup_internal - - abseil/xcprivacy - - abseil/cleanup/cleanup_internal (1.20240116.2): - - abseil/base/base_internal - - abseil/base/core_headers - - abseil/utility/utility - - abseil/xcprivacy - - abseil/container/common (1.20240116.2): - - abseil/meta/type_traits - - abseil/types/optional - - abseil/xcprivacy - - abseil/container/common_policy_traits (1.20240116.2): - - abseil/meta/type_traits - - abseil/xcprivacy - - abseil/container/compressed_tuple (1.20240116.2): - - abseil/utility/utility - - abseil/xcprivacy - - abseil/container/container_memory (1.20240116.2): - - abseil/base/config - - abseil/memory/memory - - abseil/meta/type_traits - - abseil/utility/utility - - abseil/xcprivacy - - abseil/container/fixed_array (1.20240116.2): - - abseil/algorithm/algorithm - - abseil/base/config - - abseil/base/core_headers - - abseil/base/dynamic_annotations - - abseil/base/throw_delegate - - abseil/container/compressed_tuple - - abseil/memory/memory - - abseil/xcprivacy - - abseil/container/flat_hash_map (1.20240116.2): - - abseil/algorithm/container - - abseil/base/core_headers - - abseil/container/container_memory - - abseil/container/hash_function_defaults - - abseil/container/raw_hash_map - - abseil/memory/memory - - abseil/xcprivacy - - abseil/container/flat_hash_set (1.20240116.2): - - abseil/algorithm/container - - abseil/base/core_headers - - abseil/container/container_memory - - abseil/container/hash_function_defaults - - abseil/container/raw_hash_set - - abseil/memory/memory - - abseil/xcprivacy - - abseil/container/hash_function_defaults (1.20240116.2): - - abseil/base/config - - abseil/hash/hash - - abseil/strings/cord - - abseil/strings/strings - - abseil/xcprivacy - - abseil/container/hash_policy_traits (1.20240116.2): - - abseil/container/common_policy_traits - - abseil/meta/type_traits - - abseil/xcprivacy - - abseil/container/hashtable_debug_hooks (1.20240116.2): - - abseil/base/config - - abseil/xcprivacy - - abseil/container/hashtablez_sampler (1.20240116.2): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/base/raw_logging_internal - - abseil/debugging/stacktrace - - abseil/memory/memory - - abseil/profiling/exponential_biased - - abseil/profiling/sample_recorder - - abseil/synchronization/synchronization - - abseil/time/time - - abseil/utility/utility - - abseil/xcprivacy - - abseil/container/inlined_vector (1.20240116.2): - - abseil/algorithm/algorithm - - abseil/base/core_headers - - abseil/base/throw_delegate - - abseil/container/inlined_vector_internal - - abseil/memory/memory - - abseil/meta/type_traits - - abseil/xcprivacy - - abseil/container/inlined_vector_internal (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/container/compressed_tuple - - abseil/memory/memory - - abseil/meta/type_traits - - abseil/types/span - - abseil/xcprivacy - - abseil/container/layout (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/debugging/demangle_internal - - abseil/meta/type_traits - - abseil/strings/strings - - abseil/types/span - - abseil/utility/utility - - abseil/xcprivacy - - abseil/container/raw_hash_map (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/throw_delegate - - abseil/container/container_memory - - abseil/container/raw_hash_set - - abseil/xcprivacy - - abseil/container/raw_hash_set (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/dynamic_annotations - - abseil/base/endian - - abseil/base/prefetch - - abseil/base/raw_logging_internal - - abseil/container/common - - abseil/container/compressed_tuple - - abseil/container/container_memory - - abseil/container/hash_policy_traits - - abseil/container/hashtable_debug_hooks - - abseil/container/hashtablez_sampler - - abseil/hash/hash - - abseil/memory/memory - - abseil/meta/type_traits - - abseil/numeric/bits - - abseil/utility/utility - - abseil/xcprivacy - - abseil/crc/cpu_detect (1.20240116.2): - - abseil/base/base - - abseil/base/config - - abseil/xcprivacy - - abseil/crc/crc32c (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian - - abseil/base/prefetch - - abseil/crc/cpu_detect - - abseil/crc/crc_internal - - abseil/crc/non_temporal_memcpy - - abseil/strings/str_format - - abseil/strings/strings - - abseil/xcprivacy - - abseil/crc/crc_cord_state (1.20240116.2): - - abseil/base/config - - abseil/crc/crc32c - - abseil/numeric/bits - - abseil/strings/strings - - abseil/xcprivacy - - abseil/crc/crc_internal (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian - - abseil/base/prefetch - - abseil/base/raw_logging_internal - - abseil/crc/cpu_detect - - abseil/memory/memory - - abseil/numeric/bits - - abseil/xcprivacy - - abseil/crc/non_temporal_arm_intrinsics (1.20240116.2): - - abseil/base/config - - abseil/xcprivacy - - abseil/crc/non_temporal_memcpy (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/crc/non_temporal_arm_intrinsics - - abseil/xcprivacy - - abseil/debugging/debugging_internal (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/dynamic_annotations - - abseil/base/errno_saver - - abseil/base/raw_logging_internal - - abseil/xcprivacy - - abseil/debugging/demangle_internal (1.20240116.2): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/xcprivacy - - abseil/debugging/stacktrace (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/dynamic_annotations - - abseil/base/raw_logging_internal - - abseil/debugging/debugging_internal - - abseil/xcprivacy - - abseil/debugging/symbolize (1.20240116.2): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/base/dynamic_annotations - - abseil/base/malloc_internal - - abseil/base/raw_logging_internal - - abseil/debugging/debugging_internal - - abseil/debugging/demangle_internal - - abseil/strings/strings - - abseil/xcprivacy - - abseil/flags/commandlineflag (1.20240116.2): - - abseil/base/config - - abseil/base/fast_type_id - - abseil/flags/commandlineflag_internal - - abseil/strings/strings - - abseil/types/optional - - abseil/xcprivacy - - abseil/flags/commandlineflag_internal (1.20240116.2): - - abseil/base/config - - abseil/base/fast_type_id - - abseil/xcprivacy - - abseil/flags/config (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/flags/path_util - - abseil/flags/program_name - - abseil/strings/strings - - abseil/synchronization/synchronization - - abseil/xcprivacy - - abseil/flags/flag (1.20240116.2): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/flags/config - - abseil/flags/flag_internal - - abseil/flags/reflection - - abseil/strings/strings - - abseil/xcprivacy - - abseil/flags/flag_internal (1.20240116.2): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/base/dynamic_annotations - - abseil/flags/commandlineflag - - abseil/flags/commandlineflag_internal - - abseil/flags/config - - abseil/flags/marshalling - - abseil/flags/reflection - - abseil/memory/memory - - abseil/meta/type_traits - - abseil/strings/strings - - abseil/synchronization/synchronization - - abseil/utility/utility - - abseil/xcprivacy - - abseil/flags/marshalling (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/log_severity - - abseil/numeric/int128 - - abseil/strings/str_format - - abseil/strings/strings - - abseil/types/optional - - abseil/xcprivacy - - abseil/flags/path_util (1.20240116.2): - - abseil/base/config - - abseil/strings/strings - - abseil/xcprivacy - - abseil/flags/private_handle_accessor (1.20240116.2): - - abseil/base/config - - abseil/flags/commandlineflag - - abseil/flags/commandlineflag_internal - - abseil/strings/strings - - abseil/xcprivacy - - abseil/flags/program_name (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/flags/path_util - - abseil/strings/strings - - abseil/synchronization/synchronization - - abseil/xcprivacy - - abseil/flags/reflection (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/no_destructor - - abseil/container/flat_hash_map - - abseil/flags/commandlineflag - - abseil/flags/commandlineflag_internal - - abseil/flags/config - - abseil/flags/private_handle_accessor - - abseil/strings/strings - - abseil/synchronization/synchronization - - abseil/xcprivacy - - abseil/functional/any_invocable (1.20240116.2): - - abseil/base/base_internal - - abseil/base/config - - abseil/base/core_headers - - abseil/meta/type_traits - - abseil/utility/utility - - abseil/xcprivacy - - abseil/functional/bind_front (1.20240116.2): - - abseil/base/base_internal - - abseil/container/compressed_tuple - - abseil/meta/type_traits - - abseil/utility/utility - - abseil/xcprivacy - - abseil/functional/function_ref (1.20240116.2): - - abseil/base/base_internal - - abseil/base/core_headers - - abseil/functional/any_invocable - - abseil/meta/type_traits - - abseil/xcprivacy - - abseil/hash/city (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian - - abseil/xcprivacy - - abseil/hash/hash (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian - - abseil/container/fixed_array - - abseil/functional/function_ref - - abseil/hash/city - - abseil/hash/low_level_hash - - abseil/meta/type_traits - - abseil/numeric/bits - - abseil/numeric/int128 - - abseil/strings/strings - - abseil/types/optional - - abseil/types/variant - - abseil/utility/utility - - abseil/xcprivacy - - abseil/hash/low_level_hash (1.20240116.2): - - abseil/base/config - - abseil/base/endian - - abseil/base/prefetch - - abseil/numeric/int128 - - abseil/xcprivacy - - abseil/memory (1.20240116.2): - - abseil/memory/memory (= 1.20240116.2) - - abseil/memory/memory (1.20240116.2): - - abseil/base/core_headers - - abseil/meta/type_traits - - abseil/xcprivacy - - abseil/meta (1.20240116.2): - - abseil/meta/type_traits (= 1.20240116.2) - - abseil/meta/type_traits (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/xcprivacy - - abseil/numeric/bits (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/xcprivacy - - abseil/numeric/int128 (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/numeric/bits - - abseil/xcprivacy - - abseil/numeric/representation (1.20240116.2): - - abseil/base/config - - abseil/xcprivacy - - abseil/profiling/exponential_biased (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/xcprivacy - - abseil/profiling/sample_recorder (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/synchronization/synchronization - - abseil/time/time - - abseil/xcprivacy - - abseil/random/bit_gen_ref (1.20240116.2): - - abseil/base/core_headers - - abseil/base/fast_type_id - - abseil/meta/type_traits - - abseil/random/internal/distribution_caller - - abseil/random/internal/fast_uniform_bits - - abseil/random/random - - abseil/xcprivacy - - abseil/random/distributions (1.20240116.2): - - abseil/base/base_internal - - abseil/base/config - - abseil/base/core_headers - - abseil/meta/type_traits - - abseil/numeric/bits - - abseil/random/internal/distribution_caller - - abseil/random/internal/fast_uniform_bits - - abseil/random/internal/fastmath - - abseil/random/internal/generate_real - - abseil/random/internal/iostream_state_saver - - abseil/random/internal/traits - - abseil/random/internal/uniform_helper - - abseil/random/internal/wide_multiply - - abseil/strings/strings - - abseil/xcprivacy - - abseil/random/internal/distribution_caller (1.20240116.2): - - abseil/base/config - - abseil/base/fast_type_id - - abseil/utility/utility - - abseil/xcprivacy - - abseil/random/internal/fast_uniform_bits (1.20240116.2): - - abseil/base/config - - abseil/meta/type_traits - - abseil/random/internal/traits - - abseil/xcprivacy - - abseil/random/internal/fastmath (1.20240116.2): - - abseil/numeric/bits - - abseil/xcprivacy - - abseil/random/internal/generate_real (1.20240116.2): - - abseil/meta/type_traits - - abseil/numeric/bits - - abseil/random/internal/fastmath - - abseil/random/internal/traits - - abseil/xcprivacy - - abseil/random/internal/iostream_state_saver (1.20240116.2): - - abseil/meta/type_traits - - abseil/numeric/int128 - - abseil/xcprivacy - - abseil/random/internal/nonsecure_base (1.20240116.2): - - abseil/base/core_headers - - abseil/container/inlined_vector - - abseil/meta/type_traits - - abseil/random/internal/pool_urbg - - abseil/random/internal/salted_seed_seq - - abseil/random/internal/seed_material - - abseil/types/span - - abseil/xcprivacy - - abseil/random/internal/pcg_engine (1.20240116.2): - - abseil/base/config - - abseil/meta/type_traits - - abseil/numeric/bits - - abseil/numeric/int128 - - abseil/random/internal/fastmath - - abseil/random/internal/iostream_state_saver - - abseil/xcprivacy - - abseil/random/internal/platform (1.20240116.2): - - abseil/base/config - - abseil/xcprivacy - - abseil/random/internal/pool_urbg (1.20240116.2): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian - - abseil/base/raw_logging_internal - - abseil/random/internal/randen - - abseil/random/internal/seed_material - - abseil/random/internal/traits - - abseil/random/seed_gen_exception - - abseil/types/span - - abseil/xcprivacy - - abseil/random/internal/randen (1.20240116.2): - - abseil/base/raw_logging_internal - - abseil/random/internal/platform - - abseil/random/internal/randen_hwaes - - abseil/random/internal/randen_slow - - abseil/xcprivacy - - abseil/random/internal/randen_engine (1.20240116.2): - - abseil/base/endian - - abseil/meta/type_traits - - abseil/random/internal/iostream_state_saver - - abseil/random/internal/randen - - abseil/xcprivacy - - abseil/random/internal/randen_hwaes (1.20240116.2): - - abseil/base/config - - abseil/random/internal/platform - - abseil/random/internal/randen_hwaes_impl - - abseil/xcprivacy - - abseil/random/internal/randen_hwaes_impl (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/numeric/int128 - - abseil/random/internal/platform - - abseil/xcprivacy - - abseil/random/internal/randen_slow (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian - - abseil/numeric/int128 - - abseil/random/internal/platform - - abseil/xcprivacy - - abseil/random/internal/salted_seed_seq (1.20240116.2): - - abseil/container/inlined_vector - - abseil/meta/type_traits - - abseil/random/internal/seed_material - - abseil/types/optional - - abseil/types/span - - abseil/xcprivacy - - abseil/random/internal/seed_material (1.20240116.2): - - abseil/base/core_headers - - abseil/base/dynamic_annotations - - abseil/base/raw_logging_internal - - abseil/random/internal/fast_uniform_bits - - abseil/strings/strings - - abseil/types/optional - - abseil/types/span - - abseil/xcprivacy - - abseil/random/internal/traits (1.20240116.2): - - abseil/base/config - - abseil/numeric/bits - - abseil/numeric/int128 - - abseil/xcprivacy - - abseil/random/internal/uniform_helper (1.20240116.2): - - abseil/base/config - - abseil/meta/type_traits - - abseil/numeric/int128 - - abseil/random/internal/traits - - abseil/xcprivacy - - abseil/random/internal/wide_multiply (1.20240116.2): - - abseil/base/config - - abseil/numeric/bits - - abseil/numeric/int128 - - abseil/random/internal/traits - - abseil/xcprivacy - - abseil/random/random (1.20240116.2): - - abseil/random/distributions - - abseil/random/internal/nonsecure_base - - abseil/random/internal/pcg_engine - - abseil/random/internal/pool_urbg - - abseil/random/internal/randen_engine - - abseil/random/seed_sequences - - abseil/xcprivacy - - abseil/random/seed_gen_exception (1.20240116.2): - - abseil/base/config - - abseil/xcprivacy - - abseil/random/seed_sequences (1.20240116.2): - - abseil/base/config - - abseil/random/internal/pool_urbg - - abseil/random/internal/salted_seed_seq - - abseil/random/internal/seed_material - - abseil/random/seed_gen_exception - - abseil/types/span - - abseil/xcprivacy - - abseil/status/status (1.20240116.2): - - abseil/base/atomic_hook - - abseil/base/config - - abseil/base/core_headers - - abseil/base/no_destructor - - abseil/base/nullability - - abseil/base/raw_logging_internal - - abseil/base/strerror - - abseil/container/inlined_vector - - abseil/debugging/stacktrace - - abseil/debugging/symbolize - - abseil/functional/function_ref - - abseil/memory/memory - - abseil/strings/cord - - abseil/strings/str_format - - abseil/strings/strings - - abseil/types/optional - - abseil/types/span - - abseil/xcprivacy - - abseil/status/statusor (1.20240116.2): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/base/nullability - - abseil/base/raw_logging_internal - - abseil/meta/type_traits - - abseil/status/status - - abseil/strings/has_ostream_operator - - abseil/strings/str_format - - abseil/strings/strings - - abseil/types/variant - - abseil/utility/utility - - abseil/xcprivacy - - abseil/strings/charset (1.20240116.2): - - abseil/base/core_headers - - abseil/strings/string_view - - abseil/xcprivacy - - abseil/strings/cord (1.20240116.2): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian - - abseil/base/nullability - - abseil/base/raw_logging_internal - - abseil/container/inlined_vector - - abseil/crc/crc32c - - abseil/crc/crc_cord_state - - abseil/functional/function_ref - - abseil/meta/type_traits - - abseil/numeric/bits - - abseil/strings/cord_internal - - abseil/strings/cordz_functions - - abseil/strings/cordz_info - - abseil/strings/cordz_statistics - - abseil/strings/cordz_update_scope - - abseil/strings/cordz_update_tracker - - abseil/strings/internal - - abseil/strings/strings - - abseil/types/optional - - abseil/types/span - - abseil/xcprivacy - - abseil/strings/cord_internal (1.20240116.2): - - abseil/base/base_internal - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian - - abseil/base/raw_logging_internal - - abseil/base/throw_delegate - - abseil/container/compressed_tuple - - abseil/container/container_memory - - abseil/container/inlined_vector - - abseil/container/layout - - abseil/crc/crc_cord_state - - abseil/functional/function_ref - - abseil/meta/type_traits - - abseil/strings/strings - - abseil/types/span - - abseil/xcprivacy - - abseil/strings/cordz_functions (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/raw_logging_internal - - abseil/profiling/exponential_biased - - abseil/xcprivacy - - abseil/strings/cordz_handle (1.20240116.2): - - abseil/base/base - - abseil/base/config - - abseil/base/raw_logging_internal - - abseil/synchronization/synchronization - - abseil/xcprivacy - - abseil/strings/cordz_info (1.20240116.2): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/base/raw_logging_internal - - abseil/container/inlined_vector - - abseil/debugging/stacktrace - - abseil/strings/cord_internal - - abseil/strings/cordz_functions - - abseil/strings/cordz_handle - - abseil/strings/cordz_statistics - - abseil/strings/cordz_update_tracker - - abseil/synchronization/synchronization - - abseil/time/time - - abseil/types/span - - abseil/xcprivacy - - abseil/strings/cordz_statistics (1.20240116.2): - - abseil/base/config - - abseil/strings/cordz_update_tracker - - abseil/xcprivacy - - abseil/strings/cordz_update_scope (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/strings/cord_internal - - abseil/strings/cordz_info - - abseil/strings/cordz_update_tracker - - abseil/xcprivacy - - abseil/strings/cordz_update_tracker (1.20240116.2): - - abseil/base/config - - abseil/xcprivacy - - abseil/strings/has_ostream_operator (1.20240116.2): - - abseil/base/config - - abseil/xcprivacy - - abseil/strings/internal (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian - - abseil/base/raw_logging_internal - - abseil/meta/type_traits - - abseil/xcprivacy - - abseil/strings/str_format (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/nullability - - abseil/strings/str_format_internal - - abseil/strings/string_view - - abseil/types/span - - abseil/xcprivacy - - abseil/strings/str_format_internal (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/container/fixed_array - - abseil/container/inlined_vector - - abseil/functional/function_ref - - abseil/meta/type_traits - - abseil/numeric/bits - - abseil/numeric/int128 - - abseil/numeric/representation - - abseil/strings/strings - - abseil/types/optional - - abseil/types/span - - abseil/utility/utility - - abseil/xcprivacy - - abseil/strings/string_view (1.20240116.2): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/base/nullability - - abseil/base/throw_delegate - - abseil/xcprivacy - - abseil/strings/strings (1.20240116.2): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian - - abseil/base/nullability - - abseil/base/raw_logging_internal - - abseil/base/throw_delegate - - abseil/memory/memory - - abseil/meta/type_traits - - abseil/numeric/bits - - abseil/numeric/int128 - - abseil/strings/charset - - abseil/strings/internal - - abseil/strings/string_view - - abseil/xcprivacy - - abseil/synchronization/graphcycles_internal (1.20240116.2): - - abseil/base/base - - abseil/base/base_internal - - abseil/base/config - - abseil/base/core_headers - - abseil/base/malloc_internal - - abseil/base/raw_logging_internal - - abseil/xcprivacy - - abseil/synchronization/kernel_timeout_internal (1.20240116.2): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/base/raw_logging_internal - - abseil/time/time - - abseil/xcprivacy - - abseil/synchronization/synchronization (1.20240116.2): - - abseil/base/atomic_hook - - abseil/base/base - - abseil/base/base_internal - - abseil/base/config - - abseil/base/core_headers - - abseil/base/dynamic_annotations - - abseil/base/malloc_internal - - abseil/base/raw_logging_internal - - abseil/debugging/stacktrace - - abseil/debugging/symbolize - - abseil/synchronization/graphcycles_internal - - abseil/synchronization/kernel_timeout_internal - - abseil/time/time - - abseil/xcprivacy - - abseil/time (1.20240116.2): - - abseil/time/internal (= 1.20240116.2) - - abseil/time/time (= 1.20240116.2) - - abseil/time/internal (1.20240116.2): - - abseil/time/internal/cctz (= 1.20240116.2) - - abseil/time/internal/cctz (1.20240116.2): - - abseil/time/internal/cctz/civil_time (= 1.20240116.2) - - abseil/time/internal/cctz/time_zone (= 1.20240116.2) - - abseil/time/internal/cctz/civil_time (1.20240116.2): - - abseil/base/config - - abseil/xcprivacy - - abseil/time/internal/cctz/time_zone (1.20240116.2): - - abseil/base/config - - abseil/time/internal/cctz/civil_time - - abseil/xcprivacy - - abseil/time/time (1.20240116.2): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/base/raw_logging_internal - - abseil/numeric/int128 - - abseil/strings/strings - - abseil/time/internal/cctz/civil_time - - abseil/time/internal/cctz/time_zone - - abseil/types/optional - - abseil/xcprivacy - - abseil/types (1.20240116.2): - - abseil/types/any (= 1.20240116.2) - - abseil/types/bad_any_cast (= 1.20240116.2) - - abseil/types/bad_any_cast_impl (= 1.20240116.2) - - abseil/types/bad_optional_access (= 1.20240116.2) - - abseil/types/bad_variant_access (= 1.20240116.2) - - abseil/types/compare (= 1.20240116.2) - - abseil/types/optional (= 1.20240116.2) - - abseil/types/span (= 1.20240116.2) - - abseil/types/variant (= 1.20240116.2) - - abseil/types/any (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/fast_type_id - - abseil/meta/type_traits - - abseil/types/bad_any_cast - - abseil/utility/utility - - abseil/xcprivacy - - abseil/types/bad_any_cast (1.20240116.2): - - abseil/base/config - - abseil/types/bad_any_cast_impl - - abseil/xcprivacy - - abseil/types/bad_any_cast_impl (1.20240116.2): - - abseil/base/config - - abseil/base/raw_logging_internal - - abseil/xcprivacy - - abseil/types/bad_optional_access (1.20240116.2): - - abseil/base/config - - abseil/base/raw_logging_internal - - abseil/xcprivacy - - abseil/types/bad_variant_access (1.20240116.2): - - abseil/base/config - - abseil/base/raw_logging_internal - - abseil/xcprivacy - - abseil/types/compare (1.20240116.2): - - abseil/base/config - - abseil/base/core_headers - - abseil/meta/type_traits - - abseil/xcprivacy - - abseil/types/optional (1.20240116.2): - - abseil/base/base_internal - - abseil/base/config - - abseil/base/core_headers - - abseil/base/nullability - - abseil/memory/memory - - abseil/meta/type_traits - - abseil/types/bad_optional_access - - abseil/utility/utility - - abseil/xcprivacy - - abseil/types/span (1.20240116.2): - - abseil/algorithm/algorithm - - abseil/base/core_headers - - abseil/base/nullability - - abseil/base/throw_delegate - - abseil/meta/type_traits - - abseil/xcprivacy - - abseil/types/variant (1.20240116.2): - - abseil/base/base_internal - - abseil/base/config - - abseil/base/core_headers - - abseil/meta/type_traits - - abseil/types/bad_variant_access - - abseil/utility/utility - - abseil/xcprivacy - - abseil/utility/utility (1.20240116.2): - - abseil/base/base_internal - - abseil/base/config - - abseil/meta/type_traits - - abseil/xcprivacy - - abseil/xcprivacy (1.20240116.2) - - AppAuth (1.7.5): - - AppAuth/Core (= 1.7.5) - - AppAuth/ExternalUserAgent (= 1.7.5) - - AppAuth/Core (1.7.5) - - AppAuth/ExternalUserAgent (1.7.5): - - AppAuth/Core - - AppMetrica_FMDB (5.2.0) - - AppMetrica_Protobuf (5.2.0) - - AppMetricaAdSupport (5.2.0): - - AppMetricaCore (= 5.2.0) - - AppMetricaCoreExtension (= 5.2.0) - - AppMetricaAnalytics (5.2.0): - - AppMetricaAdSupport (= 5.2.0) - - AppMetricaCore (= 5.2.0) - - AppMetricaCrashes (= 5.2.0) - - AppMetricaWebKit (= 5.2.0) - - AppMetricaCore (5.2.0): - - AppMetrica_FMDB (= 5.2.0) - - AppMetrica_Protobuf (= 5.2.0) - - AppMetricaCoreUtils (= 5.2.0) - - AppMetricaEncodingUtils (= 5.2.0) - - AppMetricaHostState (= 5.2.0) - - AppMetricaLog (= 5.2.0) - - AppMetricaNetwork (= 5.2.0) - - AppMetricaPlatform (= 5.2.0) - - AppMetricaProtobufUtils (= 5.2.0) - - AppMetricaStorageUtils (= 5.2.0) - - AppMetricaCoreExtension (5.2.0): - - AppMetricaCore (= 5.2.0) - - AppMetricaStorageUtils (= 5.2.0) - - AppMetricaCoreUtils (5.2.0): - - AppMetricaLog (= 5.2.0) - - AppMetricaCrashes (5.2.0): - - AppMetricaCore (= 5.2.0) - - AppMetricaCoreExtension (= 5.2.0) - - AppMetricaCoreUtils (= 5.2.0) - - AppMetricaEncodingUtils (= 5.2.0) - - AppMetricaHostState (= 5.2.0) - - AppMetricaLog (= 5.2.0) - - AppMetricaPlatform (= 5.2.0) - - AppMetricaProtobufUtils (= 5.2.0) - - AppMetricaStorageUtils (= 5.2.0) - - KSCrash/Recording (= 1.17.0) - - KSCrash/Recording/Tools - - AppMetricaEncodingUtils (5.2.0): - - AppMetricaCoreUtils (= 5.2.0) - - AppMetricaLog (= 5.2.0) - - AppMetricaPlatform (= 5.2.0) - - AppMetricaHostState (5.2.0): - - AppMetricaCoreUtils (= 5.2.0) - - AppMetricaLog (= 5.2.0) - - AppMetricaLog (5.2.0) - - AppMetricaNetwork (5.2.0): - - AppMetricaCoreUtils (= 5.2.0) - - AppMetricaLog (= 5.2.0) - - AppMetricaPlatform (= 5.2.0) - - AppMetricaPlatform (5.2.0): - - AppMetricaCoreUtils (= 5.2.0) - - AppMetricaLog (= 5.2.0) - - AppMetricaProtobufUtils (5.2.0): - - AppMetrica_Protobuf (= 5.2.0) - - AppMetricaStorageUtils (5.2.0): - - AppMetricaCoreUtils (= 5.2.0) - - AppMetricaLog (= 5.2.0) - - AppMetricaWebKit (5.2.0): - - AppMetricaCore (= 5.2.0) - - AppMetricaCoreUtils (= 5.2.0) - - AppMetricaLog (= 5.2.0) - - BoringSSL-GRPC (0.0.32): - - BoringSSL-GRPC/Implementation (= 0.0.32) - - BoringSSL-GRPC/Interface (= 0.0.32) - - BoringSSL-GRPC/Implementation (0.0.32): - - BoringSSL-GRPC/Interface (= 0.0.32) - - BoringSSL-GRPC/Interface (0.0.32) - - cloud_firestore (4.17.2): - - Firebase/Firestore (= 10.24.0) - - firebase_core - - Flutter - - device_info_plus (0.0.1): - - Flutter - - DivKit (28.13.0): - - DivKit_LayoutKit (= 28.13.0) - - DivKit_Serialization (= 28.13.0) - - VGSLCommonCore (~> 2.4) - - VGSLNetworking (~> 2.4) - - DivKit_LayoutKit (28.13.0): - - DivKit_LayoutKitInterface (= 28.13.0) - - VGSLCommonCore (~> 2.4) - - DivKit_LayoutKitInterface (28.13.0): - - VGSLBase (~> 2.4) - - VGSLBaseTiny (~> 2.4) - - VGSLBaseUI (~> 2.4) - - DivKit_Serialization (28.13.0): - - VGSLCommonCore (~> 2.4) - - DKImagePickerController/Core (4.3.9): - - DKImagePickerController/ImageDataManager - - DKImagePickerController/Resource - - DKImagePickerController/ImageDataManager (4.3.9) - - DKImagePickerController/PhotoGallery (4.3.9): - - DKImagePickerController/Core - - DKPhotoGallery - - DKImagePickerController/Resource (4.3.9) - - DKPhotoGallery (0.0.19): - - DKPhotoGallery/Core (= 0.0.19) - - DKPhotoGallery/Model (= 0.0.19) - - DKPhotoGallery/Preview (= 0.0.19) - - DKPhotoGallery/Resource (= 0.0.19) - - SDWebImage - - SwiftyGif - - DKPhotoGallery/Core (0.0.19): - - DKPhotoGallery/Model - - DKPhotoGallery/Preview - - SDWebImage - - SwiftyGif - - DKPhotoGallery/Model (0.0.19): - - SDWebImage - - SwiftyGif - - DKPhotoGallery/Preview (0.0.19): - - DKPhotoGallery/Model - - DKPhotoGallery/Resource - - SDWebImage - - SwiftyGif - - DKPhotoGallery/Resource (0.0.19): - - SDWebImage - - SwiftyGif - - file_picker (0.0.1): - - DKImagePickerController/PhotoGallery - - Flutter - - Firebase/CoreOnly (10.24.0): - - FirebaseCore (= 10.24.0) - - Firebase/Firestore (10.24.0): - - Firebase/CoreOnly - - FirebaseFirestore (~> 10.24.0) - - firebase_core (2.30.1): - - Firebase/CoreOnly (= 10.24.0) - - Flutter - - FirebaseAppCheckInterop (10.25.0) - - FirebaseCore (10.24.0): - - FirebaseCoreInternal (~> 10.0) - - GoogleUtilities/Environment (~> 7.12) - - GoogleUtilities/Logger (~> 7.12) - - FirebaseCoreExtension (10.25.0): - - FirebaseCore (~> 10.0) - - FirebaseCoreInternal (10.25.0): - - "GoogleUtilities/NSData+zlib (~> 7.8)" - - FirebaseFirestore (10.24.0): - - FirebaseCore (~> 10.0) - - FirebaseCoreExtension (~> 10.0) - - FirebaseFirestoreInternal (= 10.24.0) - - FirebaseSharedSwift (~> 10.0) - - FirebaseFirestoreInternal (10.24.0): - - abseil/algorithm (~> 1.20240116.1) - - abseil/base (~> 1.20240116.1) - - abseil/container/flat_hash_map (~> 1.20240116.1) - - abseil/memory (~> 1.20240116.1) - - abseil/meta (~> 1.20240116.1) - - abseil/strings/strings (~> 1.20240116.1) - - abseil/time (~> 1.20240116.1) - - abseil/types (~> 1.20240116.1) - - FirebaseAppCheckInterop (~> 10.17) - - FirebaseCore (~> 10.0) - - "gRPC-C++ (~> 1.62.0)" - - gRPC-Core (~> 1.62.0) - - leveldb-library (~> 1.22) - - nanopb (< 2.30911.0, >= 2.30908.0) - - FirebaseSharedSwift (10.25.0) - - Flutter (1.0.0) - - flutter_tts (0.0.1): - - Flutter - - FMobileSdk (2.0.0.1230) - - FunctionalSwift (2.0.7) - - google_sign_in_ios (0.0.1): - - AppAuth (>= 1.7.4) - - Flutter - - FlutterMacOS - - GoogleSignIn (~> 7.1) - - GTMSessionFetcher (>= 3.4.0) - - GoogleSignIn (7.1.0): - - AppAuth (< 2.0, >= 1.7.3) - - GTMAppAuth (< 5.0, >= 4.1.1) - - GTMSessionFetcher/Core (~> 3.3) - - GoogleUtilities/Environment (7.13.2): - - GoogleUtilities/Privacy - - PromisesObjC (< 3.0, >= 1.2) - - GoogleUtilities/Logger (7.13.2): - - GoogleUtilities/Environment - - GoogleUtilities/Privacy - - "GoogleUtilities/NSData+zlib (7.13.2)": - - GoogleUtilities/Privacy - - GoogleUtilities/Privacy (7.13.2) - - "gRPC-C++ (1.62.5)": - - "gRPC-C++/Implementation (= 1.62.5)" - - "gRPC-C++/Interface (= 1.62.5)" - - "gRPC-C++/Implementation (1.62.5)": - - abseil/algorithm/container (~> 1.20240116.2) - - abseil/base/base (~> 1.20240116.2) - - abseil/base/config (~> 1.20240116.2) - - abseil/base/core_headers (~> 1.20240116.2) - - abseil/cleanup/cleanup (~> 1.20240116.2) - - abseil/container/flat_hash_map (~> 1.20240116.2) - - abseil/container/flat_hash_set (~> 1.20240116.2) - - abseil/container/inlined_vector (~> 1.20240116.2) - - abseil/flags/flag (~> 1.20240116.2) - - abseil/flags/marshalling (~> 1.20240116.2) - - abseil/functional/any_invocable (~> 1.20240116.2) - - abseil/functional/bind_front (~> 1.20240116.2) - - abseil/functional/function_ref (~> 1.20240116.2) - - abseil/hash/hash (~> 1.20240116.2) - - abseil/memory/memory (~> 1.20240116.2) - - abseil/meta/type_traits (~> 1.20240116.2) - - abseil/random/bit_gen_ref (~> 1.20240116.2) - - abseil/random/distributions (~> 1.20240116.2) - - abseil/random/random (~> 1.20240116.2) - - abseil/status/status (~> 1.20240116.2) - - abseil/status/statusor (~> 1.20240116.2) - - abseil/strings/cord (~> 1.20240116.2) - - abseil/strings/str_format (~> 1.20240116.2) - - abseil/strings/strings (~> 1.20240116.2) - - abseil/synchronization/synchronization (~> 1.20240116.2) - - abseil/time/time (~> 1.20240116.2) - - abseil/types/optional (~> 1.20240116.2) - - abseil/types/span (~> 1.20240116.2) - - abseil/types/variant (~> 1.20240116.2) - - abseil/utility/utility (~> 1.20240116.2) - - "gRPC-C++/Interface (= 1.62.5)" - - "gRPC-C++/Privacy (= 1.62.5)" - - gRPC-Core (= 1.62.5) - - "gRPC-C++/Interface (1.62.5)" - - "gRPC-C++/Privacy (1.62.5)" - - gRPC-Core (1.62.5): - - gRPC-Core/Implementation (= 1.62.5) - - gRPC-Core/Interface (= 1.62.5) - - gRPC-Core/Implementation (1.62.5): - - abseil/algorithm/container (~> 1.20240116.2) - - abseil/base/base (~> 1.20240116.2) - - abseil/base/config (~> 1.20240116.2) - - abseil/base/core_headers (~> 1.20240116.2) - - abseil/cleanup/cleanup (~> 1.20240116.2) - - abseil/container/flat_hash_map (~> 1.20240116.2) - - abseil/container/flat_hash_set (~> 1.20240116.2) - - abseil/container/inlined_vector (~> 1.20240116.2) - - abseil/flags/flag (~> 1.20240116.2) - - abseil/flags/marshalling (~> 1.20240116.2) - - abseil/functional/any_invocable (~> 1.20240116.2) - - abseil/functional/bind_front (~> 1.20240116.2) - - abseil/functional/function_ref (~> 1.20240116.2) - - abseil/hash/hash (~> 1.20240116.2) - - abseil/memory/memory (~> 1.20240116.2) - - abseil/meta/type_traits (~> 1.20240116.2) - - abseil/random/bit_gen_ref (~> 1.20240116.2) - - abseil/random/distributions (~> 1.20240116.2) - - abseil/random/random (~> 1.20240116.2) - - abseil/status/status (~> 1.20240116.2) - - abseil/status/statusor (~> 1.20240116.2) - - abseil/strings/cord (~> 1.20240116.2) - - abseil/strings/str_format (~> 1.20240116.2) - - abseil/strings/strings (~> 1.20240116.2) - - abseil/synchronization/synchronization (~> 1.20240116.2) - - abseil/time/time (~> 1.20240116.2) - - abseil/types/optional (~> 1.20240116.2) - - abseil/types/span (~> 1.20240116.2) - - abseil/types/variant (~> 1.20240116.2) - - abseil/utility/utility (~> 1.20240116.2) - - BoringSSL-GRPC (= 0.0.32) - - gRPC-Core/Interface (= 1.62.5) - - gRPC-Core/Privacy (= 1.62.5) - - gRPC-Core/Interface (1.62.5) - - gRPC-Core/Privacy (1.62.5) - - GTMAppAuth (4.1.1): - - AppAuth/Core (~> 1.7) - - GTMSessionFetcher/Core (< 4.0, >= 3.3) - - GTMSessionFetcher (3.4.1): - - GTMSessionFetcher/Full (= 3.4.1) - - GTMSessionFetcher/Core (3.4.1) - - GTMSessionFetcher/Full (3.4.1): - - GTMSessionFetcher/Core - - in_app_purchase_storekit (0.0.1): - - Flutter - - FlutterMacOS - - KSCrash/Recording (1.17.0): - - KSCrash/Recording/Tools (= 1.17.0) - - KSCrash/Recording/Tools (1.17.0) - - leveldb-library (1.22.5) - - MoneyAuth (10.13.1): - - FMobileSdk - - FunctionalSwift (~> 2.0) - - YooMoneyCoreApi (~> 3.1) - - YooMoneySessionProfiler (~> 5.0) - - YooMoneyUI (~> 7.7) - - nanopb (2.30910.0): - - nanopb/decode (= 2.30910.0) - - nanopb/encode (= 2.30910.0) - - nanopb/decode (2.30910.0) - - nanopb/encode (2.30910.0) - - package_info_plus (0.4.5): - - Flutter - - path_provider_foundation (0.0.1): - - Flutter - - FlutterMacOS - - PromisesObjC (2.4.0) - - SDWebImage (5.19.1): - - SDWebImage/Core (= 5.19.1) - - SDWebImage/Core (5.19.1) - - shared_preferences_foundation (0.0.1): - - Flutter - - FlutterMacOS - - SPaySDK (1.0.9) - - SwiftyGif (5.4.5) - - VGSL_Fundamentals (2.4.1): - - VGSL_Fundamentals_Tiny (= 2.4.1) - - VGSL_Fundamentals_Tiny (2.4.1) - - VGSLBase (2.4.1): - - VGSL_Fundamentals (= 2.4.1) - - VGSLBaseTiny (= 2.4.1) - - VGSLBaseUI (= 2.4.1) - - VGSLBaseTiny (2.4.1): - - VGSL_Fundamentals_Tiny (= 2.4.1) - - VGSLBaseUI (2.4.1): - - VGSLBaseTiny (= 2.4.1) - - VGSLCommonCore (2.4.1): - - VGSLBase (= 2.4.1) - - VGSLNetworking (2.4.1): - - VGSLBase (= 2.4.1) - - yandex_mobileads (6.3.0): - - Flutter - - YandexMobileAds (~> 6.4.0) - - YandexMobileAds (6.4.1): - - DivKit (= 28.13.0) - - YandexMobileMetrica (< 5.0.0, >= 4.0.0) - - YandexMobileMetrica (4.5.2): - - YandexMobileMetrica/Static (= 4.5.2) - - YandexMobileMetrica/Static (4.5.2): - - YandexMobileMetrica/Static/Core (= 4.5.2) - - YandexMobileMetrica/Static/Crashes (= 4.5.2) - - YandexMobileMetrica/Static/Core (4.5.2) - - YandexMobileMetrica/Static/Crashes (4.5.2): - - YandexMobileMetrica/Static/Core - - yookassa_payments_flutter (1.3.1): - - Flutter - - YooKassaPayments (= 6.16.0) - - YooKassaPayments (6.16.0): - - AppMetricaAnalytics (~> 5.2.0) - - MoneyAuth (~> 10.13.0) - - SPaySDK (~> 1.0.8) - - YooKassaPaymentsApi (~> 2.22.0) - - YooKassaWalletApi (~> 2.6.0) - - YooMoneyCoreApi (~> 3.1) - - YooMoneySessionProfiler (~> 5.0.4) - - YooMoneyUI (~> 7.7) - - YooKassaPaymentsApi (2.22.0): - - FunctionalSwift - - YooMoneyCoreApi (~> 3.1.1) - - YooKassaWalletApi (2.6.0): - - FunctionalSwift - - YooMoneyCoreApi - - YooMoneyCoreApi (3.1.1): - - FunctionalSwift (~> 2.0) - - YooMoneySessionProfiler (5.0.4): - - FMobileSdk - - YooMoneyUI (7.7.6): - - FunctionalSwift - -DEPENDENCIES: - - cloud_firestore (from `.symlinks/plugins/cloud_firestore/ios`) - - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - - file_picker (from `.symlinks/plugins/file_picker/ios`) - - firebase_core (from `.symlinks/plugins/firebase_core/ios`) - - Flutter (from `Flutter`) - - flutter_tts (from `.symlinks/plugins/flutter_tts/ios`) - - google_sign_in_ios (from `.symlinks/plugins/google_sign_in_ios/darwin`) - - in_app_purchase_storekit (from `.symlinks/plugins/in_app_purchase_storekit/darwin`) - - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - - yandex_mobileads (from `.symlinks/plugins/yandex_mobileads/ios`) - - yookassa_payments_flutter (from `.symlinks/plugins/yookassa_payments_flutter/ios`) - -SPEC REPOS: - https://git.yoomoney.ru/scm/sdk/cocoa-pod-specs.git: - - FMobileSdk - - FunctionalSwift - - MoneyAuth - - YooKassaPayments - - YooKassaPaymentsApi - - YooKassaWalletApi - - YooMoneyCoreApi - - YooMoneySessionProfiler - - YooMoneyUI - https://github.com/CocoaPods/Specs.git: - - abseil - - AppAuth - - AppMetrica_FMDB - - AppMetrica_Protobuf - - AppMetricaAdSupport - - AppMetricaAnalytics - - AppMetricaCore - - AppMetricaCoreExtension - - AppMetricaCoreUtils - - AppMetricaCrashes - - AppMetricaEncodingUtils - - AppMetricaHostState - - AppMetricaLog - - AppMetricaNetwork - - AppMetricaPlatform - - AppMetricaProtobufUtils - - AppMetricaStorageUtils - - AppMetricaWebKit - - BoringSSL-GRPC - - DivKit - - DivKit_LayoutKit - - DivKit_LayoutKitInterface - - DivKit_Serialization - - DKImagePickerController - - DKPhotoGallery - - Firebase - - FirebaseAppCheckInterop - - FirebaseCore - - FirebaseCoreExtension - - FirebaseCoreInternal - - FirebaseFirestore - - FirebaseFirestoreInternal - - FirebaseSharedSwift - - GoogleSignIn - - GoogleUtilities - - "gRPC-C++" - - gRPC-Core - - GTMAppAuth - - GTMSessionFetcher - - KSCrash - - leveldb-library - - nanopb - - PromisesObjC - - SDWebImage - - SPaySDK - - SwiftyGif - - VGSL_Fundamentals - - VGSL_Fundamentals_Tiny - - VGSLBase - - VGSLBaseTiny - - VGSLBaseUI - - VGSLCommonCore - - VGSLNetworking - - YandexMobileAds - - YandexMobileMetrica - -EXTERNAL SOURCES: - cloud_firestore: - :path: ".symlinks/plugins/cloud_firestore/ios" - device_info_plus: - :path: ".symlinks/plugins/device_info_plus/ios" - file_picker: - :path: ".symlinks/plugins/file_picker/ios" - firebase_core: - :path: ".symlinks/plugins/firebase_core/ios" - Flutter: - :path: Flutter - flutter_tts: - :path: ".symlinks/plugins/flutter_tts/ios" - google_sign_in_ios: - :path: ".symlinks/plugins/google_sign_in_ios/darwin" - in_app_purchase_storekit: - :path: ".symlinks/plugins/in_app_purchase_storekit/darwin" - package_info_plus: - :path: ".symlinks/plugins/package_info_plus/ios" - path_provider_foundation: - :path: ".symlinks/plugins/path_provider_foundation/darwin" - shared_preferences_foundation: - :path: ".symlinks/plugins/shared_preferences_foundation/darwin" - yandex_mobileads: - :path: ".symlinks/plugins/yandex_mobileads/ios" - yookassa_payments_flutter: - :path: ".symlinks/plugins/yookassa_payments_flutter/ios" - -SPEC CHECKSUMS: - abseil: d121da9ef7e2ff4cab7666e76c5a3e0915ae08c3 - AppAuth: 501c04eda8a8d11f179dbe8637b7a91bb7e5d2fa - AppMetrica_FMDB: 86a7247cecf4b315735b119f8547779bffca535a - AppMetrica_Protobuf: 326de64e6b52ab2cddce121c780461ac9eedb6c4 - AppMetricaAdSupport: 1ebdf7d6f4555675aa776fb7fee5bd96ebef1dcf - AppMetricaAnalytics: bebf8c4f75c5015937b3e51f878b30693d293944 - AppMetricaCore: 647efed7efaa8fad9e38aa417d95aeccaf8b0926 - AppMetricaCoreExtension: 2b93264b869438d890df5bf6a69407fce5542e1c - AppMetricaCoreUtils: f6b3cfde963e1027e3044630e2fd7e1007422c1b - AppMetricaCrashes: f096e2cee83a46769685b850df846f5822c81b24 - AppMetricaEncodingUtils: 3b7d0aafefbc9a0ae84515a4b381d0a576f944f2 - AppMetricaHostState: 280370ecaf3096d4ff313bb7c13fb13252ae99e7 - AppMetricaLog: 7f5b21edad9e93e12d5d8e2aeafd17cbc2befb2b - AppMetricaNetwork: 070f7ce9fcad0e97d762d76ed7616236f9c73417 - AppMetricaPlatform: 36742fdd5e4290ab923cf5ede28612dae96a6671 - AppMetricaProtobufUtils: 37f172ca2fffacba2f0d564308c873bba4726b88 - AppMetricaStorageUtils: 4de179f2354946734cc7c407322398f5276e3305 - AppMetricaWebKit: 271bdf19ac5473df925213ec80d330805ed96818 - BoringSSL-GRPC: 1e2348957acdbcad360b80a264a90799984b2ba6 - cloud_firestore: a291c7ca0e498f9b08bbabd47a2499a5ae02c152 - device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6 - DivKit: 65af423ea03c32b6ecaa63a64d4a2ea26c217f3b - DivKit_LayoutKit: 3f88fab9edefb4938b9ccb5359ecbce4e1eeba69 - DivKit_LayoutKitInterface: 90449756ba956a7fbf3b168c01f7a0b7fc6a9de0 - DivKit_Serialization: 1b536e30c49163548bb62120320097c8ab41936d - DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c - DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 - file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655 - Firebase: 91fefd38712feb9186ea8996af6cbdef41473442 - firebase_core: 7f1e1156934d0da3be260174812842df9420e4ab - FirebaseAppCheckInterop: 5da5ce93e8797a215e3f677fb0654b74e736c8b8 - FirebaseCore: 11dc8a16dfb7c5e3c3f45ba0e191a33ac4f50894 - FirebaseCoreExtension: 8a47811d0b155501559ef05d089518152a0a1677 - FirebaseCoreInternal: 910a81992c33715fec9263ca7381d59ab3a750b7 - FirebaseFirestore: 6df1bc70a56c15921286ff2a3096fa2d350b8823 - FirebaseFirestoreInternal: d9a6e08e9bb4016ce7c0b3544f1cf7abcd7cf26f - FirebaseSharedSwift: 0274086954b1b2d5fd7e829eccc587044d72a4ba - Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - flutter_tts: 0f492aab6accf87059b72354fcb4ba934304771d - FMobileSdk: 8c49b5c8207eb4b92df761bbf16486f03bdac6ac - FunctionalSwift: 89f68db7d8cf103a4dc0878fbaa007a2a5532485 - google_sign_in_ios: 07375bfbf2620bc93a602c0e27160d6afc6ead38 - GoogleSignIn: d4281ab6cf21542b1cfaff85c191f230b399d2db - GoogleUtilities: c56430aef51a1aa57b25da78c3f8397e522c67b7 - "gRPC-C++": e725ef63c4475d7cdb7e2cf16eb0fde84bd9ee51 - gRPC-Core: eee4be35df218649fe66d721a05a7f27a28f069b - GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de - GTMSessionFetcher: 8000756fc1c19d2e5697b90311f7832d2e33f6cd - in_app_purchase_storekit: 0e4b3c2e43ba1e1281f4f46dd71b0593ce529892 - KSCrash: 593ec373759e4c1bce381421a627326a20d2dc66 - leveldb-library: e8eadf9008a61f9e1dde3978c086d2b6d9b9dc28 - MoneyAuth: e577da32ec35b502043c3766daf99a0054ec86eb - nanopb: 438bc412db1928dac798aa6fd75726007be04262 - package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c - path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c - PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 - SDWebImage: 40b0b4053e36c660a764958bff99eed16610acbb - shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695 - SPaySDK: 1015f868b6e9255457704cd6a3d051829263661a - SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 - VGSL_Fundamentals: 4a965a687c76a5a435d7aaeb6b7182e82408bf4c - VGSL_Fundamentals_Tiny: feab274b20c5838fbe09b887db0f6cfa28e26a1c - VGSLBase: 37b95dd4ea137f73e8f9a8bd8ec474d8b4d75a68 - VGSLBaseTiny: 0a0a4992a348184e8580cf73efa8eb373d750f6b - VGSLBaseUI: 62f147155e7b7049a32a30ac87b5bd8b83e636c5 - VGSLCommonCore: dd33088af6633d1c09f0c9bb8027920b99e2e0c8 - VGSLNetworking: e12d616b447522bf524c689d28f55cfbb20473cd - yandex_mobileads: 410b1d02d86e07cb38d1c9eb2fbf9384410f9619 - YandexMobileAds: ed587c669f2deb078f39a070ee2466cc40eca5a9 - YandexMobileMetrica: f5368ee93f286c793d73b58da00929babfc897c1 - yookassa_payments_flutter: f36a2380714a41ecd238667db3768052ab1c2240 - YooKassaPayments: 1f1e5daab12dd05b11e56fe568c71b5a43b77b14 - YooKassaPaymentsApi: 88ecca54eaf785787eeb1682f352016757ec0cdc - YooKassaWalletApi: 034a9d4f122557377ae3b9284e6292603fb38e6b - YooMoneyCoreApi: f53a22898878aab2f07449351a8a08f4918a6100 - YooMoneySessionProfiler: f4ac772cb6675f1200efbde75d69126eb4c867e6 - YooMoneyUI: 7332c851831398441dc841bb6474322ee5b8883d - -PODFILE CHECKSUM: 84385eb1972ae5f3226ffd3e0f88ab4954cec863 - -COCOAPODS: 1.14.3 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index 76634b0..0000000 --- a/ios/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,766 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 54; - objects = { - -/* Begin PBXBuildFile section */ - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 88925E0304DA934B99CF07B5 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 039AEB256EC09C6DAD0D33F6 /* GoogleService-Info.plist */; }; - 92F786B29EF12520764C9B34 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88D363FEAADB3317B3E14A0A /* Pods_Runner.framework */; }; - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - CF53E9FF8298D562D675E324 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F30B1603BBE3F803AFE2385E /* Pods_RunnerTests.framework */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 97C146E61CF9000F007C117D /* Project object */; - proxyType = 1; - remoteGlobalIDString = 97C146ED1CF9000F007C117D; - remoteInfo = Runner; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 9705A1C41CF9048500538489 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 039AEB256EC09C6DAD0D33F6 /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = ""; }; - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; - 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 4CC79C7D41E157D878D889F0 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - 5B971DEAC48A015D192A8EAB /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - 5DA2E070BDB961F389CD961D /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - 5EFDFD0076C43FC4B256A2F9 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 88D363FEAADB3317B3E14A0A /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; - 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - D56245B45AA4EC506A74FF4E /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; - F30B1603BBE3F803AFE2385E /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - F34F38B9F662A96F77B6E782 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 7F8493084A5D32479BB9EA15 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - CF53E9FF8298D562D675E324 /* Pods_RunnerTests.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 97C146EB1CF9000F007C117D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 92F786B29EF12520764C9B34 /* Pods_Runner.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 331C8082294A63A400263BE5 /* RunnerTests */ = { - isa = PBXGroup; - children = ( - 331C807B294A618700263BE5 /* RunnerTests.swift */, - ); - path = RunnerTests; - sourceTree = ""; - }; - 70EAEEDC0F0D76C181949289 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 88D363FEAADB3317B3E14A0A /* Pods_Runner.framework */, - F30B1603BBE3F803AFE2385E /* Pods_RunnerTests.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - 9740EEB11CF90186004384FC /* Flutter */ = { - isa = PBXGroup; - children = ( - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 9740EEB31CF90195004384FC /* Generated.xcconfig */, - ); - name = Flutter; - sourceTree = ""; - }; - 97C146E51CF9000F007C117D = { - isa = PBXGroup; - children = ( - 9740EEB11CF90186004384FC /* Flutter */, - 97C146F01CF9000F007C117D /* Runner */, - 97C146EF1CF9000F007C117D /* Products */, - 331C8082294A63A400263BE5 /* RunnerTests */, - 9BC5FF8B20D1CEE670C66ACD /* Pods */, - 70EAEEDC0F0D76C181949289 /* Frameworks */, - 039AEB256EC09C6DAD0D33F6 /* GoogleService-Info.plist */, - ); - sourceTree = ""; - }; - 97C146EF1CF9000F007C117D /* Products */ = { - isa = PBXGroup; - children = ( - 97C146EE1CF9000F007C117D /* Runner.app */, - 331C8081294A63A400263BE5 /* RunnerTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - 97C146F01CF9000F007C117D /* Runner */ = { - isa = PBXGroup; - children = ( - 97C146FA1CF9000F007C117D /* Main.storyboard */, - 97C146FD1CF9000F007C117D /* Assets.xcassets */, - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, - 97C147021CF9000F007C117D /* Info.plist */, - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, - ); - path = Runner; - sourceTree = ""; - }; - 9BC5FF8B20D1CEE670C66ACD /* Pods */ = { - isa = PBXGroup; - children = ( - 4CC79C7D41E157D878D889F0 /* Pods-Runner.debug.xcconfig */, - 5B971DEAC48A015D192A8EAB /* Pods-Runner.release.xcconfig */, - 5DA2E070BDB961F389CD961D /* Pods-Runner.profile.xcconfig */, - 5EFDFD0076C43FC4B256A2F9 /* Pods-RunnerTests.debug.xcconfig */, - F34F38B9F662A96F77B6E782 /* Pods-RunnerTests.release.xcconfig */, - D56245B45AA4EC506A74FF4E /* Pods-RunnerTests.profile.xcconfig */, - ); - name = Pods; - path = Pods; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 331C8080294A63A400263BE5 /* RunnerTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; - buildPhases = ( - FD56395D2DB589B058B3AF52 /* [CP] Check Pods Manifest.lock */, - 331C807D294A63A400263BE5 /* Sources */, - 331C807F294A63A400263BE5 /* Resources */, - 7F8493084A5D32479BB9EA15 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 331C8086294A63A400263BE5 /* PBXTargetDependency */, - ); - name = RunnerTests; - productName = RunnerTests; - productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 97C146ED1CF9000F007C117D /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - 11939B8737D0761B1D52BA09 /* [CP] Check Pods Manifest.lock */, - 9740EEB61CF901F6004384FC /* Run Script */, - 97C146EA1CF9000F007C117D /* Sources */, - 97C146EB1CF9000F007C117D /* Frameworks */, - 97C146EC1CF9000F007C117D /* Resources */, - 9705A1C41CF9048500538489 /* Embed Frameworks */, - 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 10510DEBDDA8DED725BA2250 /* [CP] Copy Pods Resources */, - EAA1E3F7E6B26606E42D1048 /* [CP] Embed Pods Frameworks */, - 8D11678BA08A0DD0DA55F454 /* FlutterFire: "flutterfire upload-crashlytics-symbols" */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Runner; - productName = Runner; - productReference = 97C146EE1CF9000F007C117D /* Runner.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 97C146E61CF9000F007C117D /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = YES; - LastUpgradeCheck = 1510; - ORGANIZATIONNAME = ""; - TargetAttributes = { - 331C8080294A63A400263BE5 = { - CreatedOnToolsVersion = 14.0; - TestTargetID = 97C146ED1CF9000F007C117D; - }; - 97C146ED1CF9000F007C117D = { - CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 1100; - }; - }; - }; - buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 97C146E51CF9000F007C117D; - productRefGroup = 97C146EF1CF9000F007C117D /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 97C146ED1CF9000F007C117D /* Runner */, - 331C8080294A63A400263BE5 /* RunnerTests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 331C807F294A63A400263BE5 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 97C146EC1CF9000F007C117D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, - 88925E0304DA934B99CF07B5 /* GoogleService-Info.plist in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 10510DEBDDA8DED725BA2250 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - 11939B8737D0761B1D52BA09 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", - ); - name = "Thin Binary"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; - }; - 8D11678BA08A0DD0DA55F454 /* FlutterFire: "flutterfire upload-crashlytics-symbols" */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "FlutterFire: \"flutterfire upload-crashlytics-symbols\""; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\n#!/bin/bash\nPATH=${PATH}:$FLUTTER_ROOT/bin:$HOME/.pub-cache/bin\nflutterfire upload-crashlytics-symbols --upload-symbols-script-path=$PODS_ROOT/FirebaseCrashlytics/upload-symbols --platform=ios --apple-project-path=${SRCROOT} --env-platform-name=${PLATFORM_NAME} --env-configuration=${CONFIGURATION} --env-project-dir=${PROJECT_DIR} --env-built-products-dir=${BUILT_PRODUCTS_DIR} --env-dwarf-dsym-folder-path=${DWARF_DSYM_FOLDER_PATH} --env-dwarf-dsym-file-name=${DWARF_DSYM_FILE_NAME} --env-infoplist-path=${INFOPLIST_PATH} --default-config=default\n"; - }; - 9740EEB61CF901F6004384FC /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; - }; - EAA1E3F7E6B26606E42D1048 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - FD56395D2DB589B058B3AF52 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 331C807D294A63A400263BE5 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 97C146EA1CF9000F007C117D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 97C146ED1CF9000F007C117D /* Runner */; - targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 97C146FA1CF9000F007C117D /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C146FB1CF9000F007C117D /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C147001CF9000F007C117D /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 249021D3217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Profile; - }; - 249021D4217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 6ZNY923QXG; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.cinnabarflower.mnemoCards; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Profile; - }; - 331C8088294A63A400263BE5 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 5EFDFD0076C43FC4B256A2F9 /* Pods-RunnerTests.debug.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.cinnabarflower.mnemoCards.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; - }; - name = Debug; - }; - 331C8089294A63A400263BE5 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = F34F38B9F662A96F77B6E782 /* Pods-RunnerTests.release.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.cinnabarflower.mnemoCards.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; - }; - name = Release; - }; - 331C808A294A63A400263BE5 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D56245B45AA4EC506A74FF4E /* Pods-RunnerTests.profile.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.cinnabarflower.mnemoCards.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; - }; - name = Profile; - }; - 97C147031CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 97C147041CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 97C147061CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 6ZNY923QXG; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.cinnabarflower.mnemoCards; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Debug; - }; - 97C147071CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 6ZNY923QXG; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.cinnabarflower.mnemoCards; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 331C8088294A63A400263BE5 /* Debug */, - 331C8089294A63A400263BE5 /* Release */, - 331C808A294A63A400263BE5 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147031CF9000F007C117D /* Debug */, - 97C147041CF9000F007C117D /* Release */, - 249021D3217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147061CF9000F007C117D /* Debug */, - 97C147071CF9000F007C117D /* Release */, - 249021D4217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 97C146E61CF9000F007C117D /* Project object */; -} diff --git a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a..0000000 --- a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c..0000000 --- a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index 8e3ca5d..0000000 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 21a3cc1..0000000 --- a/ios/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c..0000000 --- a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift deleted file mode 100644 index 70693e4..0000000 --- a/ios/Runner/AppDelegate.swift +++ /dev/null @@ -1,13 +0,0 @@ -import UIKit -import Flutter - -@UIApplicationMain -@objc class AppDelegate: FlutterAppDelegate { - override func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? - ) -> Bool { - GeneratedPluginRegistrant.register(with: self) - return super.application(application, didFinishLaunchingWithOptions: launchOptions) - } -} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index d36b1fa..0000000 --- a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "images" : [ - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@3x.png", - "scale" : "3x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" - }, - { - "size" : "1024x1024", - "idiom" : "ios-marketing", - "filename" : "Icon-App-1024x1024@1x.png", - "scale" : "1x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png deleted file mode 100644 index 8efca3a..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png deleted file mode 100644 index 226e320..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png deleted file mode 100644 index 9b3f66b..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png deleted file mode 100644 index 03475e4..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png deleted file mode 100644 index e576db3..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png deleted file mode 100644 index 5c19bdc..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png deleted file mode 100644 index 9d26dc7..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png deleted file mode 100644 index 9b3f66b..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png deleted file mode 100644 index 5fd1173..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png deleted file mode 100644 index 88a0f0a..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png deleted file mode 100644 index 1d65fd8..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png deleted file mode 100644 index 39f354c..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png deleted file mode 100644 index df61d9a..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png deleted file mode 100644 index a2b6962..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png deleted file mode 100644 index 88a0f0a..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png deleted file mode 100644 index 585e88e..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png deleted file mode 100644 index 6f591a2..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png deleted file mode 100644 index e15861a..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png deleted file mode 100644 index 8ed25e7..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png deleted file mode 100644 index 2aa1201..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png deleted file mode 100644 index e8ceae7..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json deleted file mode 100644 index 9f447e1..0000000 --- a/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "background.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png deleted file mode 100644 index 3107d37..0000000 Binary files a/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json deleted file mode 100644 index 00cabce..0000000 --- a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "filename" : "LaunchImage.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "LaunchImage@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "LaunchImage@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png deleted file mode 100644 index b8e69a5..0000000 Binary files a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png deleted file mode 100644 index aa97e95..0000000 Binary files a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png deleted file mode 100644 index 1d18d62..0000000 Binary files a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md deleted file mode 100644 index 89c2725..0000000 --- a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Launch Screen Assets - -You can customize the launch screen with your own desired assets by replacing the image files in this directory. - -You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index 8d2b7d5..0000000 --- a/ios/Runner/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard deleted file mode 100644 index f3c2851..0000000 --- a/ios/Runner/Base.lproj/Main.storyboard +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ios/Runner/GoogleService-Info.plist b/ios/Runner/GoogleService-Info.plist deleted file mode 100644 index 813a0db..0000000 --- a/ios/Runner/GoogleService-Info.plist +++ /dev/null @@ -1,36 +0,0 @@ - - - - - CLIENT_ID - 701767851968-8dqcmk706p08gujqbl2m9s4sq1aljibs.apps.googleusercontent.com - REVERSED_CLIENT_ID - com.googleusercontent.apps.701767851968-8dqcmk706p08gujqbl2m9s4sq1aljibs - ANDROID_CLIENT_ID - 701767851968-3jgootslus3ie76t682j4v7glletloud.apps.googleusercontent.com - API_KEY - AIzaSyAodY8s0ntALNeeGiUiTx6eg4g2Ar6C1to - GCM_SENDER_ID - 701767851968 - PLIST_VERSION - 1 - BUNDLE_ID - com.cinnabarflower.mnemoCards - PROJECT_ID - mnemo-cards - STORAGE_BUCKET - mnemo-cards.appspot.com - IS_ADS_ENABLED - - IS_ANALYTICS_ENABLED - - IS_APPINVITE_ENABLED - - IS_GCM_ENABLED - - IS_SIGNIN_ENABLED - - GOOGLE_APP_ID - 1:701767851968:ios:5c9040634eac8c152f7225 - - \ No newline at end of file diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist deleted file mode 100644 index b32e312..0000000 --- a/ios/Runner/Info.plist +++ /dev/null @@ -1,967 +0,0 @@ - - - - - CFBundleURLTypes - - - CFBundleTypeRole - Editor - CFBundleURLSchemes - - yookassapaymentsflutter - - - - LSApplicationQueriesSchemes - - yoomoneyauth - sberpay - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - Mnemo Cards - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - mnemo_cards - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleSignature - ???? - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - CADisableMinimumFrameDurationOnPhone - - UIApplicationSupportsIndirectInputEvents - - SKAdNetworkItems - - - - SKAdNetworkIdentifier - zq492l623r.skadnetwork - - - - SKAdNetworkIdentifier - cstr6suwn9.skadnetwork - - - - SKAdNetworkIdentifier - n9x2a789qt.skadnetwork - - - - SKAdNetworkIdentifier - r26jy69rpl.skadnetwork - - - - SKAdNetworkIdentifier - 5l3tpt7t6e.skadnetwork - - - - SKAdNetworkIdentifier - 4dzt52r2t5.skadnetwork - - - - SKAdNetworkIdentifier - su67r6k2v3.skadnetwork - - - - SKAdNetworkIdentifier - ludvb6z3bs.skadnetwork - - - - SKAdNetworkIdentifier - kbd757ywx3.skadnetwork - - - SKAdNetworkIdentifier - 633vhxswh4.skadnetwork - - - SKAdNetworkIdentifier - tmhh9296z4.skadnetwork - - - SKAdNetworkIdentifier - vcra2ehyfk.skadnetwork - - - SKAdNetworkIdentifier - zh3b7bxvad.skadnetwork - - - SKAdNetworkIdentifier - xmn954pzmp.skadnetwork - - - SKAdNetworkIdentifier - 79w64w269u.skadnetwork - - - SKAdNetworkIdentifier - 488r3q3dtq.skadnetwork - - - SKAdNetworkIdentifier - d7g9azk84q.skadnetwork - - - SKAdNetworkIdentifier - nzq8sh4pbs.skadnetwork - - - SKAdNetworkIdentifier - 866k9ut3g3.skadnetwork - - - SKAdNetworkIdentifier - 2q884k2j68.skadnetwork - - - SKAdNetworkIdentifier - x8jxxk4ff5.skadnetwork - - - SKAdNetworkIdentifier - gfat3222tu.skadnetwork - - - SKAdNetworkIdentifier - pd25vrrwzn.skadnetwork - - - SKAdNetworkIdentifier - lr83yxwka7.skadnetwork - - - SKAdNetworkIdentifier - cp8zw746q7.skadnetwork - - - SKAdNetworkIdentifier - pwdxu55a5a.skadnetwork - - - SKAdNetworkIdentifier - c6k4g5qg8m.skadnetwork - - - SKAdNetworkIdentifier - s39g8k73mm.skadnetwork - - - SKAdNetworkIdentifier - wg4vff78zm.skadnetwork - - - SKAdNetworkIdentifier - g28c52eehv.skadnetwork - - - SKAdNetworkIdentifier - 523jb4fst2.skadnetwork - - - SKAdNetworkIdentifier - 294l99pt4k.skadnetwork - - - SKAdNetworkIdentifier - 3qy4746246.skadnetwork - - - SKAdNetworkIdentifier - a8cz6cu7e5.skadnetwork - - - SKAdNetworkIdentifier - ggvn48r87g.skadnetwork - - - SKAdNetworkIdentifier - y755zyxw56.skadnetwork - - - SKAdNetworkIdentifier - qlbq5gtkt8.skadnetwork - - - SKAdNetworkIdentifier - mls7yz5dvl.skadnetwork - - - SKAdNetworkIdentifier - 67369282zy.skadnetwork - - - SKAdNetworkIdentifier - 899vrgt9g8.skadnetwork - - - SKAdNetworkIdentifier - mj797d8u6f.skadnetwork - - - SKAdNetworkIdentifier - 3sh42y64q3.skadnetwork - - - SKAdNetworkIdentifier - f38h382jlk.skadnetwork - - - SKAdNetworkIdentifier - 24t9a8vw3c.skadnetwork - - - SKAdNetworkIdentifier - mp6xlyr22a.skadnetwork - - - SKAdNetworkIdentifier - x44k69ngh6.skadnetwork - - - SKAdNetworkIdentifier - 88k8774x49.skadnetwork - - - SKAdNetworkIdentifier - hs6bdukanm.skadnetwork - - - SKAdNetworkIdentifier - t3b3f7n3x8.skadnetwork - - - SKAdNetworkIdentifier - prcb7njmu6.skadnetwork - - - SKAdNetworkIdentifier - c7g47wypnu.skadnetwork - - - SKAdNetworkIdentifier - 52fl2v3hgk.skadnetwork - - - SKAdNetworkIdentifier - 9vvzujtq5s.skadnetwork - - - SKAdNetworkIdentifier - m8dbw4sv7c.skadnetwork - - - SKAdNetworkIdentifier - 9g2aggbj52.skadnetwork - - - SKAdNetworkIdentifier - m5mvw97r93.skadnetwork - - - SKAdNetworkIdentifier - z5b3gh5ugf.skadnetwork - - - SKAdNetworkIdentifier - dd3a75yxkv.skadnetwork - - - SKAdNetworkIdentifier - 9nlqeag3gk.skadnetwork - - - SKAdNetworkIdentifier - cj5566h2ga.skadnetwork - - - SKAdNetworkIdentifier - h5jmj969g5.skadnetwork - - - SKAdNetworkIdentifier - dr774724x4.skadnetwork - - - SKAdNetworkIdentifier - t7ky8fmwkd.skadnetwork - - - SKAdNetworkIdentifier - fz2k2k5tej.skadnetwork - - - SKAdNetworkIdentifier - u679fj5vs4.skadnetwork - - - SKAdNetworkIdentifier - cs644xg564.skadnetwork - - - SKAdNetworkIdentifier - 9b89h5y424.skadnetwork - - - SKAdNetworkIdentifier - w28pnjg2k4.skadnetwork - - - SKAdNetworkIdentifier - 2rq3zucswp.skadnetwork - - - SKAdNetworkIdentifier - a7xqa6mtl2.skadnetwork - - - SKAdNetworkIdentifier - g2y4y55b64.skadnetwork - - - SKAdNetworkIdentifier - vc83br9sjg.skadnetwork - - - SKAdNetworkIdentifier - eqhxz8m8av.skadnetwork - - - SKAdNetworkIdentifier - 7k3cvf297u.skadnetwork - - - SKAdNetworkIdentifier - w9q455wk68.skadnetwork - - - SKAdNetworkIdentifier - nu4557a4je.skadnetwork - - - SKAdNetworkIdentifier - v4nxqhlyqp.skadnetwork - - - SKAdNetworkIdentifier - wzmmz9fp6w.skadnetwork - - - SKAdNetworkIdentifier - 7fmhfwg9en.skadnetwork - - - SKAdNetworkIdentifier - yclnxrl5pm.skadnetwork - - - SKAdNetworkIdentifier - 7tnzynbdc7.skadnetwork - - - SKAdNetworkIdentifier - l6nv3x923s.skadnetwork - - - SKAdNetworkIdentifier - h8vml93bkz.skadnetwork - - - SKAdNetworkIdentifier - uzqba5354d.skadnetwork - - - SKAdNetworkIdentifier - 8qiegk9qfv.skadnetwork - - - SKAdNetworkIdentifier - v79kvwwj4g.skadnetwork - - - SKAdNetworkIdentifier - xx9sdjej2w.skadnetwork - - - SKAdNetworkIdentifier - au67k4efj4.skadnetwork - - - SKAdNetworkIdentifier - t38b2kh725.skadnetwork - - - SKAdNetworkIdentifier - 7ug5zh24hu.skadnetwork - - - SKAdNetworkIdentifier - rx5hdcabgc.skadnetwork - - - SKAdNetworkIdentifier - 5lm9lj6jb7.skadnetwork - - - SKAdNetworkIdentifier - qqp299437r.skadnetwork - - - SKAdNetworkIdentifier - zmvfpc5aq8.skadnetwork - - - SKAdNetworkIdentifier - 9rd848q2bz.skadnetwork - - - SKAdNetworkIdentifier - 79pbpufp6p.skadnetwork - - - SKAdNetworkIdentifier - dmv22haz9p.skadnetwork - - - SKAdNetworkIdentifier - y5ghdn5j9k.skadnetwork - - - SKAdNetworkIdentifier - n6fk4nfna4.skadnetwork - - - SKAdNetworkIdentifier - 7rz58n8ntl.skadnetwork - - - SKAdNetworkIdentifier - v9wttpbfk9.skadnetwork - - - SKAdNetworkIdentifier - n38lu8286q.skadnetwork - - - SKAdNetworkIdentifier - feyaarzu9v.skadnetwork - - - SKAdNetworkIdentifier - 7fbxrn65az.skadnetwork - - - SKAdNetworkIdentifier - 47vhws6wlr.skadnetwork - - - SKAdNetworkIdentifier - ejvt5qm6ak.skadnetwork - - - SKAdNetworkIdentifier - b55w3d8y8z.skadnetwork - - - SKAdNetworkIdentifier - v7896pgt74.skadnetwork - - - SKAdNetworkIdentifier - 5ghnmfs3dh.skadnetwork - - - SKAdNetworkIdentifier - 275upjj5gd.skadnetwork - - - SKAdNetworkIdentifier - 627r9wr2y5.skadnetwork - - - SKAdNetworkIdentifier - sczv5946wb.skadnetwork - - - SKAdNetworkIdentifier - 8w3np9l82g.skadnetwork - - - SKAdNetworkIdentifier - hb56zgv37p.skadnetwork - - - SKAdNetworkIdentifier - 9t245vhmpl.skadnetwork - - - SKAdNetworkIdentifier - nrt9jy4kw9.skadnetwork - - - SKAdNetworkIdentifier - 7953jerfzd.skadnetwork - - - SKAdNetworkIdentifier - dn942472g5.skadnetwork - - - SKAdNetworkIdentifier - 6v7lgmsu45.skadnetwork - - - SKAdNetworkIdentifier - cad8qz2s3j.skadnetwork - - - SKAdNetworkIdentifier - eh6m2bh4zr.skadnetwork - - - SKAdNetworkIdentifier - jb7bn6koa5.skadnetwork - - - SKAdNetworkIdentifier - fkak3gfpt6.skadnetwork - - - SKAdNetworkIdentifier - a2p9lx4jpn.skadnetwork - - - SKAdNetworkIdentifier - 97r2b46745.skadnetwork - - - SKAdNetworkIdentifier - 22mmun2rn5.skadnetwork - - - SKAdNetworkIdentifier - 238da6jt44.skadnetwork - - - SKAdNetworkIdentifier - 44jx6755aq.skadnetwork - - - SKAdNetworkIdentifier - b9bk5wbcq9.skadnetwork - - - SKAdNetworkIdentifier - k674qkevps.skadnetwork - - - SKAdNetworkIdentifier - tl55sbb4fm.skadnetwork - - - SKAdNetworkIdentifier - 24zw6aqk47.skadnetwork - - - SKAdNetworkIdentifier - 4468km3ulz.skadnetwork - - - SKAdNetworkIdentifier - 2tdux39lx8.skadnetwork - - - SKAdNetworkIdentifier - 2u9pt9hc89.skadnetwork - - - SKAdNetworkIdentifier - 8s468mfl3y.skadnetwork - - - SKAdNetworkIdentifier - 3cgn6rq224.skadnetwork - - - SKAdNetworkIdentifier - glqzh8vgby.skadnetwork - - - SKAdNetworkIdentifier - av6w8kgt66.skadnetwork - - - SKAdNetworkIdentifier - klf5c3l5u5.skadnetwork - - - SKAdNetworkIdentifier - nfqy3847ph.skadnetwork - - - SKAdNetworkIdentifier - dticjx1a9i.skadnetwork - - - SKAdNetworkIdentifier - ppxm28t8ap.skadnetwork - - - SKAdNetworkIdentifier - 9wsyqb3ku7.skadnetwork - - - SKAdNetworkIdentifier - 74b6s63p6l.skadnetwork - - - SKAdNetworkIdentifier - xy9t38ct57.skadnetwork - - - SKAdNetworkIdentifier - 424m5254lk.skadnetwork - - - SKAdNetworkIdentifier - qu637u8glc.skadnetwork - - - SKAdNetworkIdentifier - f73kdq92p3.skadnetwork - - - SKAdNetworkIdentifier - 44n7hlldy6.skadnetwork - - - SKAdNetworkIdentifier - kbmxgpxpgc.skadnetwork - - - SKAdNetworkIdentifier - ecpz2srf59.skadnetwork - - - SKAdNetworkIdentifier - x5854y7y24.skadnetwork - - - SKAdNetworkIdentifier - f7s53z58qe.skadnetwork - - - SKAdNetworkIdentifier - x8uqf25wch.skadnetwork - - - SKAdNetworkIdentifier - uw77j35x4d.skadnetwork - - - SKAdNetworkIdentifier - 6964rsfnh4.skadnetwork - - - SKAdNetworkIdentifier - gvmwg8q7h5.skadnetwork - - - SKAdNetworkIdentifier - 6yxyv74ff7.skadnetwork - - - SKAdNetworkIdentifier - 84993kbrcf.skadnetwork - - - SKAdNetworkIdentifier - 54nzkqm89y.skadnetwork - - - SKAdNetworkIdentifier - pwa73g5rt2.skadnetwork - - - SKAdNetworkIdentifier - mlmmfzh3r3.skadnetwork - - - SKAdNetworkIdentifier - 9yg77x724h.skadnetwork - - - SKAdNetworkIdentifier - n66cz3y3bx.skadnetwork - - - SKAdNetworkIdentifier - 578prtvx9j.skadnetwork - - - SKAdNetworkIdentifier - bvpn9ufa9b.skadnetwork - - - SKAdNetworkIdentifier - 6qx585k4p6.skadnetwork - - - SKAdNetworkIdentifier - mtkv5xtk9e.skadnetwork - - - SKAdNetworkIdentifier - l93v5h6a4m.skadnetwork - - - SKAdNetworkIdentifier - rvh3l7un93.skadnetwork - - - SKAdNetworkIdentifier - gta9lk7p23.skadnetwork - - - SKAdNetworkIdentifier - 5tjdwbrq8w.skadnetwork - - - SKAdNetworkIdentifier - r45fhb6rf7.skadnetwork - - - SKAdNetworkIdentifier - 32z4fx6l9h.skadnetwork - - - SKAdNetworkIdentifier - e5fvkxwrpn.skadnetwork - - - SKAdNetworkIdentifier - 8c4e2ghe7u.skadnetwork - - - SKAdNetworkIdentifier - axh5283zss.skadnetwork - - - SKAdNetworkIdentifier - 3rd42ekr43.skadnetwork - - - SKAdNetworkIdentifier - 5mv394q32t.skadnetwork - - - SKAdNetworkIdentifier - 3qcr597p9d.skadnetwork - - - SKAdNetworkIdentifier - v72qych5uu.skadnetwork - - - SKAdNetworkIdentifier - ydx93a7ass.skadnetwork - - - SKAdNetworkIdentifier - 4pfyvq9l8r.skadnetwork - - - SKAdNetworkIdentifier - 5a6flpkh64.skadnetwork - - - SKAdNetworkIdentifier - 4fzdc2evr5.skadnetwork - - - SKAdNetworkIdentifier - 4w7y6s5ca2.skadnetwork - - - SKAdNetworkIdentifier - 252b5q8x7y.skadnetwork - - - SKAdNetworkIdentifier - 2fnua5tdw4.skadnetwork - - - SKAdNetworkIdentifier - 3l6bd9hu43.skadnetwork - - - SKAdNetworkIdentifier - 4mn522wn87.skadnetwork - - - SKAdNetworkIdentifier - 6g9af3uyq4.skadnetwork - - - SKAdNetworkIdentifier - 6p4ks3rnbw.skadnetwork - - - SKAdNetworkIdentifier - 6xzpu9s2p8.skadnetwork - - - SKAdNetworkIdentifier - 737z793b9f.skadnetwork - - - SKAdNetworkIdentifier - 89z7zv988g.skadnetwork - - - SKAdNetworkIdentifier - 8m87ys6875.skadnetwork - - - SKAdNetworkIdentifier - 8r8llnkz5a.skadnetwork - - - SKAdNetworkIdentifier - bxvub5ada5.skadnetwork - - - SKAdNetworkIdentifier - c3frkrj4fj.skadnetwork - - - SKAdNetworkIdentifier - cg4yq2srnc.skadnetwork - - - SKAdNetworkIdentifier - dbu4b84rxf.skadnetwork - - - SKAdNetworkIdentifier - dkc879ngq3.skadnetwork - - - SKAdNetworkIdentifier - dzg6xy7pwj.skadnetwork - - - SKAdNetworkIdentifier - gta8lk7p23.skadnetwork - - - SKAdNetworkIdentifier - hdw39hrw9y.skadnetwork - - - SKAdNetworkIdentifier - hjevpa356n.skadnetwork - - - SKAdNetworkIdentifier - krvm3zuq6h.skadnetwork - - - SKAdNetworkIdentifier - ln5gz23vtd.skadnetwork - - - SKAdNetworkIdentifier - m297p6643m.skadnetwork - - - SKAdNetworkIdentifier - p78axxw29g.skadnetwork - - - SKAdNetworkIdentifier - pu4na253f3.skadnetwork - - - SKAdNetworkIdentifier - s69wq72ugq.skadnetwork - - - SKAdNetworkIdentifier - t6d3zquu66.skadnetwork - - - SKAdNetworkIdentifier - vutu7akeur.skadnetwork - - - SKAdNetworkIdentifier - x2jnk7ly8j.skadnetwork - - - SKAdNetworkIdentifier - x5l83yy675.skadnetwork - - - SKAdNetworkIdentifier - y45688jllp.skadnetwork - - - SKAdNetworkIdentifier - yrqqpx2mcb.skadnetwork - - - SKAdNetworkIdentifier - z4gj7hsk7h.skadnetwork - - - SKAdNetworkIdentifier - 33r6p7g8nc.skadnetwork - - - SKAdNetworkIdentifier - g69uk9uh2b.skadnetwork - - - UIStatusBarHidden - - - diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h deleted file mode 100644 index 308a2a5..0000000 --- a/ios/Runner/Runner-Bridging-Header.h +++ /dev/null @@ -1 +0,0 @@ -#import "GeneratedPluginRegistrant.h" diff --git a/ios/RunnerTests/RunnerTests.swift b/ios/RunnerTests/RunnerTests.swift deleted file mode 100644 index 86a7c3b..0000000 --- a/ios/RunnerTests/RunnerTests.swift +++ /dev/null @@ -1,12 +0,0 @@ -import Flutter -import UIKit -import XCTest - -class RunnerTests: XCTestCase { - - func testExample() { - // If you add code to the Runner application, consider adding tests here. - // See https://developer.apple.com/documentation/xctest for more information about using XCTest. - } - -} diff --git a/ios/firebase_app_id_file.json b/ios/firebase_app_id_file.json deleted file mode 100644 index a81cdf7..0000000 --- a/ios/firebase_app_id_file.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "file_generated_by": "FlutterFire CLI", - "purpose": "FirebaseAppID & ProjectID for this Firebase app in this directory", - "GOOGLE_APP_ID": "1:701767851968:ios:4148f84add5cc6732f7225", - "FIREBASE_PROJECT_ID": "mnemo-cards", - "GCM_SENDER_ID": "701767851968" -} \ No newline at end of file diff --git a/lib.zip b/lib.zip deleted file mode 100644 index 0a685b8..0000000 Binary files a/lib.zip and /dev/null differ diff --git a/lib/admin/add_card.dart b/lib/admin/add_card.dart deleted file mode 100644 index e77d0e9..0000000 --- a/lib/admin/add_card.dart +++ /dev/null @@ -1,295 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:flutter/material.dart'; - -import 'package:file_picker/file_picker.dart'; -import 'package:mnemo_cards/admin/admin_api.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart'; - -import '../main.dart'; - -class EditCard { - GameCardDto gameCardDto; - AdminApi adminApi; - bool imageChanged = false; - - EditCard(GameCardDto? cardDto, this.adminApi) - : gameCardDto = cardDto ?? - const GameCardDto( - id: -1, - image: null, - mnemo: null, - original: null, - translation: null, - transcription: null, - transcriptionMnemo: null, - imageBack: null, - back: null, - ); - - void _saveCard() async { - final dto = imageChanged ? gameCardDto : gameCardDto.copyWith.image(null); - final result = await adminApi.addCard(dto); - if (!result) { - ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(SnackBar( - content: Text('ERROR'), - )); - } else { - ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(SnackBar( - content: Text('Success!!!'), - )); - appRouter.maybePop(); - } - } - - void _deleteCard() async { - final result = await adminApi.deleteCard(gameCardDto); - if (!result) { - ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(SnackBar( - content: Text('ERROR'), - )); - } else { - ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(SnackBar( - content: Text('Success!!!'), - )); - appRouter.maybePop(); - } - } - - void addCard(BuildContext context) { - final theme = Theme.of(context).textTheme; - - final TextEditingController originalController = - TextEditingController(text: gameCardDto.original); - final TextEditingController translationController = - TextEditingController(text: gameCardDto.translation); - final TextEditingController transcriptionController = - TextEditingController(text: gameCardDto.transcription); - final TextEditingController transcriptionMnemoController = - TextEditingController(text: gameCardDto.transcriptionMnemo); - final TextEditingController mnemoController = - TextEditingController(text: gameCardDto.mnemo); - final TextEditingController backController = - TextEditingController(text: gameCardDto.back); - - showDialog( - context: context, - builder: (c) { - return Center( - child: Scaffold( - body: Material( - child: ListView( - children: [ - Header( - '${gameCardDto.id}', - hasPopButton: true, - trail: Row( - children: [ - IconButton( - onPressed: _saveCard, - icon: Icon(Icons.save), - ), - if (gameCardDto.id >= 0) - GestureDetector( - onLongPress: _deleteCard, - child: Icon(Icons.delete), - ) - ], - ), - ), - StatefulBuilder(builder: (context, setState) { - return Stack( - alignment: Alignment.center, - children: [ - Padding( - padding: EdgeInsets.all(8.0), - child: Column( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Column( - children: [ - TextField( - controller: originalController, - onChanged: (v) { - gameCardDto = - gameCardDto.copyWith(original: v); - }, - decoration: InputDecoration( - hintText: 'Original (cerdo)'), - maxLines: 1, - style: theme.displayMedium?.copyWith( - fontSize: - theme.displayMedium!.fontSize!, - ), - ), - TextField( - controller: translationController, - onChanged: (v) { - gameCardDto = gameCardDto.copyWith( - translation: v); - }, - decoration: InputDecoration( - hintText: 'Translation (свинья)'), - maxLines: 1, - style: theme.headlineSmall?.copyWith( - color: theme.headlineSmall!.color! - .withOpacity(0.5), - fontSize: - theme.headlineSmall!.fontSize!, - ), - ), - Row( - children: [ - Flexible( - child: TextField( - controller: transcriptionController, - onChanged: (v) { - gameCardDto = gameCardDto - .copyWith(transcription: v); - }, - decoration: InputDecoration( - hintText: 'Transcription'), - maxLines: 1, - style: - theme.headlineSmall?.copyWith( - color: - theme.headlineSmall!.color!, - fontSize: theme - .headlineSmall!.fontSize!, - ), - ), - ), - SizedBox( - width: 4.0, - ), - Flexible( - child: TextField( - controller: - transcriptionMnemoController, - onChanged: (v) { - gameCardDto = - gameCardDto.copyWith( - transcriptionMnemo: v); - }, - decoration: InputDecoration( - hintText: - 'Transcription mnemo'), - maxLines: 1, - style: - theme.headlineSmall?.copyWith( - color: - theme.headlineSmall!.color!, - fontSize: theme - .headlineSmall!.fontSize!, - ), - ), - ), - ], - ), - ], - ), - SizedBox( - width: MediaQuery.of(context).size.width - 20, - height: - MediaQuery.of(context).size.width - 20, - child: GestureDetector( - onTap: () async { - FilePickerResult? result = - await FilePicker.platform.pickFiles(); - if (result != null && - result.files.isNotEmpty) { - imageChanged = true; - final file = result.files.first; - final encoded = base64.normalize( - base64.encode( - File(file.path!).readAsBytesSync(), - ), - ); - gameCardDto = gameCardDto.copyWith( - image: encoded, - ); - } - setState(() {}); - }, - child: gameCardDto.image == null - ? Container( - alignment: Alignment.center, - color: Colors.white, - child: Icon( - Icons.image, - color: Colors.grey, - ), - ) - : Stack( - children: [ - ImageFade( - key: ValueKey(gameCardDto.id), - image: MemoryImage( - base64.decode( - gameCardDto.image!)), - fit: BoxFit.contain, - syncDuration: Duration.zero, - ), - Positioned( - top: 8.0, - right: 8.0, - child: IconButton( - onPressed: () { - gameCardDto = - gameCardDto.copyWith( - image: null, - ); - setState(() {}); - }, - icon: Icon(Icons.delete)), - ), - ], - ), - ), - ), - TextField( - controller: mnemoController, - onChanged: (v) { - gameCardDto = - gameCardDto.copyWith(mnemo: v); - }, - decoration: InputDecoration( - hintText: 'Mnemo ([свинья] с сердечком)', - ), - maxLines: 1, - style: theme.headlineSmall?.copyWith( - fontSize: theme.headlineSmall!.fontSize!, - ), - ), - TextField( - controller: backController, - onChanged: (v) { - gameCardDto = gameCardDto.copyWith( - back: v, - ); - }, - decoration: InputDecoration( - hintText: 'Задняя сторона', - ), - maxLines: 1, - style: theme.headlineSmall?.copyWith( - fontSize: theme.headlineSmall!.fontSize!, - ), - ), - ], - ), - ), - ], - ); - }), - ], - ), - ), - ), - ); - }); - } -} diff --git a/lib/admin/add_package.dart b/lib/admin/add_package.dart deleted file mode 100644 index a3de6bd..0000000 --- a/lib/admin/add_package.dart +++ /dev/null @@ -1,298 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:flutter_colorpicker/flutter_colorpicker.dart'; -import 'package:mnemo_cards/admin/admin_api.dart'; -import 'package:mnemo_cards/managers/repository/api.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:file_picker/file_picker.dart'; -import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart'; -import '../utils/string_helper.dart'; - -import '../main.dart'; -import 'text_param.dart'; - -class AddPackage with Api { - EditCardPackDto cardPackDto; - AdminApi adminApi; - bool palleteVisible = false; - - AddPackage(EditCardPackDto? cardPackDto, this.adminApi) - : cardPackDto = cardPackDto ?? EditCardPackDto.fromJson({}); - - void _deletePack() async { - final delete = await showDialog( - context: scaffoldKey.currentContext!, - builder: (c) => Center( - child: Container( - color: Colors.white, - width: 200, - child: IconButton( - icon: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text('Удалить '), - Icon(Icons.delete), - ], - ), - onPressed: () { - Navigator.of(c).pop(true); - }, - ), - ), - )); - if (!delete) return; - final result = await adminApi.deletePack(cardPackDto); - if (!result) { - ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(SnackBar( - content: Text('ERROR'), - )); - } else { - ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(SnackBar( - content: Text('Success!!!'), - )); - appRouter.maybePop(); - } - } - - void _savePack() async { - final result = await adminApi.editPack(cardPackDto); - if (!result) { - ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(SnackBar( - content: Text('ERROR'), - )); - } else { - ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(SnackBar( - content: Text('Success!!!'), - )); - appRouter.maybePop(); - } - } - - void addPack(BuildContext context) { - showDialog( - context: context, - builder: (c) { - return Center( - child: Card( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: ListView( - children: [ - Header( - '${cardPackDto.id}', - hasPopButton: true, - trail: Row( - children: [ - IconButton( - onPressed: _savePack, - icon: Icon(Icons.save), - ), - if (cardPackDto.id != null) - GestureDetector( - onLongPress: _deletePack, - child: Icon(Icons.delete), - ) - ], - ), - ), - TextParam( - 'Title', - cardPackDto.title, - (v) { - cardPackDto = cardPackDto.copyWith.title(v); - }, - ), - TextParam( - 'Subtitle', - cardPackDto.subtitle, - (v) { - cardPackDto = cardPackDto.copyWith.subtitle(v); - }, - ), - TextParam( - 'Description', - cardPackDto.description, - (v) => cardPackDto = cardPackDto.copyWith.description(v), - ), - TextParam( - 'Size', - cardPackDto.size?.toString(), - (v) { - cardPackDto = cardPackDto.copyWith.size( - int.tryParse(v), - ); - }, - ), - TextParam( - 'Google play id', - cardPackDto.googlePlayId, - (v) { - cardPackDto = cardPackDto.copyWith.googlePlayId(v); - }, - ), - TextParam( - 'Rustore id', - cardPackDto.rustoreId, - (v) { - cardPackDto = cardPackDto.copyWith.rustoreId(v); - }, - ), - TextParam( - 'Appstore id', - cardPackDto.appStoreId, - (v) { - cardPackDto = cardPackDto.copyWith.appStoreId(v); - }, - ), - TextParam( - 'Price', - cardPackDto.price, - (v) { - cardPackDto = cardPackDto.copyWith.price(v); - }, - ), - Row( - children: [ - Expanded(child: Text('Enabled ')), - StatefulBuilder(builder: (context, setState) { - return Switch( - value: cardPackDto.enabled ?? false, - onChanged: (v) { - setState(() { - cardPackDto = cardPackDto.copyWith.enabled(v); - }); - }); - }), - ], - ), - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - Row( - children: [ - Expanded(child: Text('Поменять цвет ')), - Switch( - value: palleteVisible, - onChanged: (v) => setState(() { - palleteVisible = !palleteVisible; - }), - ), - ], - ), - if (palleteVisible) - ColorPicker( - pickerAreaHeightPercent: 0.5, - paletteType: PaletteType.hsvWithHue, - pickerColor: cardPackDto.color?.asColor ?? - Colors.transparent, - onColorChanged: (color) { - String? colorString; - if (color.alpha != 0) { - colorString = '#${color.toHexString()}'; - } - cardPackDto = - cardPackDto.copyWith.color(colorString); - }, - ), - ], - ); - }), - StatefulBuilder(builder: (context, setState) { - return SizedBox( - width: MediaQuery.of(context).size.width - 20, - height: MediaQuery.of(context).size.width - 20, - child: GestureDetector( - onTap: () async { - FilePickerResult? result = - await FilePicker.platform.pickFiles(); - if (result != null && result.files.isNotEmpty) { - final file = result.files.first; - final encoded = base64.normalize( - base64.encode( - File(file.path!).readAsBytesSync(), - ), - ); - cardPackDto = cardPackDto.copyWith.cover( - encoded, - ); - } - setState(() {}); - }, - child: cardPackDto.cover == null - ? Container( - alignment: Alignment.center, - color: Colors.white, - child: Icon( - Icons.image, - color: Colors.grey, - ), - ) - : Stack( - children: [ - ImageFade( - key: ValueKey(cardPackDto.id), - image: MemoryImage( - base64.decode(cardPackDto.cover!)), - fit: BoxFit.contain, - syncDuration: Duration.zero, - ), - Positioned( - top: 8.0, - right: 8.0, - child: IconButton( - onPressed: () { - cardPackDto = cardPackDto.copyWith( - cover: null, - ); - setState(() {}); - }, - icon: Icon(Icons.delete)), - ), - ], - ), - ), - ); - }), - TextParam( - 'Preview cards', - cardPackDto.previewCards?.toString(), - (v) { - final cards = v - .decodeList() - .map((e) => e.toString()) - .toList(); - cardPackDto = cardPackDto.copyWith.previewCards(cards); - }, - ), - TextParam( - 'Order (lower is higher)', - cardPackDto.order?.toString(), - (v) { - cardPackDto = cardPackDto.copyWith.order( - int.tryParse(v), - ); - }, - ), - TextParam( - 'Cards order (ids)', - cardPackDto.cardsOrder?.join(','), - (v) { - final ids = v - .split(',') - .map((s) => int.tryParse(s.trim())) - .whereNotNull() - .toList(); - cardPackDto = cardPackDto.copyWith(cardsOrder: ids); - }, - ), - ], - ), - ), - ), - ); - }); - } -} - diff --git a/lib/admin/add_plan.dart b/lib/admin/add_plan.dart deleted file mode 100644 index 3ab4166..0000000 --- a/lib/admin/add_plan.dart +++ /dev/null @@ -1,389 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:mnemo_cards/admin/admin_api.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart'; - -import '../di/injector.dart'; -import '../main.dart'; -import 'text_param.dart'; - -class AddPlan { - List allPlans = []; - List selectedFeatures = []; - late SubscriptionPlanAdminDto planDto; - late AdminApi adminApi; - - final availableFeatures = [ - SubscriptionFeatureEnum.ads, - SubscriptionFeatureEnum.packs, - ]; - - AddPlan() { - this.adminApi = getIt.get(); - _init(null); - } - - void _init(SubscriptionPlanAdminDto? dto) { - planDto = dto ?? - const SubscriptionPlanAdminDto( - ui: SubscriptionPlanUIDto( - currency: 'руб', - ), - price: '123', - currency: 'руб', - durationDays: 0, - features: [ - SubscriptionFeatureEnum.ads, - SubscriptionFeatureEnum.packs, - ], - paymentId: null, - paymentSystem: PaymentSystem.unknown, - ); - selectedFeatures = planDto.features.toList(); - } - - Future _loadPlans() async { - final result = await adminApi.getSubscriptionPlans(); - allPlans = result; - return true; - } - - void _saveSubscriptionPlan() async { - final result = await adminApi.addSubscriptionPlan( - planDto.copyWith( - features: selectedFeatures, - ), - ); - if (!result) { - ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(SnackBar( - content: Text('ERROR'), - )); - } else { - ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(SnackBar( - content: Text('Success!!!'), - )); - appRouter.maybePop(); - } - } - - void _deletePLan() async { - if (planDto.id == null) return; - final result = await adminApi.deleteSubscriptionPlan(planDto.id.toString()); - if (!result) { - ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(SnackBar( - content: Text('ERROR'), - )); - } else { - ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(SnackBar( - content: Text('Success!!!'), - )); - appRouter.maybePop(); - } - } - - void addPlan(BuildContext context) { - showDialog( - context: context, - builder: (c) { - return FutureBuilder( - future: _loadPlans(), - builder: (context, snapshot) { - if (snapshot.data != true) { - return Center( - child: Material( - child: Center(child: CircularProgressIndicator()), - ), - ); - } - return Center( - child: Scaffold( - body: Material( - child: StatefulBuilder(builder: (context, setPlan) { - return ListView( - children: [ - Header( - '${planDto.id}', - hasPopButton: true, - trail: Row( - children: [ - IconButton( - onPressed: () async { - await _loadPlans(); - setPlan(() {}); - }, - icon: Icon(Icons.sync), - ), - IconButton( - onPressed: _saveSubscriptionPlan, - icon: Icon(Icons.save), - ), - if (planDto.id != null) - IconButton( - onPressed: () async { - planDto = planDto.copyWith(id: null); - _saveSubscriptionPlan(); - }, - icon: Icon(Icons.copy), - ), - if (planDto.id != null) - GestureDetector( - onLongPress: _deletePLan, - child: Icon(Icons.delete), - ) - ], - ), - ), - DropdownMenu( - initialSelection: null, - onSelected: (String? value) { - setPlan(() { - _init( - allPlans - .firstWhereOrNull((v) => v.id == value), - ); - }); - }, - dropdownMenuEntries: [ - ...allPlans, - null, - ].map((plan) { - return DropdownMenuEntry( - value: plan?.id, - label: plan == null - ? 'New' - : '${plan.ui.titleLead ?? ''} ${plan.ui.title ?? ''} ${plan.paymentSystem.name}', - ); - }).toList(), - ), - StatefulBuilder(builder: (context, setState) { - return Stack( - alignment: Alignment.center, - children: [ - Padding( - padding: EdgeInsets.all(8.0), - child: Column( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: - MainAxisAlignment.spaceEvenly, - children: [ - Column( - children: [ - TextParam( - 'Currency', - planDto.currency, - (v) { - planDto = planDto.copyWith( - currency: v); - }, - ), - TextParam( - 'Price', - planDto.price, - (v) { - planDto = - planDto.copyWith(price: v); - }, - ), - TextParam( - 'Days', - planDto.durationDays.toString(), - (v) { - final days = int.parse(v); - planDto = planDto.copyWith( - durationDays: days, - ); - }, - ), - Text('Features:'), - ListView.builder( - shrinkWrap: true, - itemCount: - availableFeatures.length, - itemBuilder: (context, index) { - final item = - availableFeatures[index]; - return ListTile( - title: Text(item.name), - onTap: () { - setState(() { - if (selectedFeatures - .contains(item)) { - selectedFeatures - .remove(item); - } else { - selectedFeatures - .add(item); - } - }); - }, - selected: selectedFeatures - .contains(item), - selectedTileColor: Colors.blue - .withOpacity(0.5), - ); - }, - ), - Text('UI:'), - TextParam( - 'UI Currency', - planDto.ui.currency, - (v) { - final ui = planDto.ui.copyWith( - currency: v, - ); - planDto = - planDto.copyWith.ui(ui); - }, - ), - TextParam( - 'UI Title lead', - planDto.ui.titleLead, - (v) { - final ui = planDto.ui.copyWith( - titleLead: v, - ); - planDto = - planDto.copyWith.ui(ui); - }, - ), - TextParam( - 'UI Title', - planDto.ui.title, - (v) { - final ui = planDto.ui.copyWith( - title: v, - ); - planDto = - planDto.copyWith.ui(ui); - }, - ), - TextParam( - 'UI Price per month', - planDto.ui.pricePerMonth, - (v) { - final ui = planDto.ui.copyWith( - pricePerMonth: v, - ); - planDto = - planDto.copyWith.ui(ui); - }, - ), - TextParam( - 'UI Subtitle crossed', - planDto.ui.subtitleCrossed, - (v) { - final ui = planDto.ui.copyWith( - subtitleCrossed: v, - ); - planDto = - planDto.copyWith.ui(ui); - }, - ), - TextParam( - 'UI Subtitle', - planDto.ui.subtitle, - (v) { - final ui = planDto.ui.copyWith( - subtitle: v, - ); - planDto = - planDto.copyWith.ui(ui); - }, - ), - TextParam( - 'UI Tip', - planDto.ui.tip, - (v) { - final ui = planDto.ui.copyWith( - tip: v, - ); - planDto = - planDto.copyWith.ui(ui); - }, - ), - TextParam( - 'UI Tip color', - planDto.ui.tipColor, - (v) { - final ui = planDto.ui.copyWith( - tipColor: v, - ); - planDto = - planDto.copyWith.ui(ui); - }, - ), - Padding( - padding: - const EdgeInsets.all(8.0), - child: Row( - children: [ - Text('Payment type '), - Expanded( - child: StatefulBuilder( - builder: (context, - setState) { - return DropdownButton< - PaymentSystem>( - value: planDto - .paymentSystem, - items: - PaymentSystem.values - .map( - (v) => - DropdownMenuItem( - child: Text( - v.name), - value: v, - ), - ) - .toList(), - onChanged: (v) { - setState(() { - planDto = planDto - .copyWith( - paymentSystem: v ?? - PaymentSystem - .unknown, - ); - }); - }, - ); - }), - ), - ], - ), - ), - TextParam( - 'Payment id', - planDto.paymentId, - (v) { - planDto = planDto.copyWith( - paymentId: v, - ); - }, - ), - ], - ), - ], - ), - ), - ], - ); - }), - ], - ); - }), - ), - ), - ); - }); - }); - } - - Widget uiEditor() { - return Column( - children: [], - ); - } -} diff --git a/lib/admin/add_promocode_campaign.dart b/lib/admin/add_promocode_campaign.dart deleted file mode 100644 index ba94a9b..0000000 --- a/lib/admin/add_promocode_campaign.dart +++ /dev/null @@ -1,388 +0,0 @@ -import 'dart:async'; -import 'dart:developer'; - -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -import 'package:mnemo_cards/admin/admin_api.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart'; - -import '../di/injector.dart'; -import '../main.dart'; -import 'date_param.dart'; -import 'enum_param.dart'; -import 'int_param.dart'; -import 'text_param.dart'; - -class AddPromocodeCampaign { - late PromoCodesCampaignDto campaignDto; - late AdminApi adminApi; - List _campaigns = []; - - final availableStatuses = [ - PromoCodeCampaignStatus.created, - PromoCodeCampaignStatus.preparing, - PromoCodeCampaignStatus.ready, - PromoCodeCampaignStatus.active, - PromoCodeCampaignStatus.disabled, - ]; - - AddPromocodeCampaign() { - this.adminApi = getIt.get(); - _init(null); - } - - void _init(PromoCodesCampaignDto? dto) { - final now = DateTime.now(); - campaignDto = dto ?? - PromoCodesCampaignDto( - products: [], - activationsPerCode: 1, - generationSize: 100, - start: now, - finish: now.add(Duration(days: 30)), - status: PromoCodeCampaignStatus.disabled, - name: null, - template: r'$s$s$s$s$s$s$s$s', - ); - } - - Future _loadCampaigns() async { - try { - _campaigns = await adminApi.getPromoCodeCampaigns(); - } catch (e, s) { - log('Error', error: e, stackTrace: s); - print('Error $e'); - return false; - } - return true; - } - - Future _loadCampaign(String id) async { - try { - final campaign = await adminApi.getPromoCodeCampaign(id); - if (campaign != null) { - _campaigns.removeWhere((v) => v.id.toString() == id); - _campaigns.add(campaign); - campaignDto = campaign; - } - } catch (e, s) { - log('Error', error: e, stackTrace: s); - print('Error $e'); - return false; - } - return true; - } - - void _savePromoCodeCampaign() async { - final result = await adminApi.addPromoCodeCampaign(campaignDto); - if (!result) { - ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(SnackBar( - content: Text('ERROR'), - )); - } else { - ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(SnackBar( - content: Text('Success!!!'), - )); - appRouter.maybePop(); - } - } - - void _deletePromoCodeCampaign() async { - if (campaignDto.id == null) return; - final result = - await adminApi.deletePromoCodeCampaign(campaignDto.id.toString()); - if (!result) { - ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(SnackBar( - content: Text('ERROR'), - )); - } else { - ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(SnackBar( - content: Text('Success!!!'), - )); - appRouter.maybePop(); - } - } - - void addCampaign(BuildContext context) { - showDialog( - context: context, - builder: (c) { - return FutureBuilder( - future: _loadCampaigns(), - builder: (context, snapshot) { - if (snapshot.data != true) { - return Center( - child: Material( - child: Center(child: CircularProgressIndicator()), - ), - ); - } - return Center( - child: Scaffold( - body: Material( - child: StatefulBuilder(builder: (context, setCampaign) { - final codes = campaignDto.promoCodes; - - return ListView( - children: [ - Header( - '${campaignDto.id}', - hasPopButton: true, - trail: Row( - children: [ - if (campaignDto.id != null) - IconButton( - onPressed: () async { - await _loadCampaign( - campaignDto.id.toString(), - ); - setCampaign(() {}); - }, - icon: Icon(Icons.note), - ), - IconButton( - onPressed: _savePromoCodeCampaign, - icon: Icon(Icons.save), - ), - if (campaignDto.id != null) - IconButton( - onPressed: () async { - campaignDto = - campaignDto.copyWith(id: null); - _savePromoCodeCampaign(); - }, - icon: Icon(Icons.copy), - ), - if (campaignDto.id != null) - GestureDetector( - onLongPress: _deletePromoCodeCampaign, - child: Icon(Icons.delete), - ) - ], - ), - ), - DropdownMenu( - initialSelection: null, - onSelected: (int? value) { - setCampaign(() { - _init( - _campaigns - .firstWhereOrNull((v) => v.id == value), - ); - }); - }, - dropdownMenuEntries: [ - ..._campaigns, - null, - ].map((campaign) { - return DropdownMenuEntry( - value: campaign?.id, - label: campaign == null - ? 'New' - : '${campaign.name ??campaign.id} ${campaign.status.name}', - ); - }).toList(), - ), - StatefulBuilder(builder: (context, setState) { - return Padding( - padding: EdgeInsets.all(8.0), - child: Column( - children: [ - TextParam( - 'Name', - campaignDto.name, - (v) { - campaignDto = campaignDto.copyWith( - name: v, - ); - }, - ), - TextParam( - 'Template', - campaignDto.template, - (v) { - campaignDto = campaignDto.copyWith( - template: v, - ); - }, - description: - r'$d - digit $l - letter $s - any', - ), - IntParam( - 'Generation size', - campaignDto.generationSize, - (v) { - campaignDto = campaignDto.copyWith( - generationSize: v); - }, - ), - IntParam( - 'Activations per code', - campaignDto.activationsPerCode, - (v) { - campaignDto = campaignDto.copyWith( - activationsPerCode: v, - ); - }, - ), - DateParam( - 'Start', - campaignDto.start, - (v) { - setState(() { - campaignDto = - campaignDto.copyWith(start: v); - }); - }, - ), - DateParam( - 'Finish', - campaignDto.finish, - (v) { - setState(() { - campaignDto = - campaignDto.copyWith(finish: v); - }); - }, - ), - EnumParam( - 'Status', - PromoCodeCampaignStatus.values, - campaignDto.status, (v) { - setState(() { - campaignDto = - campaignDto.copyWith(status: v); - }); - }), - Text( - 'Created to generate promocodes\n' - 'Disabled to disable\n' - 'Dont use other statuses!', - style: TextStyle(fontSize: 12), - ), - Text('Products:'), - ListView.builder( - shrinkWrap: true, - itemCount: - campaignDto.products.length + 1, - itemBuilder: (context, index) { - if (index == - campaignDto.products.length) { - return ListTile( - title: Text('Add'), - onTap: () async { - final p = - await _showProductDialog( - context, null); - if (p != null) { - setState(() { - campaignDto.products.add(p); - }); - } - }, - ); - } - final item = - campaignDto.products[index]; - return ListTile( - title: Text( - '${item.type.name} ${item.id ?? ''}'), - onTap: () async { - final p = await _showProductDialog( - context, - item, - ); - if (p != null) { - setState(() { - campaignDto.products.add(p); - }); - } - }, - trailing: IconButton( - icon: Icon(Icons.delete), - onPressed: () { - setState(() { - campaignDto.products - .remove(item); - }); - }, - ), - ); - }, - ), - SimpleTile.text( - text: codes == null - ? 'Codes not loaded' - : 'Codes: ${codes.length == 1 ? codes.first : codes.length}', - onTap: () async { - if (codes != null) { - await Clipboard.setData( - ClipboardData( - text: codes.join('\n'), - ), - ); - } - }, - ), - ], - ), - ); - }), - ], - ); - }), - ), - ), - ); - }); - }); - } - - Future _showProductDialog( - BuildContext context, - MnemoCardsProduct? initialProduct, - ) async { - var prod = initialProduct ?? MnemoCardsProduct(); - final result = await showDialog( - context: context, - builder: (c) => Center( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Material( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - TextParam('Id', prod.id, (v) { - prod = prod.copyWith(id: v); - }), - EnumParam( - 'Type', - MnemoCardsProductType.values, - prod.type, - (v) { - prod = prod.copyWith(type: v); - }, - ), - MaterialButton( - onPressed: () { - Navigator.of(context).pop(prod); - }, - child: Text('Save'), - ), - ], - ), - ), - ), - ), - ); - return result; - } - - Widget uiEditor() { - return Column( - children: [], - ); - } -} diff --git a/lib/admin/add_user.dart b/lib/admin/add_user.dart deleted file mode 100644 index 930d9a3..0000000 --- a/lib/admin/add_user.dart +++ /dev/null @@ -1,228 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; - -import 'package:flutter/services.dart'; -import 'package:mnemo_cards/admin/admin_api.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart'; - -import '../main.dart'; - -class EditUser { - UserDto userDto; - AdminApi adminApi; - bool imageChanged = false; - - EditUser(UserDto userDto, this.adminApi) : userDto = userDto; - - void _saveUser() async { - final dto = userDto; - final result = await adminApi.editUser(dto); - if (!result) { - ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(SnackBar( - content: Text('ERROR'), - )); - } else { - ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(SnackBar( - content: Text('Success!!!'), - )); - appRouter.maybePop(); - } - } - - void _deleteCard() async { - final result = await adminApi.deleteUser(userDto); - if (!result) { - ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(SnackBar( - content: Text('ERROR'), - )); - } else { - ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(SnackBar( - content: Text('Success!!!'), - )); - appRouter.maybePop(); - } - } - - void addUser(BuildContext context) { - final theme = Theme.of(context).textTheme; - - final TextEditingController nameController = - TextEditingController(text: userDto.name); - - final TextEditingController packsController = - TextEditingController(text: userDto.packs.join(',')); - - showDialog( - context: context, - builder: (c) { - return Center( - child: Scaffold( - body: Material( - child: ListView( - children: [ - Header( - '${userDto.id}', - hasPopButton: true, - trail: Row( - children: [ - IconButton( - onPressed: _saveUser, - icon: Icon(Icons.save), - ), - if (userDto.id != null && userDto.id! >= 0) - GestureDetector( - onLongPress: _deleteCard, - child: Icon(Icons.delete), - ) - ], - ), - ), - StatefulBuilder(builder: (context, setState) { - return Padding( - padding: EdgeInsets.all(8.0), - child: Column( - children: [ - GestureDetector( - onTap: () { - if (userDto.id != null) { - Clipboard.setData(ClipboardData( - text: userDto.id.toString())); - } - }, - child: Text('ID: ${userDto.id}'), - ), - Text('Admin: ${userDto.admin}'), - TextField( - controller: nameController, - onChanged: (v) { - userDto = userDto.copyWith(name: v); - }, - decoration: InputDecoration(hintText: 'Name'), - maxLines: 1, - style: theme.displayMedium?.copyWith( - fontSize: theme.displayMedium!.fontSize!, - ), - ), - Row( - children: [ - Expanded(child: Text('Subscription ')), - StatefulBuilder(builder: (context, setState) { - return Switch( - value: userDto.subscription ?? false, - onChanged: (v) { - setState(() { - userDto = - userDto.copyWith.subscription(v); - }); - }); - }), - ], - ), - TextField( - controller: packsController, - onChanged: (v) { - final packs = v - .split(',') - .map((s) => - int.tryParse(s.trim())?.toString()) - .whereNotNull() - .toList(); - userDto = userDto.copyWith(packs: packs); - final packsText = packs.join(','); - if (!packsController.text - .startsWith(packsText)) { - packsController.text = packsText; - } - }, - decoration: InputDecoration(hintText: 'Packs'), - maxLines: 1, - style: theme.displayMedium?.copyWith( - fontSize: theme.displayMedium!.fontSize!, - ), - ), - ], - ), - ); - }), - _Payments(adminApi, userDto), - Text( - 'Stats: ${userDto.userDataDto?.allWordsStatistics?.total} words', - ), - Text( - 'Stats: ${userDto.userDataDto?.allTestsStatistics?.tests.length} tests', - ), - Text( - 'Last test session: ${userDto.userDataDto?.allTestsStatistics?.lastSessionToken}', - ), - ], - ), - ), - ), - ); - }); - } -} - -class _Payments extends StatelessWidget { - final AdminApi adminApi; - final UserDto userDto; - - _Payments(this.adminApi, this.userDto); - - @override - Widget build(BuildContext context) { - final sc = StreamController?>(); - final stream = sc.stream; - - return ExpansionTile( - title: Text('Транзакции'), - maintainState: true, - onExpansionChanged: (v) async { - if (v) { - sc.add(null); - final payments = - await adminApi.getUserPurchases(userDto.id.toString()); - sc.add(payments); - } - }, - children: [ - StreamBuilder?>( - stream: stream, - builder: (context, snapshot) { - final payments = snapshot.data; - if (payments == null) { - return Text('Loading...'); - } - if (payments.isEmpty) { - return Text('Нет покупок'); - } - return Column( - children: payments.map((p) { - return ListTile( - title: Text('${p.amount} ${p.currency}'), - subtitle: Text(dateString(p.date)), - trailing: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text(p.status.name), - Text(p.paymentSystem.name), - Text( - p.products.map((p) => '${p.type}: ${p.id}').join('\n'), - ), - ], - ), - ); - }).toList(), - ); - }, - ), - ], - ); - } - - String dateString(DateTime date) => - '${date.year}.${date.month.toString().padLeft(2, '0')}.${date.day.toString().padLeft(2, '0')} ${date.hour.toString().padLeft(2, '0')}:${date.minute.toString().padLeft(2, '0')}:${date.second.toString().padLeft(2, '0')}'; -} diff --git a/lib/admin/admin_api.dart b/lib/admin/admin_api.dart deleted file mode 100644 index 48f9494..0000000 --- a/lib/admin/admin_api.dart +++ /dev/null @@ -1,168 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; - -import 'package:dio/dio.dart'; -import 'package:mnemo_cards/di/injector.dart'; -import 'package:mnemo_cards/features/packs/packs_api.dart'; -import 'package:mnemo_cards/features/worker/worker_ext.dart'; -import 'package:mnemo_cards/managers/repository/api.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; - -class AdminApi with Api { - final Dio _dio; - - AdminApi(this._dio); - - Future addCard(GameCardDto dto) async { - final r = await _dio.post( - '$path/cards/add', - data: jsonEncode(dto.toJson()), - ); - return r.statusCode == 200; - } - - Future deleteCard(GameCardDto dto) async { - final r = await _dio.post( - '$path/cards/delete', - data: jsonEncode({'id': dto.id.toString()}), - ); - return r.statusCode == 200; - } - - Future> getCardIds() async { - final r = await _dio.get( - '$path/cards/ids', - ); - return (jsonDecode(r.data!)['ids'] as String).split(',').cast(); - } - - Future> getCards(List ids) async { - final r = await _dio.get( - '$path/cards', - queryParameters: {'ids': ids.join(',')}, - ); - return (r.data as String).decodeList(GameCardDto.fromJson); - } - - Future> getUsers(List ids) async { - final r = await _dio.get( - '$path/users', - queryParameters: {'ids': ids.join(',')}, - ); - return (jsonDecode(r.data!) as List) - .map((e) => UserDto.fromJson(e as Map)) - .toList() - .cast(); - } - - Future> getUserIds() async { - final r = await _dio.get( - '$path/users/ids', - ); - return (jsonDecode(r.data!)['ids'] as String).split(',').cast(); - } - - Future> getUserPurchases(String userId) async { - final r = await _dio - .get('$path/user/purchases', queryParameters: {'id': userId}); - return (jsonDecode(r.data!)['payments'] as List) - .cast>() - .map(PaymentDto.fromJson) - .toList(); - } - - Future deleteUser(UserDto dto) async { - final r = await _dio.post( - '$path/users/delete', - data: jsonEncode({'id': dto.id.toString()}), - ); - return r.statusCode == 200; - } - - Future editUser(UserDto dto) async { - final r = await _dio.post( - '$path/users/add', - data: jsonEncode(dto.toJson()), - ); - return r.statusCode == 200; - } - - Future getEditPack(String id) async { - final r = await _dio.get( - '$path/pack/edit/$id', - ); - return (r.data as String).decode(EditCardPackDto.fromJson); - } - - Future deletePack(EditCardPackDto dto) async { - final r = await _dio.post( - '$path/pack/delete', - data: jsonEncode({'id': dto.id.toString()}), - ); - return r.statusCode == 200; - } - - Future editPack(EditCardPackDto dto) async { - final r = await _dio.post( - '$path/pack/edit', - data: jsonEncode(dto.toJson()), - ); - return r.statusCode == 200; - } - - Future> packs() => - getIt.get().getPacksPreviews(null); - - Future> getSubscriptionPlans() async { - final r = await _dio.get( - '$path/subscriptions', - ); - return (jsonDecode(r.data!)['plans'] as List) - .cast>() - .map(SubscriptionPlanAdminDto.fromJson) - .toList(); - } - - Future addSubscriptionPlan(SubscriptionPlanAdminDto dto) async { - final r = await _dio.post( - '$path/subscription/add', - data: jsonEncode(dto.toJson()), - ); - return r.statusCode == 200; - } - - Future deleteSubscriptionPlan(String id) async { - final r = await _dio.post( - '$path/subscription/delete/$id', - ); - return r.statusCode == 200; - } - - Future deletePromoCodeCampaign(String id) async { - final r = await _dio.post( - '$path/promocode/delete/$id', - ); - return r.statusCode == 200; - } - - Future addPromoCodeCampaign(PromoCodesCampaignDto dto) async { - final r = await _dio.post( - '$path/promocode/add', - data: dto.encode(), - ); - return r.statusCode == 200; - } - - Future> getPromoCodeCampaigns() async { - final r = await _dio.get('$path/promocode/list'); - return (jsonDecode(r.data!)['campaigns'] as List) - .cast>() - .map(PromoCodesCampaignDto.fromJson) - .toList(); - } - - Future getPromoCodeCampaign(String id) async { - final r = await _dio.get('$path/promocode/$id'); - return r.data?.isolatedDecode(PromoCodesCampaignDto.fromJson); - } -} diff --git a/lib/admin/all_cards.dart b/lib/admin/all_cards.dart deleted file mode 100644 index ceabd80..0000000 --- a/lib/admin/all_cards.dart +++ /dev/null @@ -1,367 +0,0 @@ -import 'dart:convert'; -import 'dart:math'; - -import 'package:dio/dio.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:mnemo_cards/admin/add_card.dart'; -import 'package:mnemo_cards/admin/admin_api.dart'; -import 'package:mnemo_cards/di/injector.dart'; -import 'package:mnemo_cards/managers/repository/dio_provider.dart'; -import 'package:mnemo_cards/theme/themes.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart'; - -import '../main.dart'; -import 'add_package.dart'; - -class AllCards extends StatefulWidget { - @override - State createState() => _AllCardsState(); - - const AllCards(); -} - -class _AllCardsState extends State { - static List ids = []; - static Set cards = {}; - static String filter = ''; - static List _packPreviews = []; - static EditCardPackDto? _currentPack; - static String? _currentId; - static List _selectedCards = []; - static Map _cache = {}; - - bool get selectingMode => _selectedCards.isNotEmpty; - - Dio get dio => getIt.get().dio; - - AdminApi get api => getIt.get(); - - Future loadCards() async { - var loadingIds = ids.toList(); - cards.clear(); - _cache.clear(); - while (loadingIds.isNotEmpty) { - try { - final ids = loadingIds.take(10).toList(); - loadingIds.removeRange(0, ids.length); - final cardsPage = await api.getCards(ids); - for (final card in cardsPage) { - final imageString = - card.image?.substring(0, min(100, card.image!.length)) ?? ''; - _cache[card.id.toString()] = - '${card.id}${card.mnemo}${card.transcription}${card.transcriptionMnemo}${card.original}${card.translation}${imageString}'; - } - cards.addAll(cardsPage); - setState(() {}); - } catch (e) { - print(e); - } - } - } - - Future loadIds() async { - ids = await api.getCardIds(); - setState(() {}); - } - - Future loadPacks() async { - _packPreviews = await api.packs(); - clearCurrentPack(); - } - - Future loadCurrentPack() async { - if (_currentId != null) { - _currentPack = await api.getEditPack(_currentId!); - setState(() {}); - } - } - - void clearCurrentPack() { - _currentId = null; - _currentPack = null; - setState(() {}); - } - - Future editPack(EditCardPackDto pack) async { - final result = await api.editPack(pack); - if (!result) { - ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(SnackBar( - content: Text('ERROR'), - )); - } else { - ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(SnackBar( - content: Text('Success!!!'), - )); - loadCurrentPack(); - _selectedCards.clear(); - } - } - - void addOrEditPack() => AddPackage(_currentPack, api).addPack(context); - - List buildCards() { - final filteredCards = cards.where((dto) { - final filterOk = - filter.isEmpty || _cache[dto.id.toString()]?.contains(filter) == true; - final packFilter = - _currentPack?.addCardIds?.contains(dto.id.toString()) ?? true; - return filterOk && packFilter; - }); - return filteredCards.toList(); - } - - @override - Widget build(BuildContext context) { - final filteredCards = buildCards(); - return Scaffold( - body: SafeArea( - child: Stack( - children: [ - ListView.builder( - itemExtentBuilder: (index, dims) => index >= 1 ? 160.h : 350, - itemBuilder: (context, index) { - if (index == 0) - return Column( - children: [ - Header( - _currentId == null - ? 'Все карточки' - : _currentPack?.title ?? - 'Пак ${_currentPack?.id}', - subtitle: filteredCards.isEmpty - ? 'Нет карточек' - : '${filteredCards.length} карточек', - hasPopButton: true, - trail: IconButton( - onPressed: () async { - setState(() {}); - await loadIds(); - await loadPacks(); - await loadCards(); - }, - icon: Icon(Icons.sync)), - ), - dropdownButton(context), - Padding( - padding: const EdgeInsets.all(8.0), - child: TextField( - decoration: InputDecoration(hintText: 'Поиск'), - onChanged: (v) { - if (filter != v.toLowerCase()) { - filter = v.toLowerCase(); - setState(() {}); - } - }, - ), - ), - Expanded( - child: GestureDetector( - onTap: () => EditCard(null, api).addCard(context), - child: Container( - color: Colors.green.withOpacity(0.1), - alignment: Alignment.center, - child: Icon(Icons.add, size: 30), - ), - ), - ), - ], - ); - if (filteredCards.length >= (index - 1) * 3) { - final cardIndex = (index - 1) * 3; - return Row( - children: [ - if (filteredCards.length > cardIndex) - cardWidget(filteredCards[cardIndex]), - if (filteredCards.length > cardIndex + 1) - cardWidget(filteredCards[cardIndex + 1]), - if (filteredCards.length > cardIndex + 2) - cardWidget(filteredCards[cardIndex + 2]), - ], - ); - } - return null; - }), - if (selectingMode) - Align( - alignment: Alignment.bottomCenter, - child: Container( - height: 100, - padding: EdgeInsets.all(10.0), - alignment: Alignment.center, - child: Row( - children: [ - GestureDetector( - onTap: () { - _selectedCards.clear(); - setState(() {}); - }, - child: Container( - color: Colors.blue, - padding: EdgeInsets.all(8.0), - child: Row( - children: [ - Text('${_selectedCards.length} '), - Icon(Icons.clear) - ], - ), - ), - ), - if (_currentPack != null) - if (_selectedCards.every((c) => - _currentPack!.addCardIds?.contains(c) ?? false)) - Row( - children: [ - SizedBox(width: 8.0), - GestureDetector( - onTap: () { - final updated = - _currentPack!.copyWith.removeCardIds( - _selectedCards.toList(), - ); - editPack(updated); - }, - child: Container( - padding: EdgeInsets.all(8.0), - color: Colors.blue, - child: Text('Убрать из пака'), - ), - ), - SizedBox(width: 8.0), - GestureDetector( - onTap: () { - final updated = - _currentPack!.copyWith.previewCards( - _selectedCards.toList(), - ); - editPack(updated); - }, - child: Container( - padding: EdgeInsets.all(8.0), - color: Colors.blue, - child: Text('Как превью'), - ), - ), - ], - ) - else - MaterialButton( - onPressed: () { - final updated = _currentPack!.copyWith.addCardIds( - _selectedCards.toList(), - ); - editPack(updated); - }, - child: Container( - padding: EdgeInsets.all(8.0), - color: Colors.blue, - child: Text('Добавить в пак'), - ), - ) - ], - ), - ), - ), - ], - ), - ), - ); - } - - Widget cardWidget(GameCardDto card) { - void toggleSelect() { - final id = card.id.toString(); - if (_selectedCards.contains(id)) { - _selectedCards.remove(id); - } else { - _selectedCards.add(card.id.toString()); - } - setState(() {}); - } - - return GestureDetector( - onTap: () { - if (selectingMode) { - toggleSelect(); - } else { - EditCard(card, api).addCard(context); - } - }, - onLongPress: toggleSelect, - child: _Card(card, api, _selectedCards.contains(card.id.toString())), - ); - } - - void _onPackChanged(String? id) { - if (id == null) { - clearCurrentPack(); - } else { - _currentId = id; - loadCurrentPack(); - } - } - - Widget dropdownButton(BuildContext context) { - return Row( - children: [ - Expanded( - child: DropdownButton( - items: [..._packPreviews, null] - .map((p) => DropdownMenuItem( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: p == null - ? Text('Все паки') - : Text('${p.title} ${p.cards}'), - ), - value: p?.id, - )) - .toList(), - value: _currentId, - onChanged: (v) => _onPackChanged(v), - ), - ), - IconButton( - onPressed: addOrEditPack, - icon: _currentPack == null ? Icon(Icons.add) : Icon(Icons.edit), - ), - ], - ); - } -} - -class _Card extends StatelessWidget { - final GameCardDto cardDto; - final AdminApi api; - final bool selected; - - _Card(this.cardDto, this.api, this.selected); - - @override - Widget build(BuildContext context) { - final image = cardDto.image != null ? base64.decode(cardDto.image!) : null; - return Container( - padding: EdgeInsets.all(8.0), - width: MediaQuery.of(context).size.width / 3, - height: 160.h, - decoration: BoxDecoration( - border: Border.all(color: selected ? Colors.blue : borderGray), - color: selected ? Colors.blue.withOpacity(0.2) : null, - ), - child: Column(children: [ - Text(cardDto.translation ?? ''), - Text(cardDto.original ?? ''), - if (image != null) - Expanded( - child: Image.memory( - key: ObjectKey(cardDto.image), - gaplessPlayback: true, - image, - fit: BoxFit.scaleDown, - ), - ), - ]), - ); - } -} diff --git a/lib/admin/all_users.dart b/lib/admin/all_users.dart deleted file mode 100644 index bd58c30..0000000 --- a/lib/admin/all_users.dart +++ /dev/null @@ -1,230 +0,0 @@ - -import 'package:dio/dio.dart'; -import 'package:flutter/material.dart'; -import 'package:mnemo_cards/admin/add_user.dart'; -import 'package:mnemo_cards/admin/admin_api.dart'; -import 'package:mnemo_cards/di/injector.dart'; -import 'package:mnemo_cards/di/locator.dart'; -import 'package:mnemo_cards/managers/repository/dio_provider.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart'; - -import '../main.dart'; - -class AllUsers extends StatefulWidget { - @override - State createState() => _AllUsersState(); - - const AllUsers(); -} - -class _AllUsersState extends State { - static List ids = []; - static Set users = {}; - static String filter = ''; - static UserDto? _currentUser; - static String? _currentId; - - bool get selectingMode => false; //_selectedCards.isNotEmpty; - - Dio get dio => getIt.get().dio; - - AdminApi get api => getIt.get(); - - Future loadUsers() async { - var loadingIds = ids.toList(); - users.clear(); - while (loadingIds.isNotEmpty) { - try { - final ids = loadingIds.take(10).toList(); - loadingIds.removeRange(0, ids.length); - final usersPage = await api.getUsers(ids); - users.addAll(usersPage); - setState(() {}); - } catch (e) { - print(e); - } - } - } - - Future loadIds() async { - ids = await api.getUserIds(); - setState(() {}); - } - - Future loadCurrentUser() async { - if (_currentId != null) { - _currentUser = (await api.getUsers([_currentId!])).firstOrNull; - setState(() {}); - } - } - - void clearCurrentUser() { - _currentId = null; - _currentUser = null; - setState(() {}); - } - - Future editUser(UserDto user) async { - final result = await api.editUser(user); - if (!result) { - ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(SnackBar( - content: Text('ERROR'), - )); - } else { - ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(SnackBar( - content: Text('Success!!!'), - )); - } - } - - List buildUsersList() { - final filteredUsers = users.where((dto) { - final filterOk = filter.isEmpty || - dto.name.toString().toLowerCase().contains(filter) || - dto.id.toString().toLowerCase().contains(filter) || - dto.packs.any((p) => p.toLowerCase().contains(filter)); - return filterOk; - }); - return filteredUsers.toList(); - } - - @override - Widget build(BuildContext context) { - final filteredUsers = buildUsersList(); - return Scaffold( - body: SafeArea( - child: Stack( - children: [ - ListView.builder(itemBuilder: (context, index) { - if (index == 0) - return Container( - height: 270, - child: Column( - children: [ - Header( - _currentId == null - ? 'Все' - : _currentUser?.name ?? 'Пак ${_currentUser?.id}', - subtitle: filteredUsers.isEmpty - ? 'Нет пользователей' - : '${filteredUsers.length} пользователя', - hasPopButton: true, - trail: IconButton( - onPressed: () async { - setState(() {}); - await loadIds(); - await loadUsers(); - }, - icon: Icon(Icons.sync)), - ), - dropdownButton(context), - Padding( - padding: const EdgeInsets.all(8.0), - child: TextField( - decoration: InputDecoration(hintText: 'Поиск'), - onChanged: (v) { - if (filter != v.toLowerCase()) { - filter = v.toLowerCase(); - setState(() {}); - } - }, - ), - ), - // Expanded( - // child: GestureDetector( - // onTap: () => EditUser(null, api).addCard(context), - // child: Container( - // color: Colors.green.withOpacity(0.1), - // alignment: Alignment.center, - // child: Icon(Icons.add, size: 30), - // ), - // ), - // ), - ], - ), - ); - else if (filteredUsers.length > index - 1) - return userWidget(filteredUsers[index - 1]); - else - return null; - }), - if (selectingMode) - Align( - alignment: Alignment.bottomCenter, - child: Container( - height: 100, - padding: EdgeInsets.all(10.0), - alignment: Alignment.center, - child: Row( - children: [ - GestureDetector( - onTap: () { - setState(() {}); - }, - child: Container( - color: Colors.blue, - padding: EdgeInsets.all(8.0), - child: Row( - children: [Icon(Icons.clear)], - ), - ), - ), - ], - ), - ), - ), - ], - ), - ), - ); - } - - Widget userWidget(UserDto user) { - void toggleSelect() { - final id = user.id.toString(); - setState(() {}); - } - - return GestureDetector( - onTap: () { - if (selectingMode) { - toggleSelect(); - } else { - EditUser(user, api).addUser(context); - } - }, - onLongPress: toggleSelect, - child: _User(user, api, false), - ); - } - - Widget dropdownButton(BuildContext context) { - return Row( - children: [ - IconButton( - onPressed: null, - icon: _currentUser == null ? Icon(Icons.add) : Icon(Icons.edit), - ), - ], - ); - } -} - -class _User extends StatelessWidget { - final UserDto userDto; - final AdminApi api; - final bool selected; - - _User(this.userDto, this.api, this.selected); - - @override - Widget build(BuildContext context) { - final isCurrent = locator.userManager.userStateHolder.user?.id == userDto.id; - return ListTile( - title: Text('${userDto.id}${isCurrent ? ' (you)' : ''}'), - subtitle: Text(userDto.name ?? ''), - trailing: userDto.admin ? Text('admin') : null, - ); - } -} diff --git a/lib/admin/date_param.dart b/lib/admin/date_param.dart deleted file mode 100644 index eb3f013..0000000 --- a/lib/admin/date_param.dart +++ /dev/null @@ -1,77 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; - -class DateParam extends StatelessWidget { - final String title; - final DateTime? initial; - final Function(DateTime? v) onChanged; - - DateParam(this.title, this.initial, this.onChanged); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(4.0), - child: Row( - children: [ - Text(title), - SizedBox( - width: 8, - ), - Expanded( - child: Container( - padding: EdgeInsets.all(8.0), - decoration: BoxDecoration( - border: Border.all(), - borderRadius: BorderRadius.circular(8.0), - ), - child: GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () async { - final date = await showDatePicker( - context: context, - initialEntryMode: DatePickerEntryMode.calendar, - initialDate: initial ?? DateTime.now(), - firstDate: DateTime.utc(2000), - lastDate: DateTime.utc(2050), - ).then((selectedDate) { - // After selecting the date, display the time picker. - if (selectedDate != null) { - return showTimePicker( - context: context, - initialTime: TimeOfDay.now(), - ).then((selectedTime) { - // Handle the selected date and time here. - if (selectedTime != null) { - DateTime selectedDateTime = DateTime( - selectedDate.year, - selectedDate.month, - selectedDate.day, - selectedTime.hour, - selectedTime.minute, - ); - return selectedDateTime; - } - return null; - }); - } - return null; - }); - onChanged(date); - }, - child: Text( - initial?.let( - (d) => - '${d.year}.${d.month}.${d.day} ${d.hour}:${d.minute}', - ) ?? - 'No date', - ), - ), - ), - ), - ], - ), - ); - } -} diff --git a/lib/admin/enum_param.dart b/lib/admin/enum_param.dart deleted file mode 100644 index 44b903d..0000000 --- a/lib/admin/enum_param.dart +++ /dev/null @@ -1,64 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; - -class EnumParam extends StatelessWidget { - final String title; - final String? description; - final T initial; - final List values; - final Function(T v) onChanged; - - EnumParam( - this.title, - this.values, - this.initial, - this.onChanged, { - this.description, - }); - - @override - Widget build(BuildContext context) { - T value = initial; - return Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - children: [ - Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(title), - if (description != null) - Text( - description!, - style: TextStyle(fontSize: 12), - ) - ], - ), - SizedBox( - width: 2, - ), - Expanded( - child: StatefulBuilder(builder: (context, setState) { - return DropdownButton( - value: value, - items: values - .map( - (v) => DropdownMenuItem( - child: Text(v.name), - value: v, - ), - ) - .toList(), - onChanged: (v) => setState(() { - value = v!; - onChanged(v); - }), - ); - }), - ) - ], - ), - ); - } -} diff --git a/lib/admin/int_param.dart b/lib/admin/int_param.dart deleted file mode 100644 index 9451701..0000000 --- a/lib/admin/int_param.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; - -class IntParam extends StatelessWidget { - final String title; - final int? initial; - final Function(int? v) onChanged; - - IntParam(this.title, this.initial, this.onChanged); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - children: [ - Text(title), - SizedBox( - width: 2, - ), - Expanded( - child: TextField( - controller: - TextEditingController(text: initial?.toString() ?? ''), - onChanged: (v) => onChanged(int.tryParse(v)), - ), - ), - ], - ), - ); - } -} diff --git a/lib/admin/text_param.dart b/lib/admin/text_param.dart deleted file mode 100644 index b6e8ffe..0000000 --- a/lib/admin/text_param.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; - -class TextParam extends StatelessWidget { - final String title; - final String? description; - final String? initial; - final Function(String v) onChanged; - - TextParam(this.title, this.initial, this.onChanged, {this.description}); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - children: [ - Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(title), - if (description != null) - Text( - description!, - style: TextStyle(fontSize: 12), - ) - ], - ), - SizedBox( - width: 2, - ), - Expanded( - child: TextField( - controller: TextEditingController(text: initial), - onChanged: onChanged, - ), - ), - ], - ), - ); - } -} diff --git a/lib/di/injector.dart b/lib/di/injector.dart deleted file mode 100644 index 7768e70..0000000 --- a/lib/di/injector.dart +++ /dev/null @@ -1,161 +0,0 @@ -import 'package:app_links/app_links.dart'; -import 'package:get_it/get_it.dart'; -import 'package:mnemo_cards/features/packs/pack_cache_manager.dart'; -import 'package:mnemo_cards/features/packs/packs_api.dart'; -import 'package:mnemo_cards/features/purchase/google_purchase_handler.dart'; -import 'package:mnemo_cards/features/purchase/yookassa_purchase_handler.dart'; -import 'package:mnemo_cards/features/subscription/subscription_api.dart'; -import 'package:mnemo_cards/managers/favorite_cards.dart'; -import 'package:mnemo_cards/managers/repository/dio_provider.dart'; -import 'package:mnemo_cards/managers/repository/http_repository.dart'; -import 'package:mnemo_cards/managers/user_state_holder.dart'; - -import '../features/deeplinks/deeplinks_manager_impl.dart'; -import '../features/packs/images_holder.dart'; -import '../features/packs/loading_packs_holder.dart'; -import '../features/packs/pack_updater.dart'; -import '../features/packs/preview_packs_poller.dart'; -import '../features/purchase/in_app_purchase.dart'; -import '../features/purchase/purchase_deeplink_handler.dart'; -import '../features/purchase/purchase_manager.dart'; -import '../features/subscription/subscription_deeplink_handler.dart'; -import '../features/subscription/subscription_manager.dart'; -import '../features/tests/test_manager.dart'; -import '../features/packs/pack_holder.dart'; -import '../features/packs/pack_manager.dart'; -import '../features/packs/preview_pack_holder.dart'; -import '../managers/user_manager.dart'; - -GetIt getIt = GetIt.I; - -Future setInjections() async { - final _dio = await DioProvider().init(); - getIt.registerSingleton(_dio); - print('set dio'); - - getIt.registerLazySingleton( - () => HttpRepository(getIt.get().dio), - ); - print('set repo'); - - getIt.registerLazySingleton( - () => PacksApi(getIt.get().dio), - ); - print('set pack'); - - getIt.registerLazySingleton( - () => SubscriptionApi(getIt.get().dio), - ); - - getIt.registerLazySingleton( - () => SubscriptionManager( - getIt.get(), - getIt.get(), - ), - ); - print('set subscription'); - - getIt.registerLazySingleton( - () => PackHolder(), - ); - print('set pack holder'); - - getIt.registerLazySingleton( - () => LoadingPacksHolder(), - ); - print('set loading pack holder'); - - getIt.registerLazySingleton( - () => PreviewPackHolder(), - ); - print('set preview'); - - getIt.registerLazySingleton( - () => PackCacheManager(), - ); - print('set cache'); - - getIt.registerLazySingleton( - () => ImagesHolder(), - ); - print('set images holder'); - - getIt.registerLazySingleton( - () => PreviewPacksPoller( - getIt.get(), - getIt.get(), - ), - ); - print('set preview poller'); - - getIt.registerLazySingleton( - () => PackManager( - getIt.get(), - getIt.get(), - getIt.get(), - getIt.get(), - getIt.get(), - getIt.get(), - ), - ); - print('set pack manager'); - - getIt.registerLazySingleton( - () => PackUpdater( - getIt.get(), - getIt.get(), - getIt.get(), - getIt.get(), - getIt.get(), - getIt.get(), - ), - ); - print('set pack updater'); - - getIt.registerLazySingleton( - () => UserManager( - getIt.get(), - ), - ); - print('set user manager'); - - getIt.registerLazySingleton( - () => FavoriteCardsController(), - ); - print('set fav cards container'); - - getIt.registerLazySingleton( - () => - TestManager(getIt.get(), getIt.get()), - ); - print('set test manager'); - - getIt.registerLazySingleton( - () => InAppPurchaseService(), - ); - - getIt.registerLazySingleton( - () => PurchaseDetailsStreamSubscription(), - ); - - getIt.registerLazySingleton( - () => PurchaseManager( - GooglePurchaseHandler(getIt.get()), - YookassaPurchaseHandler(getIt.get()), - ), - ); - - getIt.registerLazySingleton( - () => DeeplinkManager(AppLinks(), [ - PurchaseDeeplinkHandler(getIt.get()), - SubscriptionDeeplinkHandler(), - ]), - ); - - try { - await getIt.allReady(); - print('injector all ready'); - } catch (e, s) { - print('injector error $e $s'); - } -} diff --git a/lib/di/locator.dart b/lib/di/locator.dart deleted file mode 100644 index 86c787e..0000000 --- a/lib/di/locator.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:get_it/get_it.dart'; -import 'package:mnemo_cards/features/packs/preview_pack_holder.dart'; -import 'package:mnemo_cards/features/packs/preview_packs_poller.dart'; -import 'package:mnemo_cards/features/purchase/purchase_manager.dart'; -import 'package:mnemo_cards/features/subscription/subscription_manager.dart'; -import 'package:mnemo_cards/managers/favorite_cards.dart'; -import 'package:mnemo_cards/managers/repository/http_repository.dart'; - -import '../features/deeplinks/deeplinks_manager_impl.dart'; -import '../features/packs/images_holder.dart'; -import '../features/packs/pack_cache_manager.dart'; -import '../features/packs/pack_updater.dart'; -import '../features/purchase/in_app_purchase.dart'; -import '../features/packs/pack_manager.dart'; -import '../features/subscription/subscription_api.dart'; -import '../features/tests/test_manager.dart'; -import '../managers/user_manager.dart'; -import 'injector.dart'; - -const locator = Locator(); - -class Locator { - GetIt get _getIt => getIt; - - const Locator(); - - HttpRepository get repository => _getIt.get(); - - PackManager get packManager => _getIt.get(); - - UserManager get userManager => _getIt.get(); - - SubscriptionApi get subscriptionApi => _getIt.get(); - - FavoriteCardsController get favoriteCardsController => - _getIt.get(); - - PackCacheManager get packCacheManager => _getIt.get(); - - PackUpdater get packUpdater => _getIt.get(); - - DeeplinkManager get deeplinkManager => _getIt.get(); - - PreviewPackHolder get previewPackHolder => _getIt.get(); - - PreviewPacksPoller get previewPackPoller => _getIt.get(); - - SubscriptionManager get subscriptionManager => - _getIt.get(); - - TestManager get testManager => _getIt.get(); - - ImagesHolder get imagesHolder => _getIt.get(); - - InAppPurchaseService get purchaseService => - _getIt.get(); - - PurchaseManager get purchaseManager => _getIt.get(); -} diff --git a/lib/domain/router/app_router.dart b/lib/domain/router/app_router.dart deleted file mode 100644 index e4e2d8e..0000000 --- a/lib/domain/router/app_router.dart +++ /dev/null @@ -1,93 +0,0 @@ -import 'package:auto_route/auto_route.dart'; - -import '../../di/locator.dart'; -import '../../main.dart' as main; -import 'app_router.gr.dart'; - -@AutoRouterConfig( - replaceInRouteName: 'Route,Screen', -) -class AppRouter extends RootStackRouter { - final bool admin; - - AppRouter(this.admin); - - @override - List get routes => [ - CustomRoute( - // initial: !admin, - initial: true, - page: MainTabsPage.page, - transitionsBuilder: TransitionsBuilders.noTransition, - children: [ - CustomRoute( - page: HomePage.page, - transitionsBuilder: TransitionsBuilders.noTransition, - durationInMilliseconds: 200, - ), - ]), - CustomRoute( - page: ProfilePage.page, - transitionsBuilder: TransitionsBuilders.slideLeftWithFade, - durationInMilliseconds: 200, - ), - CustomRoute( - page: SettingsPage.page, - transitionsBuilder: TransitionsBuilders.slideLeftWithFade, - durationInMilliseconds: 200, - ), - CustomRoute( - // initial: !locator.userManager.hasUser, - page: AuthPage.page, - transitionsBuilder: TransitionsBuilders.slideLeftWithFade, - durationInMilliseconds: 200, - ), - CustomRoute( - page: TestPage.page, - transitionsBuilder: TransitionsBuilders.slideLeftWithFade, - durationInMilliseconds: 200, - ), - CustomRoute( - page: CardGamePage.page, - transitionsBuilder: TransitionsBuilders.fadeIn, - durationInMilliseconds: 400, - ), - CustomRoute( - page: CardPackPage.page, - transitionsBuilder: TransitionsBuilders.fadeIn, - durationInMilliseconds: 200, - ), - ]; - - static void openAuth() { - final router = main.appRouter; - if (!router.isRouteActive(AuthPage.name)) { - router.replace(PageRouteInfo(AuthPage.name)); - } - } - - static void closeAuthPage() { - final router = main.appRouter; - if (router.isRouteActive(AuthPage.name)) { - router.replace(PageRouteInfo(HomePage.name)); - } - } - - static void openAuthOrProfile() { - if (locator.userManager.hasUser) { - openProfile(); - } else { - openAuth(); - } - } - - static void openProfile() { - final router = main.appRouter; - if (!router.isRouteActive(ProfilePage.name)) { - if (router.current.name == AuthPage.name) { - router.maybePop(); - } - router.push(PageRouteInfo(ProfilePage.name)); - } - } -} diff --git a/lib/domain/router/app_router.gr.dart b/lib/domain/router/app_router.gr.dart deleted file mode 100644 index 020f7cb..0000000 --- a/lib/domain/router/app_router.gr.dart +++ /dev/null @@ -1,266 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -// ************************************************************************** -// AutoRouterGenerator -// ************************************************************************** - -// ignore_for_file: type=lint -// coverage:ignore-file - -// ignore_for_file: no_leading_underscores_for_library_prefixes -import 'package:auto_route/auto_route.dart' as _i9; -import 'package:flutter/material.dart' as _i11; -import 'package:mnemo_cards/features/tests/test_page.dart' as _i8; -import 'package:mnemo_cards/main.dart' as _i5; -import 'package:mnemo_cards/pages/auth_page.dart' as _i1; -import 'package:mnemo_cards/pages/home_page.dart' as _i4; -import 'package:mnemo_cards/pages/profile_page.dart' as _i6; -import 'package:mnemo_cards/pages/settgins_page.dart' as _i7; -import 'package:mnemo_cards/widgets/card_game/card_game_page.dart' as _i2; -import 'package:mnemo_cards/widgets/card_pack/card_pack_page.dart' as _i3; -import 'package:mnemo_cards_common/mnemo_cards_common.dart' as _i10; - -/// generated route for -/// [_i1.AuthPage] -class AuthPage extends _i9.PageRouteInfo { - const AuthPage({List<_i9.PageRouteInfo>? children}) - : super( - AuthPage.name, - initialChildren: children, - ); - - static const String name = 'AuthPage'; - - static _i9.PageInfo page = _i9.PageInfo( - name, - builder: (data) { - return _i1.AuthPage(); - }, - ); -} - -/// generated route for -/// [_i2.CardGamePage] -class CardGamePage extends _i9.PageRouteInfo { - CardGamePage({ - required List<_i10.GameCardDto> cards, - required _i11.Color? color, - int? startCardId, - bool infinite = false, - bool shuffle = true, - _i11.Key? key, - List<_i9.PageRouteInfo>? children, - }) : super( - CardGamePage.name, - args: CardGamePageArgs( - cards: cards, - color: color, - startCardId: startCardId, - infinite: infinite, - shuffle: shuffle, - key: key, - ), - initialChildren: children, - ); - - static const String name = 'CardGamePage'; - - static _i9.PageInfo page = _i9.PageInfo( - name, - builder: (data) { - final args = data.argsAs(); - return _i2.CardGamePage( - cards: args.cards, - color: args.color, - startCardId: args.startCardId, - infinite: args.infinite, - shuffle: args.shuffle, - key: args.key, - ); - }, - ); -} - -class CardGamePageArgs { - const CardGamePageArgs({ - required this.cards, - required this.color, - this.startCardId, - this.infinite = false, - this.shuffle = true, - this.key, - }); - - final List<_i10.GameCardDto> cards; - - final _i11.Color? color; - - final int? startCardId; - - final bool infinite; - - final bool shuffle; - - final _i11.Key? key; - - @override - String toString() { - return 'CardGamePageArgs{cards: $cards, color: $color, startCardId: $startCardId, infinite: $infinite, shuffle: $shuffle, key: $key}'; - } -} - -/// generated route for -/// [_i3.CardPackPage] -class CardPackPage extends _i9.PageRouteInfo { - CardPackPage({ - required _i10.CardPackPreviewDto previewDto, - _i11.Key? key, - List<_i9.PageRouteInfo>? children, - }) : super( - CardPackPage.name, - args: CardPackPageArgs( - previewDto: previewDto, - key: key, - ), - initialChildren: children, - ); - - static const String name = 'CardPackPage'; - - static _i9.PageInfo page = _i9.PageInfo( - name, - builder: (data) { - final args = data.argsAs(); - return _i3.CardPackPage( - args.previewDto, - key: args.key, - ); - }, - ); -} - -class CardPackPageArgs { - const CardPackPageArgs({ - required this.previewDto, - this.key, - }); - - final _i10.CardPackPreviewDto previewDto; - - final _i11.Key? key; - - @override - String toString() { - return 'CardPackPageArgs{previewDto: $previewDto, key: $key}'; - } -} - -/// generated route for -/// [_i4.HomePage] -class HomePage extends _i9.PageRouteInfo { - const HomePage({List<_i9.PageRouteInfo>? children}) - : super( - HomePage.name, - initialChildren: children, - ); - - static const String name = 'HomePage'; - - static _i9.PageInfo page = _i9.PageInfo( - name, - builder: (data) { - return _i4.HomePage(); - }, - ); -} - -/// generated route for -/// [_i5.MainTabsPage] -class MainTabsPage extends _i9.PageRouteInfo { - const MainTabsPage({List<_i9.PageRouteInfo>? children}) - : super( - MainTabsPage.name, - initialChildren: children, - ); - - static const String name = 'MainTabsPage'; - - static _i9.PageInfo page = _i9.PageInfo( - name, - builder: (data) { - return const _i5.MainTabsPage(); - }, - ); -} - -/// generated route for -/// [_i6.ProfilePage] -class ProfilePage extends _i9.PageRouteInfo { - const ProfilePage({List<_i9.PageRouteInfo>? children}) - : super( - ProfilePage.name, - initialChildren: children, - ); - - static const String name = 'ProfilePage'; - - static _i9.PageInfo page = _i9.PageInfo( - name, - builder: (data) { - return _i6.ProfilePage(); - }, - ); -} - -/// generated route for -/// [_i7.SettingsPage] -class SettingsPage extends _i9.PageRouteInfo { - const SettingsPage({List<_i9.PageRouteInfo>? children}) - : super( - SettingsPage.name, - initialChildren: children, - ); - - static const String name = 'SettingsPage'; - - static _i9.PageInfo page = _i9.PageInfo( - name, - builder: (data) { - return _i7.SettingsPage(); - }, - ); -} - -/// generated route for -/// [_i8.TestPage] -class TestPage extends _i9.PageRouteInfo { - TestPage({ - required String testId, - List<_i9.PageRouteInfo>? children, - }) : super( - TestPage.name, - args: TestPageArgs(testId: testId), - initialChildren: children, - ); - - static const String name = 'TestPage'; - - static _i9.PageInfo page = _i9.PageInfo( - name, - builder: (data) { - final args = data.argsAs(); - return _i8.TestPage(args.testId); - }, - ); -} - -class TestPageArgs { - const TestPageArgs({required this.testId}); - - final String testId; - - @override - String toString() { - return 'TestPageArgs{testId: $testId}'; - } -} diff --git a/lib/domain/router/dialogs.dart b/lib/domain/router/dialogs.dart deleted file mode 100644 index b869db2..0000000 --- a/lib/domain/router/dialogs.dart +++ /dev/null @@ -1,78 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:mnemo_cards/theme/themes.dart'; - -import '../../main.dart'; - -Future showInfoDialog(String message, {Completer? completer}) { - final context = scaffoldKey.currentContext!; - completer ??= Completer(); - final result = showDialog( - context: context, - useRootNavigator: false, - builder: (context) { - return Center( - child: Material( - color: Colors.transparent, - child: Container( - margin: EdgeInsets.all(4.0.h), - padding: EdgeInsets.all(4.0.h), - height: 200.h, - width: MediaQuery.of(context).size.width * 0.8, - decoration: BoxDecoration( - color: white, - border: Border.all( - color: Colors.grey.withOpacity(0.5), - width: 1, - ), - borderRadius: BorderRadius.circular(12.0), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - child: Center( - child: Text( - message, - textAlign: TextAlign.center, - ), - ), - ), - GestureDetector( - onTap: Navigator.of(context).pop, - child: Container( - height: 40.h, - decoration: BoxDecoration( - color: testBlue, - border: Border.all( - color: Colors.grey.withOpacity(0.5), - width: 1, - ), - borderRadius: BorderRadius.circular(8.0), - ), - alignment: Alignment.center, - child: Text('Оки'), - ), - ) - ], - ), - ), - ), - ); - }, - ); - bool dialogClosed = false; - completer.future.then((_) { - if (!dialogClosed) { - return Navigator.of(context).pop(); - } - }); - return result.then((_) { - dialogClosed = true; - if (completer?.isCompleted == false) { - completer!.complete(); - } - }); -} \ No newline at end of file diff --git a/lib/features/analytics/analytics.dart b/lib/features/analytics/analytics.dart deleted file mode 100644 index ee3cdb2..0000000 --- a/lib/features/analytics/analytics.dart +++ /dev/null @@ -1,230 +0,0 @@ -import 'package:firebase_analytics/firebase_analytics.dart'; -import 'package:firebase_crashlytics/firebase_crashlytics.dart'; -import 'package:mnemo_cards/managers/user_manager.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; - -class Analytics { - static final _analytics = FirebaseAnalytics.instance; - static final _crashlytics = FirebaseCrashlytics.instance; - - static Future setUser(UserDto user) { - return _analytics.setUserId(id: user.id?.toString()); - } - - static Future login(ExternalIdType idType) { - return _analytics.logLogin(loginMethod: idType.name); - } - - static Future logout(UserDto? dto) async { - await _analytics.logEvent( - name: 'logout', - parameters: { - 'id': dto?.id, - }.asFirebaseMap, - ); - return _analytics.setUserId(id: dto?.id?.toString()); - } - - static Future profilePageOpened(UserDto? dto) { - return _analytics.logScreenView( - screenClass: 'profile', - screenName: 'profile', - parameters: { - 'id': dto?.id, - }.asFirebaseMap); - } - - static Future settingsPageOpened(UserDto? dto) { - return _analytics.logScreenView( - screenClass: 'settings', - screenName: 'settings', - parameters: { - 'id': dto?.id, - }.asFirebaseMap); - } - - static Future availablePackOpened(CardPackDto dto) { - return _analytics.logScreenView( - screenClass: 'pack', - screenName: 'available', - parameters: { - 'id': dto.id, - 'title': dto.title, - 'version': dto.version, - }.asFirebaseMap); - } - - static Future buyPackOpened(CardPackBuyDto dto) { - return _analytics.logScreenView( - screenClass: 'pack', - screenName: 'buy', - parameters: { - 'id': dto.id, - 'title': dto.title, - 'version': dto.version, - 'price': dto.price, - }.asFirebaseMap); - } - - static Future buyPackPressed(CardPackBuyDto dto, PaymentSystem system) { - return _analytics.logEvent( - name: 'buy', - parameters: { - 'id': dto.id, - 'title': dto.title, - 'version': dto.version, - 'price': dto.price, - 'system': system.name, - }.asFirebaseMap); - } - - static Future buyDeeplinkError( - Uri? uri, { - Map? data, - String? message, - }) { - return _analytics.logEvent( - name: 'buy_deeplink_error', - parameters: { - 'uri': uri.toString(), - if (message != null) 'message': message, - if (data != null) ...data, - }.asFirebaseMap); - } - - static Future buyError( - CardPackBuyDto? dto, - PaymentSystem system, { - String? id, - String? paymentId, - Map? data, - String? message, - }) { - return _analytics.logEvent( - name: 'buy_error', - parameters: { - 'id': id ?? dto?.id.toString(), - 'paymentId': paymentId, - 'system': system.name, - 'version': dto?.version.toString(), - 'price': dto?.price.toString(), - if (message != null) 'message': message, - if (data != null) ...data, - }.asFirebaseMap); - } - - static Future buySubscriptionError( - SubscriptionPlanDto? dto, - PaymentSystem system, { - Map? data, - String? message, - }) { - return _analytics.logEvent( - name: 'buy_sub_error', - parameters: { - 'id': dto?.id.toString(), - 'system': system.name, - 'currency': dto?.currency, - 'price': dto?.price.toString(), - if (message != null) 'message': message, - if (data != null) ...data, - }.asFirebaseMap, - ); - } - - static Future buyEvent( - CardPackBuyDto? dto, - PaymentSystem system, { - Map? data, - String? message, - }) { - return _analytics.logEvent( - name: 'buy_event', - parameters: { - 'id': dto?.id, - 'system': system.name, - 'version': dto?.version, - 'price': dto?.price, - if (message != null) 'message': message, - if (data != null) ...data, - }.asFirebaseMap); - } - - static Future buyComplete( - CardPackBuyDto? dto, - PaymentSystem system, { - String? id, - Map? data, - String? message, - }) { - return _analytics.logEvent( - name: 'buy_complete', - parameters: { - 'id': id ?? dto?.id, - 'system': system.name, - 'version': dto?.version, - 'price': dto?.price, - if (message != null) 'message': message, - if (data != null) ...data, - }.asFirebaseMap, - ); - } - - static Future loadingPackOpened(CardPackDto dto, int timeMillis) { - return _analytics.logScreenView( - screenClass: 'pack', - screenName: 'loading', - parameters: { - 'id': dto.id, - 'title': dto.title, - 'version': dto.version, - 'time': timeMillis, - }.asFirebaseMap); - } - - static Future testOpened(TestDto dto) { - return _analytics.logScreenView( - screenClass: 'test', - screenName: 'test', - parameters: { - 'id': dto.id, - 'name': dto.name, - 'version': dto.version, - 'questions': dto.questions.length, - }.asFirebaseMap); - } - - static crashlyticsError({ - Object? e, - StackTrace? s, - String? message, - Map? data, - }) => - _crashlytics.recordError( - e, - s, - reason: message, - information: data?.asFirebaseMap.entries.toList() ?? [], - ); - - static Future deeplinkError( - Uri uri, { - Map? data, - String? message, - }) { - return _analytics.logEvent( - name: 'deeplink_error', - parameters: { - 'uri': uri.toString(), - if (message != null) 'message': message, - if (data != null) ...data, - }.asFirebaseMap, - ); - } -} - -extension on Map { - Map get asFirebaseMap => (Map.from(this) - ..removeWhere((k, v) => v == null)) - .map((k, v) => MapEntry(k, (v is String || v is num) ? v : v.toString())); -} diff --git a/lib/features/card_flipper/flip_card.dart b/lib/features/card_flipper/flip_card.dart deleted file mode 100644 index 9b5a87e..0000000 --- a/lib/features/card_flipper/flip_card.dart +++ /dev/null @@ -1,142 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'flip_side.dart'; -import 'flip_card_controllers.dart'; -import 'dart:math'; - -/// [FlipCard] A component that provides a flip card animation - -class FlipCard extends StatefulWidget { - /// [controller] used to ccontrol the flip - final FlipCardController controller; - - ///[frontWidget] The Front side widget of the card - final Widget frontWidget; - - ///[backWidget] The Back side widget of the card - final Widget backWidget; - - /// [onTapFlipping] When enabled, the card will flip automatically when touched. - final bool onTapFlipping; - - /// [axis] The flip axis [Horizontal] and [Vertical] - final FlipAxis axis; - - /// [rotateSide] The card rotate side - final RotateSide rotateSide; - - /// [animationDuration] The amount of milliseconds a turn animation will take. - final Duration animationDuration; - - /// [disableSplashEffect] The option for disable Inkwell widget's splash effect. - final bool disableSplashEffect; - - /// [splashColor] The option for Inkwell widget's splashColor. - final Color? splashColor; - - /// [focusColor] The option for Inkwell widget's focusColor. - final Color? focusColor; - - const FlipCard({ - Key? key, - this.focusColor, - this.splashColor, - this.onTapFlipping = false, - this.disableSplashEffect = false, - required this.frontWidget, - required this.backWidget, - required this.controller, - this.axis = FlipAxis.vertical, - required this.rotateSide, - this.animationDuration = const Duration(milliseconds: 800), - }) : super(key: key); - - @override - FlipCardState createState() => FlipCardState(); -} - -class FlipCardState extends State with TickerProviderStateMixin { - late AnimationController animationController; - double anglePlus = 0; - late bool _isFront; - StreamSubscription? _sub; - - @override - void initState() { - super.initState(); - _isFront = widget.controller.isFront; - animationController = - AnimationController(duration: widget.animationDuration, vsync: this); - if (!_isFront) { - animationController.value = 1.0; - anglePlus = pi; - } - _sub ??= widget.controller.asStream.distinct().listen((isFront) async { - if (this._isFront != isFront) { - if (animationController.isAnimating) return; - this._isFront = isFront; - await animationController - .forward(from: 0) - .then((value) => anglePlus = pi); - } - }); - } - - @override - void dispose() { - animationController.dispose(); - _sub?.cancel(); - _sub = null; - super.dispose(); - } - - @override - Widget build(BuildContext context) => AnimatedBuilder( - animation: animationController, - builder: (context, child) { - double piValue = 0.0; - if (widget.rotateSide == RotateSide.top || - widget.rotateSide == RotateSide.left) { - piValue = pi; - } else { - piValue = -pi; - } - print('animation controller ${animationController.value}'); - double angle = animationController.value * piValue; - late Matrix4 transform; - late Matrix4 transformForBack; - if (_isFront) angle += anglePlus; - if (widget.axis == FlipAxis.horizontal) { - transform = Matrix4.identity() - ..setEntry(3, 2, 0.001) - ..rotateX(angle); - transformForBack = Matrix4.identity()..rotateX(pi); - } else { - transform = Matrix4.identity() - ..setEntry(3, 2, 0.001) - ..rotateY(angle); - transformForBack = Matrix4.identity()..rotateY(pi); - } - - final isFrontWidget = _isFrontWidget(angle.abs()); - - return Transform( - alignment: Alignment.center, - transform: transform, - child: isFrontWidget - ? widget.frontWidget - : Transform( - transform: transformForBack, - alignment: Alignment.center, - child: widget.backWidget, - ), - ); - }); - - bool _isFrontWidget(double angle) { - const degrees90 = pi / 2; - const degrees270 = 3 * pi / 2; - return angle <= degrees90 || angle >= degrees270; - } -} diff --git a/lib/features/card_flipper/flip_card_controllers.dart b/lib/features/card_flipper/flip_card_controllers.dart deleted file mode 100644 index a0e53c2..0000000 --- a/lib/features/card_flipper/flip_card_controllers.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'dart:async'; - -import 'package:rxdart/rxdart.dart'; - - -///This controller used to call Fliping -class FlipCardController { - final StreamController _controller = StreamController.broadcast(); - bool isFront = true; - - FlipCardController(); - - /// Flip the card - Future flipcard() async { - isFront = !isFront; - _controller.add(isFront); - } - - bool get state => isFront; - - Stream get asStream => - _controller.stream.asBroadcastStream().startWith(state); -} diff --git a/lib/features/card_flipper/flip_side.dart b/lib/features/card_flipper/flip_side.dart deleted file mode 100644 index 66dcde9..0000000 --- a/lib/features/card_flipper/flip_side.dart +++ /dev/null @@ -1,13 +0,0 @@ -/// [FlipAxis] The flip axis [Horizontal] and [Vertical] -enum FlipAxis { - horizontal, - vertical, -} - -/// [rotateSide] The card rotate side -enum RotateSide { - right, - left, - top, - bottom, -} diff --git a/lib/features/card_swiper/flutter_card_swiper.dart b/lib/features/card_swiper/flutter_card_swiper.dart deleted file mode 100644 index fa2a0da..0000000 --- a/lib/features/card_swiper/flutter_card_swiper.dart +++ /dev/null @@ -1,9 +0,0 @@ -/// A Tinder-like card swiper package. It allows you to swipe left, right, up, -/// and down and define your own business logic for each direction. Very smooth -/// animations supporting Android, iOS, Web & Desktop. -library flutter_card_swiper; - -export 'src/allowed_swipe_direction.dart'; -export 'src/card_swiper.dart'; -export 'src/card_swiper_controller.dart'; -export 'src/enums.dart'; diff --git a/lib/features/card_swiper/src/allowed_swipe_direction.dart b/lib/features/card_swiper/src/allowed_swipe_direction.dart deleted file mode 100644 index 69486f3..0000000 --- a/lib/features/card_swiper/src/allowed_swipe_direction.dart +++ /dev/null @@ -1,62 +0,0 @@ -/// Class to define the direction in which the card can be swiped -class AllowedSwipeDirection { - /// Set to true to allow the card to be swiped in the up direction - final bool up; - - /// Set to true to allow the card to be swiped in the down direction - final bool down; - - /// Set to true to allow the card to be swiped in the left direction - final bool left; - - /// Set to true to allow the card to be swiped in the right direction - final bool right; - - /// Define the direction in which the card can be swiped - const AllowedSwipeDirection._({ - required this.up, - required this.down, - required this.left, - required this.right, - }); - - /// Allow the card to be swiped in any direction - const AllowedSwipeDirection.all() - : up = true, - down = true, - right = true, - left = true; - - /// Does not allow the card to be swiped in any direction - const AllowedSwipeDirection.none() - : up = false, - down = false, - right = false, - left = false; - - /// Allow the card to be swiped in only the specified directions - factory AllowedSwipeDirection.only({ - up = false, - down = false, - left = false, - right = false, - }) => - AllowedSwipeDirection._( - up: up, - down: down, - left: left, - right: right, - ); - - /// Allow the card to be swiped symmetrically in horizontal or vertical directions - factory AllowedSwipeDirection.symmetric({ - horizontal = false, - vertical = false, - }) => - AllowedSwipeDirection._( - up: vertical, - down: vertical, - right: horizontal, - left: horizontal, - ); -} diff --git a/lib/features/card_swiper/src/card_animation.dart b/lib/features/card_swiper/src/card_animation.dart deleted file mode 100644 index d4dc443..0000000 --- a/lib/features/card_swiper/src/card_animation.dart +++ /dev/null @@ -1,276 +0,0 @@ -import 'dart:math' as math; -import 'dart:ui'; - -import 'package:flutter/widgets.dart'; - -import 'allowed_swipe_direction.dart'; -import 'enums.dart'; - -class CardAnimation { - CardAnimation({ - required this.animationController, - required this.maxAngle, - required this.initialScale, - required this.initialOffset, - this.isHorizontalSwipingEnabled = true, - this.isVerticalSwipingEnabled = true, - this.allowedSwipeDirection = const AllowedSwipeDirection.all(), - this.onSwipeDirectionChanged, - }) : scale = initialScale; - - final double maxAngle; - final double initialScale; - final Offset initialOffset; - final AnimationController animationController; - final bool isHorizontalSwipingEnabled; - final bool isVerticalSwipingEnabled; - final AllowedSwipeDirection allowedSwipeDirection; - final ValueChanged? onSwipeDirectionChanged; - - double left = 0; - double top = 0; - double total = 0; - double angle = 0; - double scale; - Offset difference = Offset.zero; - - late Animation _leftAnimation; - late Animation _topAnimation; - late Animation _scaleAnimation; - late Animation _differenceAnimation; - - double get _maxAngleInRadian => maxAngle * (math.pi / 180); - - void sync() { - left = _leftAnimation.value; - top = _topAnimation.value; - scale = _scaleAnimation.value; - difference = _differenceAnimation.value; - } - - void reset() { - animationController.reset(); - left = 0; - top = 0; - total = 0; - angle = 0; - scale = initialScale; - difference = Offset.zero; - } - - void update(double dx, double dy, bool inverseAngle) { - if (allowedSwipeDirection.right && allowedSwipeDirection.left) { - if (left > 0) { - onSwipeDirectionChanged?.call(CardSwiperDirection.right); - } else if (left < 0) { - onSwipeDirectionChanged?.call(CardSwiperDirection.left); - } - left += dx; - } else if (allowedSwipeDirection.right) { - if (left >= 0) { - onSwipeDirectionChanged?.call(CardSwiperDirection.right); - left += dx; - } - } else if (allowedSwipeDirection.left) { - if (left <= 0) { - onSwipeDirectionChanged?.call(CardSwiperDirection.left); - left += dx; - } - } - - if (allowedSwipeDirection.up && allowedSwipeDirection.down) { - if (top > 0) { - onSwipeDirectionChanged?.call(CardSwiperDirection.bottom); - } else if (top < 0) { - onSwipeDirectionChanged?.call(CardSwiperDirection.top); - } - top += dy; - } else if (allowedSwipeDirection.up) { - if (top <= 0) { - onSwipeDirectionChanged?.call(CardSwiperDirection.top); - top += dy; - } - } else if (allowedSwipeDirection.down) { - if (top >= 0) { - onSwipeDirectionChanged?.call(CardSwiperDirection.bottom); - top += dy; - } - } - - total = left + top; - updateAngle(inverseAngle); - updateScale(); - updateDifference(); - } - - void updateAngle(bool inverse) { - angle = clampDouble( - _maxAngleInRadian * left / 1000, - -_maxAngleInRadian, - _maxAngleInRadian, - ); - if (inverse) angle *= -1; - } - - void updateScale() { - scale = clampDouble(initialScale + (total.abs() / 5000), initialScale, 1.0); - } - - void updateDifference() { - final discrepancy = (total / 10).abs(); - - var diffX = 0.0, diffY = 0.0; - - if (initialOffset.dx > 0) { - diffX = discrepancy; - } else if (initialOffset.dx < 0) { - diffX = -discrepancy; - } - - if (initialOffset.dy < 0) { - diffY = -discrepancy; - } else if (initialOffset.dy > 0) { - diffY = discrepancy; - } - - difference = Offset(diffX, diffY); - } - - void animate(BuildContext context, CardSwiperDirection direction) { - switch (direction) { - case CardSwiperDirection.left: - return animateHorizontally(context, false); - case CardSwiperDirection.right: - return animateHorizontally(context, true); - case CardSwiperDirection.top: - return animateVertically(context, false); - case CardSwiperDirection.bottom: - return animateVertically(context, true); - default: - return; - } - } - - void animateHorizontally(BuildContext context, bool isToRight) { - final screenWidth = MediaQuery.of(context).size.width; - - _leftAnimation = Tween( - begin: left, - end: isToRight ? screenWidth : -screenWidth, - ).animate(animationController); - _topAnimation = Tween( - begin: top, - end: top + top, - ).animate(animationController); - _scaleAnimation = Tween( - begin: scale, - end: 1.0, - ).animate(animationController); - _differenceAnimation = Tween( - begin: difference, - end: initialOffset, - ).animate(animationController); - animationController.forward(); - } - - void animateVertically(BuildContext context, bool isToBottom) { - final screenHeight = MediaQuery.of(context).size.height; - - _leftAnimation = Tween( - begin: left, - end: left + left, - ).animate(animationController); - _topAnimation = Tween( - begin: top, - end: isToBottom ? screenHeight : -screenHeight, - ).animate(animationController); - _scaleAnimation = Tween( - begin: scale, - end: 1.0, - ).animate(animationController); - _differenceAnimation = Tween( - begin: difference, - end: initialOffset, - ).animate(animationController); - animationController.forward(); - } - - void animateBack(BuildContext context) { - _leftAnimation = Tween( - begin: left, - end: 0, - ).animate(animationController); - _topAnimation = Tween( - begin: top, - end: 0, - ).animate(animationController); - _scaleAnimation = Tween( - begin: scale, - end: initialScale, - ).animate(animationController); - _differenceAnimation = Tween( - begin: difference, - end: Offset.zero, - ).animate(animationController); - animationController.forward(); - } - - void animateUndo(BuildContext context, CardSwiperDirection direction) { - switch (direction) { - case CardSwiperDirection.left: - return animateUndoHorizontally(context, false); - case CardSwiperDirection.right: - return animateUndoHorizontally(context, true); - case CardSwiperDirection.top: - return animateUndoVertically(context, false); - case CardSwiperDirection.bottom: - return animateUndoVertically(context, true); - default: - return; - } - } - - void animateUndoHorizontally(BuildContext context, bool isToRight) { - final size = MediaQuery.of(context).size; - - _leftAnimation = Tween( - begin: isToRight ? size.width : -size.width, - end: 0, - ).animate(animationController); - _topAnimation = Tween( - begin: top, - end: top + top, - ).animate(animationController); - _scaleAnimation = Tween( - begin: 1.0, - end: scale, - ).animate(animationController); - _differenceAnimation = Tween( - begin: initialOffset, - end: difference, - ).animate(animationController); - animationController.forward(); - } - - void animateUndoVertically(BuildContext context, bool isToBottom) { - final size = MediaQuery.of(context).size; - - _leftAnimation = Tween( - begin: left, - end: left + left, - ).animate(animationController); - _topAnimation = Tween( - begin: isToBottom ? -size.height : size.height, - end: 0, - ).animate(animationController); - _scaleAnimation = Tween( - begin: 1.0, - end: scale, - ).animate(animationController); - _differenceAnimation = Tween( - begin: initialOffset, - end: difference, - ).animate(animationController); - animationController.forward(); - } -} diff --git a/lib/features/card_swiper/src/card_swiper.dart b/lib/features/card_swiper/src/card_swiper.dart deleted file mode 100644 index 736382a..0000000 --- a/lib/features/card_swiper/src/card_swiper.dart +++ /dev/null @@ -1,549 +0,0 @@ -import 'dart:collection'; -import 'dart:math' as math; - -import 'package:flutter/widgets.dart'; - -import 'allowed_swipe_direction.dart'; -import 'card_animation.dart'; -import 'card_swiper_controller.dart'; -import 'enums.dart'; -import 'typedefs.dart'; -import 'undoable.dart'; -import 'extensions.dart'; - -class CardSwiper extends StatefulWidget { - /// Function that builds each card in the stack. - /// - /// The function is called with the index of the card to be built, the build context, the ratio - /// of vertical drag to [threshold] as a percentage, and the ratio of horizontal drag to [threshold] - /// as a percentage. The function should return a widget that represents the card at the given index. - /// It can return `null`, which will result in an empty card being displayed. - final NullableCardBuilder cardBuilder; - - /// The number of cards in the stack. - /// - /// The [cardsCount] parameter specifies the number of cards that will be displayed in the stack. - /// - /// This parameter is required and must be greater than 0. - final int cardsCount; - - /// The index of the card to display initially. - /// - /// Defaults to 0, meaning the first card in the stack is displayed initially. - final int initialIndex; - - /// The [CardSwiperController] used to control the swiper externally. - /// - /// If `null`, the swiper can only be controlled by user input. - final CardSwiperController? controller; - - /// The duration of each swipe animation. - /// - /// Defaults to 200 milliseconds. - final Duration duration; - - /// The padding around the swiper. - /// - /// Defaults to `EdgeInsets.symmetric(horizontal: 20, vertical: 25)`. - final EdgeInsetsGeometry padding; - - /// The maximum angle the card reaches while swiping. - /// - /// Must be between 0 and 360 degrees. Defaults to 30 degrees. - final double maxAngle; - - /// The threshold from which the card is swiped away. - /// - /// Must be between 1 and 100 percent of the card width. Defaults to 50 percent. - final int threshold; - - /// The scale of the card that is behind the front card. - /// - /// The [scale] and [backCardOffset] both impact the positions of the back cards. - /// In order to keep the back card position same after changing the [scale], - /// the [backCardOffset] should also be adjusted. - /// * As a rough rule of thumb, 0.1 change in [scale] effects an - /// [backCardOffset] of ~35px. - /// - /// Must be between 0 and 1. Defaults to 0.9. - final double scale; - - /// Whether swiping is disabled. - /// - /// If `true`, swiping is disabled, except when triggered by the [controller]. - /// - /// Defaults to `false`. - final bool isDisabled; - - /// Callback function that is called when a swipe action is performed. - /// - /// The function is called with the oldIndex, the currentIndex and the direction of the swipe. - /// If the function returns `false`, the swipe action is canceled and the current card remains - /// on top of the stack. If the function returns `true`, the swipe action is performed as expected. - final CardSwiperOnSwipe? onSwipe; - - /// Callback function that is called when there are no more cards to swipe. - final CardSwiperOnEnd? onEnd; - - /// Callback function that is called when the swiper is disabled. - final CardSwiperOnTapDisabled? onTapDisabled; - - /// The direction in which the card is swiped when triggered by the [controller]. - /// - /// Defaults to [CardSwiperDirection.right]. - final CardSwiperDirection direction; - - /// Defined the directions in which the card is allowed to be swiped. - /// Defaults to [AllowedSwipeDirection.all] - final AllowedSwipeDirection allowedSwipeDirection; - - /// A boolean value that determines whether the card stack should loop. When the last card is swiped, - /// if isLoop is true, the first card will become the last card again. The default value is true. - final bool isLoop; - - /// An integer that determines the number of cards that are displayed at the same time. - /// The default value is 2. Note that you must display at least one card, and no more than the [cardsCount] parameter. - final int numberOfCardsDisplayed; - - /// Callback function that is called when a card is unswiped. - /// - /// The function is called with the oldIndex, the currentIndex and the direction of the previous swipe. - /// If the function returns `false`, the undo action is canceled and the current card remains - /// on top of the stack. If the function returns `true`, the undo action is performed as expected. - final CardSwiperOnUndo? onUndo; - - /// Callback function that is called when a card swipe direction changes. - /// - /// The function is called with the last detected horizontal direction and the last detected vertical direction - final CardSwiperDirectionChange? onSwipeDirectionChange; - - /// The offset of the back card from the front card. - /// - /// In order to keep the back card position same after changing the [backCardOffset], - /// the [scale] should also be adjusted. - /// * As a rough rule of thumb, 35px change in [backCardOffset] effects a - /// [scale] change of 0.1. - /// - /// Must be a positive value. Defaults to Offset(0, 40). - final Offset backCardOffset; - - final int adsCount; - - const CardSwiper({ - Key? key, - required this.cardBuilder, - required this.cardsCount, - this.controller, - this.adsCount = 0, - this.initialIndex = 0, - this.padding = const EdgeInsets.symmetric(horizontal: 20, vertical: 25), - this.duration = const Duration(milliseconds: 200), - this.maxAngle = 30, - this.threshold = 50, - this.scale = 0.9, - this.isDisabled = false, - this.onTapDisabled, - this.onSwipe, - this.onEnd, - this.direction = CardSwiperDirection.right, - this.onSwipeDirectionChange, - this.allowedSwipeDirection = const AllowedSwipeDirection.all(), - this.isLoop = true, - this.numberOfCardsDisplayed = 2, - this.onUndo, - this.backCardOffset = const Offset(0, 40), - }) : assert( - maxAngle >= 0 && maxAngle <= 360, - 'maxAngle must be between 0 and 360', - ), - assert( - threshold >= 1 && threshold <= 100, - 'threshold must be between 1 and 100', - ), - assert( - direction != CardSwiperDirection.none, - 'direction must not be none', - ), - assert( - scale >= 0 && scale <= 1, - 'scale must be between 0 and 1', - ), - assert( - numberOfCardsDisplayed >= 1 && numberOfCardsDisplayed <= cardsCount, - 'you must display at least one card, and no more than [cardsCount]', - ), - assert( - initialIndex >= 0 && initialIndex < cardsCount, - 'initialIndex must be between 0 and [cardsCount]', - ), - super(key: key); - - @override - State createState() => _CardSwiperState(); -} - -class _CardSwiperState extends State - with SingleTickerProviderStateMixin { - late CardAnimation _cardAnimation; - late AnimationController _animationController; - - SwipeType _swipeType = SwipeType.none; - CardSwiperDirection _detectedDirection = CardSwiperDirection.none; - CardSwiperDirection _detectedHorizontalDirection = CardSwiperDirection.none; - CardSwiperDirection _detectedVerticalDirection = CardSwiperDirection.none; - bool _tappedOnTop = false; - - final _undoableIndex = Undoable(null); - final Queue _directionHistory = Queue(); - - bool usePrevIndex = false; - - int? get _currentIndex => - usePrevIndex ? _undoableIndex.previousState : _undoableIndex.state; - - int? get _nextIndex => getValidIndexOffset(1); - - bool get _canSwipe => _currentIndex != null && !widget.isDisabled; - - @override - void initState() { - super.initState(); - - _undoableIndex.state = widget.initialIndex; - - widget.controller?.addListener(_controllerListener); - - _animationController = AnimationController( - duration: widget.duration, - vsync: this, - ) - ..addListener(_animationListener) - ..addStatusListener(_animationStatusListener); - - _cardAnimation = CardAnimation( - animationController: _animationController, - maxAngle: widget.maxAngle, - initialScale: widget.scale, - allowedSwipeDirection: widget.allowedSwipeDirection, - initialOffset: widget.backCardOffset, - onSwipeDirectionChanged: onSwipeDirectionChanged, - ); - } - - void onSwipeDirectionChanged(CardSwiperDirection direction) { - if (direction == CardSwiperDirection.none) { - _detectedVerticalDirection = direction; - _detectedHorizontalDirection = direction; - widget.onSwipeDirectionChange - ?.call(_detectedHorizontalDirection, _detectedVerticalDirection); - } else if (direction == CardSwiperDirection.right || - direction == CardSwiperDirection.left) { - if (_detectedHorizontalDirection != direction) { - _detectedHorizontalDirection = direction; - widget.onSwipeDirectionChange - ?.call(_detectedHorizontalDirection, _detectedVerticalDirection); - } - } else if (direction == CardSwiperDirection.top || - direction == CardSwiperDirection.bottom) { - if (_detectedVerticalDirection != direction) { - _detectedVerticalDirection = direction; - widget.onSwipeDirectionChange - ?.call(_detectedHorizontalDirection, _detectedVerticalDirection); - } - } - } - - @override - void dispose() { - super.dispose(); - _animationController.dispose(); - widget.controller?.removeListener(_controllerListener); - } - - @override - Widget build(BuildContext context) { - widget.controller?.setIndex(_currentIndex ?? -1); - - return LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) { - return Padding( - padding: widget.padding, - child: LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) { - return Stack( - alignment: Alignment.center, - clipBehavior: Clip.none, - fit: StackFit.expand, - children: List.generate(numberOfCardsOnScreen(), (index) { - if (index == 0) return _frontItem(constraints); - return _backItem(constraints, index); - }).reversed.toList(), - ); - }, - ), - ); - }, - ); - } - - Widget _frontItem(BoxConstraints constraints) { - return Positioned( - left: _cardAnimation.left, - top: _cardAnimation.top, - child: GestureDetector( - child: Transform.rotate( - angle: _cardAnimation.angle, - child: ConstrainedBox( - constraints: constraints, - child: widget.cardBuilder( - context, - _currentIndex!, - (100 * _cardAnimation.left / widget.threshold).ceil(), - (100 * _cardAnimation.top / widget.threshold).ceil(), - _cardAnimation.scale, - ), - ), - ), - onTap: () async { - if (widget.isDisabled) { - await widget.onTapDisabled?.call(); - } - }, - onPanStart: (tapInfo) { - if (!widget.isDisabled) { - final renderBox = context.findRenderObject()! as RenderBox; - final position = renderBox.globalToLocal(tapInfo.globalPosition); - - if (position.dy < renderBox.size.height / 2) _tappedOnTop = true; - } - }, - onPanUpdate: (tapInfo) { - if (!widget.isDisabled && - tapInfo.delta.dx.abs() > 2 * tapInfo.delta.dy.abs()) { - setState(() { - _cardAnimation.update( - tapInfo.delta.dx, - tapInfo.delta.dy, - _tappedOnTop, - ); - }); - } - }, - onPanEnd: (tapInfo) { - if (_canSwipe) { - _tappedOnTop = false; - _onEndAnimation(); - } - }, - ), - ); - } - - Widget _backItem(BoxConstraints constraints, int index) { - final scale = _cardAnimation.scale - ((1 - widget.scale) * (index - 1)); - return Positioned( - top: (widget.backCardOffset.dy * index) - _cardAnimation.difference.dy, - left: (widget.backCardOffset.dx * index) - _cardAnimation.difference.dx, - child: Transform.scale( - scale: scale, - child: ConstrainedBox( - constraints: constraints, - child: widget.cardBuilder( - context, - getValidIndexOffset(index)!, - 0, - 0, - scale, - ), - ), - ), - ); - } - - void _controllerListener() { - switch (widget.controller?.state) { - case CardSwiperState.swipe: - return _swipe(widget.direction); - case CardSwiperState.swipeLeft: - return _swipe(CardSwiperDirection.left); - case CardSwiperState.swipeRight: - return _swipe(CardSwiperDirection.right); - case CardSwiperState.swipeTop: - return _swipe(CardSwiperDirection.top); - case CardSwiperState.swipeBottom: - return _swipe(CardSwiperDirection.bottom); - case CardSwiperState.undo: - return _undo(); - case CardSwiperState.restart: - return _restart(); - default: - return; - } - } - - void _animationListener() { - if (_animationController.status == AnimationStatus.forward) { - setState(_cardAnimation.sync); - } - } - - Future _animationStatusListener(AnimationStatus status) async { - if (status == AnimationStatus.completed) { - switch (_swipeType) { - case SwipeType.swipe: - await _handleCompleteSwipe(); - break; - default: - break; - } - - _reset(); - } - } - - Future _handleCompleteSwipe() async { - final isLastCard = _currentIndex! == widget.cardsCount - 1; - final shouldCancelSwipe = await widget.onSwipe - ?.call(_currentIndex!, _nextIndex, _detectedDirection) == - false; - - if (shouldCancelSwipe) { - return; - } - - _undoableIndex.state = _nextIndex; - _directionHistory.add(_detectedDirection); - - if (isLastCard) { - widget.onEnd?.call(); - } - } - - void _reset() { - onSwipeDirectionChanged(CardSwiperDirection.none); - _detectedDirection = CardSwiperDirection.none; - setState(() { - _animationController.reset(); - _cardAnimation.reset(); - _swipeType = SwipeType.none; - }); - } - - void _onEndAnimation() { - if (_cardAnimation.left.abs() > widget.threshold) { - final direction = _cardAnimation.left.isNegative - ? CardSwiperDirection.left - : CardSwiperDirection.right; - if (direction == CardSwiperDirection.left && - widget.allowedSwipeDirection.left || - direction == CardSwiperDirection.right && - widget.allowedSwipeDirection.right) { - _swipe(direction); - } else { - _goBack(); - } - } else if (_cardAnimation.top.abs() > widget.threshold) { - final direction = _cardAnimation.top.isNegative - ? CardSwiperDirection.top - : CardSwiperDirection.bottom; - if (direction == CardSwiperDirection.top && - widget.allowedSwipeDirection.up || - direction == CardSwiperDirection.bottom && - widget.allowedSwipeDirection.down) { - _swipe(direction); - } else { - _goBack(); - } - } else { - _goBack(); - } - } - - void _swipe(CardSwiperDirection direction) { - if (_currentIndex == null) return; - _swipeType = SwipeType.swipe; - _detectedDirection = direction; - _cardAnimation.animate(context, direction); - // widget.controller?.notifyListeners(); - if (direction == CardSwiperDirection.left) { - widget.controller?.swipedLeft(); - } else if (direction == CardSwiperDirection.right) { - widget.controller?.swipedRight(); - } - } - - void _goBack() { - _swipeType = SwipeType.back; - _cardAnimation.animateBack(context); - } - - void _undo({bool animate = true}) { - if (_directionHistory.isEmpty) return; - if (_undoableIndex.previousState == null) return; - - final direction = _directionHistory.last; - final shouldCancelUndo = widget.onUndo?.call( - _currentIndex, - _undoableIndex.previousState!, - direction, - ) == - false; - - if (shouldCancelUndo) { - return; - } - - _undoableIndex.undo(); - _directionHistory.removeLast(); - _swipeType = SwipeType.undo; - widget.controller?.directionHistory.removeLast(); - widget.controller?.setIndex(_currentIndex ?? 0); - if (animate) { - _cardAnimation.animateUndo(context, direction); - } - } - - void _restart() { - int undoQuan = 4; - while (undoQuan > 0 && - _directionHistory.isNotEmpty && - _undoableIndex.previousState != null) { - _undo(animate: true); - _swipeType = SwipeType.none; - undoQuan--; - } - while ( - _directionHistory.isNotEmpty && _undoableIndex.previousState != null) { - _undo(animate: false); - _swipeType = SwipeType.none; - } - _swipeType = SwipeType.none; - print('RESTARTED'); - } - - int numberOfCardsOnScreen() { - if (widget.isLoop) { - return widget.numberOfCardsDisplayed; - } - if (_currentIndex == null) { - return 0; - } - - return math.min( - widget.numberOfCardsDisplayed, - widget.cardsCount - _currentIndex!, - ); - } - - int? getValidIndexOffset(int offset) { - if (_currentIndex == null) { - return null; - } - - final index = _currentIndex! + offset; - if (!widget.isLoop && !index.isBetween(0, widget.cardsCount - 1)) { - return null; - } - return index % widget.cardsCount; - } -} diff --git a/lib/features/card_swiper/src/card_swiper_controller.dart b/lib/features/card_swiper/src/card_swiper_controller.dart deleted file mode 100644 index 8b07964..0000000 --- a/lib/features/card_swiper/src/card_swiper_controller.dart +++ /dev/null @@ -1,80 +0,0 @@ -import 'dart:async'; -import 'dart:collection'; - -import 'package:flutter/foundation.dart'; - -import 'enums.dart'; - -/// A controller that can be used to trigger swipes on a CardSwiper widget. -class CardSwiperController extends ChangeNotifier { - CardSwiperState? state; - final StreamController _controller = StreamController.broadcast(); - final Queue<(CardSwiperDirection, String)> directionHistory = Queue(); - - int currentIndex = 0; - - CardSwiperController() {} - - Set get swipedLeftIds => directionHistory - .where((element) => element.$1 == CardSwiperDirection.left) - .map((e) => e.$2) - .toSet(); - - Set get swipedRightIds => directionHistory - .where((element) => element.$1 == CardSwiperDirection.right) - .map((e) => e.$2) - .toSet(); - - int get progress => directionHistory.length; - - void setIndex(int index) { - currentIndex = index; - _controller.add(index); - } - - @override - Future dispose() async { - super.dispose(); - } - - /// Swipe the card by changing the status of the controller - void swipe({CardSwiperState? direction}) { - state = direction ?? CardSwiperState.swipeLeft; - notifyListeners(); - } - - void swipedLeft() { - directionHistory.add((CardSwiperDirection.left, currentIndex.toString())); - // notifyListeners(); - } - - void swipedRight() { - directionHistory.add((CardSwiperDirection.right, currentIndex.toString())); - // notifyListeners(); - } - - /// Swipe the card to the top side by changing the status of the controller - void swipeTop() { - state = CardSwiperState.swipeTop; - notifyListeners(); - } - - /// Swipe the card to the bottom side by changing the status of the controller - void swipeBottom() { - state = CardSwiperState.swipeBottom; - notifyListeners(); - } - - // Undo the last swipe by changing the status of the controller - void undo() { - state = CardSwiperState.undo; - notifyListeners(); - } - - void restart() { - state = CardSwiperState.restart; - notifyListeners(); - } - - Stream get asStream => _controller.stream.distinct(); -} diff --git a/lib/features/card_swiper/src/enums.dart b/lib/features/card_swiper/src/enums.dart deleted file mode 100644 index daba98f..0000000 --- a/lib/features/card_swiper/src/enums.dart +++ /dev/null @@ -1,13 +0,0 @@ -enum CardSwiperState { - swipe, - swipeLeft, - swipeRight, - swipeTop, - swipeBottom, - undo, - restart, -} - -enum CardSwiperDirection { none, left, right, top, bottom } - -enum SwipeType { none, swipe, back, undo } diff --git a/lib/features/card_swiper/src/extensions.dart b/lib/features/card_swiper/src/extensions.dart deleted file mode 100644 index 9372911..0000000 --- a/lib/features/card_swiper/src/extensions.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:flutter/widgets.dart'; - -import 'enums.dart'; - -extension Range on num { - bool isBetween(num from, num to) { - return from <= this && this <= to; - } -} - -extension CardSwiperDirectionExtension on CardSwiperDirection { - Axis get axis { - switch (this) { - case CardSwiperDirection.left: - case CardSwiperDirection.right: - return Axis.horizontal; - case CardSwiperDirection.top: - case CardSwiperDirection.bottom: - return Axis.vertical; - case CardSwiperDirection.none: - throw Exception('Direction is none'); - } - } -} diff --git a/lib/features/card_swiper/src/typedefs.dart b/lib/features/card_swiper/src/typedefs.dart deleted file mode 100644 index 9ac326b..0000000 --- a/lib/features/card_swiper/src/typedefs.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; - -import 'enums.dart'; - -typedef CardSwiperOnSwipe = FutureOr Function( - int previousIndex, - int? currentIndex, - CardSwiperDirection direction, -); - -typedef CardSwiperOnSwipeUpdate = Function( - int? currentIndex, - CardSwiperDirection direction, -); - -typedef NullableCardBuilder = Widget? Function( - BuildContext context, - int index, - int horizontalOffsetPercentage, - int verticalOffsetPercentage, - double scale, -); - -typedef CardSwiperDirectionChange = Function( - CardSwiperDirection horizontalDirection, - CardSwiperDirection verticalDirection, -); - -typedef CardSwiperOnEnd = FutureOr Function(); - -typedef CardSwiperOnTapDisabled = FutureOr Function(); - -typedef CardSwiperOnUndo = bool Function( - int? previousIndex, - int currentIndex, - CardSwiperDirection direction, -); diff --git a/lib/features/card_swiper/src/undoable.dart b/lib/features/card_swiper/src/undoable.dart deleted file mode 100644 index f79f428..0000000 --- a/lib/features/card_swiper/src/undoable.dart +++ /dev/null @@ -1,21 +0,0 @@ -class Undoable { - Undoable(this._value, {Undoable? previousValue}) : _previous = previousValue; - - T _value; - Undoable? _previous; - - T get state => _value; - T? get previousState => _previous?.state; - - set state(T newValue) { - _previous = Undoable(_value, previousValue: _previous); - _value = newValue; - } - - void undo() { - if (_previous != null) { - _value = _previous!._value; - _previous = _previous?._previous; - } - } -} diff --git a/lib/features/deeplinks/deeplinks_manager.dart b/lib/features/deeplinks/deeplinks_manager.dart deleted file mode 100644 index 29df710..0000000 --- a/lib/features/deeplinks/deeplinks_manager.dart +++ /dev/null @@ -1,5 +0,0 @@ -abstract interface class DeeplinkHandler { - bool canHandle(Uri uri); - - void handle(Uri uri); -} diff --git a/lib/features/deeplinks/deeplinks_manager_impl.dart b/lib/features/deeplinks/deeplinks_manager_impl.dart deleted file mode 100644 index c25e46a..0000000 --- a/lib/features/deeplinks/deeplinks_manager_impl.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'dart:async'; -import 'dart:developer'; - -import 'package:app_links/app_links.dart'; -import 'package:mnemo_cards/features/analytics/analytics.dart'; - -import 'deeplinks_manager.dart'; - -class DeeplinkManager implements DeeplinkHandler { - final AppLinks _appLinks; - final List _handlers; - - StreamSubscription? _subscription; - - DeeplinkManager( - this._appLinks, - this._handlers, - ); - - Future init() async { - _subscription ??= _appLinks.uriLinkStream.listen(_handleDeeplink); - } - - Future dispose() async { - await _subscription?.cancel(); - _subscription = null; - } - - @override - bool canHandle(Uri uri) { - for (final handler in _handlers) { - if (handler.canHandle(uri)) { - return true; - } - } - return false; - } - - @override - void handle(Uri uri) => _handleDeeplink(uri); - - void _handleDeeplink(Uri uri) { - for (final handler in _handlers) { - if (handler.canHandle(uri)) { - return handler.handle(uri); - } - } - log('Deeplink not handled! ${uri}'); - Analytics.deeplinkError(uri); - } -} diff --git a/lib/features/packs/images_holder.dart b/lib/features/packs/images_holder.dart deleted file mode 100644 index bd08be0..0000000 --- a/lib/features/packs/images_holder.dart +++ /dev/null @@ -1,50 +0,0 @@ - -import 'package:flutter/cupertino.dart'; -import 'package:mnemo_cards/di/locator.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; - -class ImagesHolder { - Map _images = {}; - Map _packImages = {}; - String? packId; - DateTime? unlockTime; - - ImagesHolder(); - - void setPackImages(Map images, String packId) { - _packImages = Map.from(images); - this.packId = packId; - } - - void setImage(String key, MemoryImage image) { - _images[key] = image; - } - - void clear() { - _images.clear(); - _packImages.clear(); - packId = null; - } - - MemoryImage? get(String id) { - final memoryImage = getPackImage(id) ?? _images[id]; - - return memoryImage; - } - - MemoryImage? getPackImage(String id) { - final memoryImage = _packImages[id]; - return memoryImage; - } - - bool havePackImage(String id) => _packImages[id] != null; - bool haveImage(String id) => _images[id] != null || havePackImage(id); -} - -extension ImagesHolderStringExt on String { - MemoryImage? get memoryImage => locator.imagesHolder.get(this); -} - -extension ImagesHolderCardExt on GameCardDto { - MemoryImage? get memoryImage => locator.imagesHolder.get(id.toString()); -} diff --git a/lib/features/packs/loading_packs_holder.dart b/lib/features/packs/loading_packs_holder.dart deleted file mode 100644 index 0771580..0000000 --- a/lib/features/packs/loading_packs_holder.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'dart:async'; - -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:rxdart/rxdart.dart'; - -class LoadingPacksHolder { - final StreamController _streamController = - StreamController.broadcast(); - LoadingPacksState? _state; - - LoadingPacksHolder(); - - void addLoadingPack( - CardPackLoadingDto dto, - ) { - _state = LoadingPacksState({ - ...?_state?.packs.where((p) => p.id != dto.id), - dto, - }); - _streamController.add(_state); - } - - void removeLoadingPack( - String id, - ) { - _state = LoadingPacksState( - {...?_state?.packs.where((p) => p.id != id)}, - ); - _streamController.add(_state); - } - - void clear() { - _state = null; - _streamController.add(_state); - } - - LoadingPacksState? get state => _state; - - Stream get asStream => - _streamController.stream.startWith(_state); -} - -class LoadingPacksState { - final Set packs; - - LoadingPacksState( - this.packs, - ); -} diff --git a/lib/features/packs/pack_cache_manager.dart b/lib/features/packs/pack_cache_manager.dart deleted file mode 100644 index 7af5b0e..0000000 --- a/lib/features/packs/pack_cache_manager.dart +++ /dev/null @@ -1,183 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:developer'; -import 'dart:io'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/widgets.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; - -import 'package:path_provider/path_provider.dart'; -import 'package:shared_preferences/shared_preferences.dart'; - -import '../../di/locator.dart'; -import '../analytics/analytics.dart'; - -// persist -class PackCacheManager { - late final SharedPreferences _sharedPreferences; - - PackCacheManager(); - - Future init() async { - _sharedPreferences = await SharedPreferences.getInstance(); - } - - Future markPackForUpdate(String packId) async { - try { - final dir = await _packDirectory(packId); - final file = File('${dir.path}/pack.json'); - if (file.existsSync()) { - final json = (await file.readAsString()).decode(); - json['id'] = '0'; - await file.writeAsString(jsonEncode(json)); - } - } catch (e, s) { - log('Cant mark pack for update', error: e, stackTrace: s); - Analytics.crashlyticsError( - e: e, - s: s, - message: 'Cant mark pack for update', - ); - } - } - - Future loadPackFromCache( - String packId, { - bool withCardImages = true, - }) async { - log('Loading from cache ${packId}'); - try { - final packDir = await _packDirectory(packId); - final packFile = File('${packDir.path}/pack.json'); - if (!packFile.existsSync()) { - log('Pack file not exist'); - return throw Exception('Pack file not exist'); - } - var pack = packFile.readAsStringSync().decode(CardPackDto.fromJson); - if (withCardImages) { - Map images = {}; - log('Cards ${packId}: ${pack.cards.length}'); - if (pack.cards.isNotEmpty) { - log('Loading cards ${packId}'); - await Future.wait( - pack.cards.map((dto) async { - final file = File('${packDir.path}/images/${dto.id}'); - if (await file.exists()) { - try { - images[dto.id.toString()] = - MemoryImage(await file.readAsBytes()); - } catch (e) { - markPackForUpdate(packId); - throw Exception('Cant read file $packId $e'); - } - } else { - log('Cant load card ${dto.id} from $packId, skiping'); - } - }), - ); - } - log('Pack loaded ${packId}'); - locator.imagesHolder.setPackImages(images, packId); - } - return pack; - } catch (e, s) { - log('Cant load from cache ${packId} ${e} ${s}'); - deletePack(packId); - rethrow; - } - } - - Future> _updateSavedPacks() async { - if (kIsWeb) { - return []; - } - final packsDir = - Directory('${(await getApplicationDocumentsDirectory()).path}/packs'); - List savedPackIds = []; - if (packsDir.existsSync()) { - for (final packDir in packsDir.listSync()) { - if (File('${packDir.path}/pack.json').existsSync()) { - savedPackIds.add(packDir.path.split('/').last); - } - } - } - _sharedPreferences.setStringList('packs', savedPackIds); - return savedPackIds; - } - - Future> loadAllPacks() async { - final ids = await _updateSavedPacks(); - final List packs = []; - for (final id in ids) { - try { - final pack = await loadPackFromCache(id, withCardImages: false); - packs.add(pack); - } catch (e) { - log('Pack $id doesnt exist anymore'); - } - } - return packs; - } - - Future deletePack(String packId) async { - try { - final dir = await _packDirectory(packId); - dir.deleteSync(recursive: true); - } catch (e) { - log('Deletion failed $e'); - } - _updateSavedPacks(); - } - - Future loadPackFile(String packId, String path) async { - final bytes = - File('${(await _packDirectory(packId)).path}/$path').readAsBytesSync(); - return bytes; - } - - Future clearCache() async { - final saved = await _updateSavedPacks(); - for (final p in saved) { - await deletePack(p); - } - await _sharedPreferences.remove('packs'); - } - - Future savePack( - CardPackDto dto, { - Map>? images, - }) async { - log('Saving pack ${dto.id}'); - final dir = await _packDirectory(dto.id); - final json = dto.encode(); - log('Saving dto ${dto.id}'); - final file = File('${dir.path}/pack.json'); - await file.create(recursive: true); - await file.writeAsString(json); - - if (images != null) { - log('Saving card images ${dto.id}'); - final imageDir = Directory('${dir.path}/images'); - if (imageDir.existsSync()) { - imageDir.deleteSync(recursive: true); - } - for (final card in dto.cards) { - final bytes = images[card.id.toString()]; - if (bytes != null) { - File('${imageDir.path}/${card.id}') - ..createSync(recursive: true) - ..writeAsBytesSync(bytes); - } - } - } - - log('Saved ${dto.id}'); - } - - Future _packDirectory(String id) async => kIsWeb - ? Directory.current - : Directory( - '${(await getApplicationDocumentsDirectory()).path}/packs/$id', - ); -} diff --git a/lib/features/packs/pack_holder.dart b/lib/features/packs/pack_holder.dart deleted file mode 100644 index e790bf7..0000000 --- a/lib/features/packs/pack_holder.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'dart:async'; - -import 'package:rxdart/rxdart.dart'; - -class PackHolder { - final StreamController _streamController = - StreamController.broadcast(); - PackState _state; - - PackHolder() : _state = PackState.empty; - - void setPacksWithCards( - Iterable packs, - ) { - _state = PackState(packs.toSet()); - _streamController.add(_state); - } - - void removePackWithCards(String id) => - setPacksWithCards(state.packsWithCards.toList()..remove(id)); - - void clear() { - _state = PackState.empty; - _streamController.add(_state); - } - - PackState get state => _state; - - Stream get asStream => _streamController.stream.startWith(_state); -} - -class PackState { - final Set packsWithCards; - - static const empty = PackState({}); - - const PackState( - this.packsWithCards, - ); -} diff --git a/lib/features/packs/pack_manager.dart b/lib/features/packs/pack_manager.dart deleted file mode 100644 index 503e70a..0000000 --- a/lib/features/packs/pack_manager.dart +++ /dev/null @@ -1,126 +0,0 @@ -import 'dart:async'; -import 'dart:developer'; - -import 'package:mnemo_cards/di/locator.dart'; -import 'package:mnemo_cards/features/packs/loading_packs_holder.dart'; -import 'package:mnemo_cards/features/packs/pack_cache_manager.dart'; -import 'package:mnemo_cards/features/packs/packs_api.dart'; -import 'package:mnemo_cards/features/packs/preview_pack_holder.dart'; -import 'package:mnemo_cards/managers/repository/repository.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:rxdart/rxdart.dart'; - -import 'pack_holder.dart'; - -class PackManager { - final Repository _repository; - final PacksApi _packsApi; - final PackHolder _packHolder; - final PreviewPackHolder _previewPackHolder; - final LoadingPacksHolder _loadingPacksHolder; - final PackCacheManager _cacheManager; - - PackManager( - this._repository, - this._packsApi, - this._packHolder, - this._previewPackHolder, - this._loadingPacksHolder, - this._cacheManager, - ); - - Future init() async { - final packs = await _cacheManager.loadAllPacks(); - _packHolder.setPacksWithCards(packs.map((p) => p.id).toList()); - - // _repository.userStream.map((u) => u.packs).distinct().listen((packs) async { - // final savedPacks = await savedPacksIds; - // for (final p in packs) { - // if (savedPacks.contains(p)) { - // final loaded = await loadPackFromCache(p); - // if (loaded.cards.isEmpty) { - // await downloadPackById(p); - // } - // } else { - // await downloadPackById(p); - // } - // } - // }); - } - - Stream getCardPackStream( - CardPackPreviewDto preview, - ) async* { - final loadingDto = CardPackLoadingDto.fromPreviewDto(preview); - final isAvailable = preview.isAvailable; - final id = preview.id; - - if (isAvailable) { - try { - yield await _cacheManager.loadPackFromCache(id); - } catch (err) { - yield loadingDto; - log('Cant load pack from cache, fetching pack', error: err); - try { - await locator.packUpdater.updatePackAndSaveImages(id); - yield await _cacheManager.loadPackFromCache(id); - } on Object catch (e, s) { - log('Cant fetch pack', error: e, stackTrace: s); - } - } - } else { - yield loadingDto; - final buyDto = await _packsApi.getPackBuy(id).catchError((_) => null); - if (buyDto != null) { - yield buyDto; - _packHolder.removePackWithCards(id); - } else { - log('Buy dto is null, fetching pack'); - try { - await locator.packUpdater.updatePackAndSaveImages(id); - yield await _cacheManager.loadPackFromCache(id); - } on Object catch (e, s) { - log('Cant fetch pack', error: e, stackTrace: s); - } - } - } - yield* Rx.combineLatest2( - _packHolder.asStream - .map( - (state) => - state.packsWithCards.firstWhereOrNull((packId) => packId == id), - ) - .asyncMap( - (packId) async => packId == null - ? null - : await _cacheManager.loadPackFromCache(packId), - ), - _loadingPacksHolder.asStream - .map((state) => state?.packs.firstWhereOrNull((p) => p.id == id)), - (dto, isLoadingResponse) => isLoadingResponse ?? dto, - ).whereNotNull(); - } - - Future clearCache() async { - _packHolder.clear(); - await _cacheManager.clearCache(); - } - - Stream?> get packsPreviewStream => - _previewPackHolder.asStream - .map((event) => event?.previewPacks) - .whereNotNull() - .distinct((prev, next) { - if (prev.length != next.length) return false; - for (int i = 0; i < prev.length; i++) { - if (prev[i] != next[i]) return false; - // if (prev[i].isAvailable != next[i].isAvailable) return false; - // if (prev[i].id != next[i].id) return false; - // if (prev[i].version != next[i].version) return false; - // if (prev[i].price != next[i].price) return false; - // if (prev[i].price != next[i].price) return false; - // if (prev[i].cards != next[i].cards) return false; - } - return true; - }); -} diff --git a/lib/features/packs/pack_updater.dart b/lib/features/packs/pack_updater.dart deleted file mode 100644 index 72f21fd..0000000 --- a/lib/features/packs/pack_updater.dart +++ /dev/null @@ -1,221 +0,0 @@ -import 'dart:async'; -import 'dart:developer'; -import 'dart:typed_data'; - -import 'package:archive/archive_io.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/widgets.dart'; -import 'package:mnemo_cards/features/packs/loading_packs_holder.dart'; -import 'package:mnemo_cards/features/packs/preview_packs_poller.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:package_info_plus/package_info_plus.dart'; -import 'package:rxdart/rxdart.dart'; -import '../analytics/analytics.dart'; -import 'pack_cache_manager.dart'; -import 'pack_holder.dart'; -import 'packs_api.dart'; -import 'preview_pack_holder.dart'; - -/// Updates cache and state holders for available packs -/// Does polling -class PackUpdater { - final PacksApi _packsApi; - final PackCacheManager _cacheManager; - final PackHolder _packHolder; - final PreviewPackHolder _previewPackHolder; - final LoadingPacksHolder _loadingPacksHolder; - final PreviewPacksPoller _previewPacksPoller; - - Completer? _updateCompleter; - - PackUpdater( - this._packsApi, - this._cacheManager, - this._packHolder, - this._previewPackHolder, - this._loadingPacksHolder, - this._previewPacksPoller, - ); - - Future init() async { - final pollStream = Stream.periodic(const Duration(seconds: 600)); - pollStream.startWith(0).listen((event) async { - await updateAvailablePacks(); - await _previewPacksPoller.poll(); - }); - } - - Future updateAvailablePacks() async { - if (_updateCompleter?.isCompleted ?? true) { - _updateCompleter = Completer(); - } - try { - print('Updating packs started'); - await _updateAvailablePacks(); - } catch (e, s) { - log('Error when updating packs', error: e, stackTrace: s); - print('Error when updating pack: ${e}'); - // Future.delayed(Duration(seconds: 5), () => updateAvailablePacks()); - } finally { - print('Updating packs complete'); - if (_updateCompleter?.isCompleted == false) { - _updateCompleter?.complete(); - } - } - return _updateCompleter!.future; - } - - Future _updateAvailablePacks() async { - final savedList = await _cacheManager.loadAllPacks(); - final packData = Map.fromEntries( - savedList.map( - (e) => MapEntry( - e.id.toString(), - e.version, - ), - ), - ); - final actions = await _packsApi.getPacksActions(packData); - Map updatedPacks = {}; - bool shouldUpdateHolder = false; - - for (final action in actions) { - switch (action.action) { - case PackAction.delete: - log('DELETE ${action.id}'); - shouldUpdateHolder = true; - await _cacheManager.deletePack(action.id); - savedList.removeWhere((p) => p.id == action.id); - break; - case PackAction.update: - log('UPDATE ${action.id}'); - shouldUpdateHolder = true; - try { - final updatedPack = await _updatePackAndSaveImages(action.id); - updatedPacks[action.id] = updatedPack; - } catch (e) { - log('Error while download cards for ${action.id} $e'); - } - break; - case PackAction.unknown: - log('Unknown action on update ${action.id}'); - break; - } - } - if (shouldUpdateHolder) { - List packs = []; - for (final saved in savedList) { - if (updatedPacks[saved.id] != null) { - packs.add(updatedPacks[saved.id]!); - updatedPacks.remove(saved.id); - } else { - packs.add(saved); - } - } - packs.addAll(updatedPacks.values); - _packHolder.setPacksWithCards(packs.map((p) => p.id).toList()); - } - } - - Future updatePackAndSaveImages( - String packId, { - bool shouldUpdateHolder = false, - }) { - _previewPacksPoller.poll(); - return _updatePackAndSaveImages( - packId, - shouldUpdateHolder: shouldUpdateHolder, - ); - } - - Future _updatePackAndSaveImages( - String packId, { - bool shouldUpdateHolder = false, - }) async { - final loadingDto = _previewPackHolder.state?.previewPacks - .firstWhereOrNull((p) => p.id == packId) - ?.let(CardPackLoadingDto.fromPreviewDto) ?? - CardPackLoadingDto.fromId(packId, message: 'Загружаем карточки...'); - try { - _loadingPacksHolder.addLoadingPack(loadingDto); - log('Updating with cards ${packId}', name: 'updatePackWithCards'); - final s = Stopwatch()..start(); - final packDto = await _packsApi.getPackDto(packId); - print('get from api ${s.elapsedMilliseconds} mills'); - if (packDto == null) { - throw Exception('Cant update pack $packId'); - } - final imagesData = await _packsApi.packImagesArchive(packId); - print('get cards from api ${s.elapsedMilliseconds} mills'); - _loadingPacksHolder.addLoadingPack( - loadingDto.copyWith(message: 'Распаковываем карточки...'), - ); - final appVersion = (await PackageInfo.fromPlatform()).version; - final images = await compute( - _decodeArchive, - _Envelope( - data: imagesData, - packId: packId, - appVersion: appVersion, - ), - ); - _loadingPacksHolder.addLoadingPack( - loadingDto.copyWith(message: 'Почти всё'), - ); - await _cacheManager.savePack(packDto, images: images); - print('saved pack ${s.elapsedMilliseconds} mills'); - log('Pack ${packId} updated with cards'); - if (shouldUpdateHolder) { - _packHolder.setPacksWithCards( - [ - ..._packHolder.state.packsWithCards, - packDto.id, - ], - ); - } - return packDto; - } catch (e, s) { - log('Cant update pack', error: e, stackTrace: s); - Analytics.crashlyticsError( - message: 'Cant update pack', - e: e, - s: s, - ); - rethrow; - } finally { - _loadingPacksHolder.removeLoadingPack(packId); - } - } -} - -Map> _decodeArchive(_Envelope envelope) { - final s = Stopwatch()..start(); - final password = TokenGenerator.generateArchivePassword( - appVersion: envelope.appVersion, - packId: envelope.packId, - ); - print('generate token ${s.elapsedMilliseconds} mills'); - final imagesArchive = ZipDecoder().decodeBytes( - envelope.data, - password: password, - ); - print('deflate archive ${s.elapsedMilliseconds} mills'); - final images = Map>(); - for (final file in imagesArchive) { - images[file.name] = file.content as List; - } - print('loaded images ${s.elapsedMilliseconds} mills'); - return images; -} - -class _Envelope { - final List data; - final String packId; - final String appVersion; - - _Envelope({ - required this.data, - required this.packId, - required this.appVersion, - }); -} diff --git a/lib/features/packs/packs_api.dart b/lib/features/packs/packs_api.dart deleted file mode 100644 index 9ece823..0000000 --- a/lib/features/packs/packs_api.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'dart:typed_data'; - -import 'package:dio/dio.dart'; -import 'package:mnemo_cards/features/worker/worker_ext.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; - -import '../../managers/repository/api.dart'; - -class PacksApi with Api { - final Dio _dio; - - PacksApi(this._dio); - - // previews for the main page - Future> getPacksPreviews( - Map? packData, - ) async { - final r = await _dio.get( - '$path/packs/previews', - queryParameters: packData, - ); - final previews = - (r.data! as String).isolatedDecodeList(CardPackPreviewDto.fromJson); - return previews; - } - - // previews for the main page - Future> getPacksActions( - Map? packData, - ) async { - final r = await _dio.get( - '$path/packs/actions', - queryParameters: packData, - ); - final actions = - (r.data! as String).isolatedDecodeList(CardPackAction.fromJson); - return actions; - } - - // pack dto for available packs or null - Future getPackDto(String id) async { - final r = await _dio.get( - '$path/pack/$id', - options: Options( - sendTimeout: Duration(seconds: 5), - receiveTimeout: Duration(seconds: 5), - ), - ); - return (r.data as String).isolatedDecode(CardPackDto.fromJson); - } - - // buy page dto if not available - Future getPackBuy(String id) async { - final r = await _dio.get( - '$path/pack/buy/$id', - options: Options( - sendTimeout: Duration(seconds: 5), - receiveTimeout: Duration(seconds: 5), - ), - ); - return (r.data as String).isolatedDecode(CardPackBuyDto.fromJson); - } - - /// cards zip archive for available pack - /// unavailable packs transfer images by base64 - Future packImagesArchive(String packId) async { - final response = await _dio.get( - '$path/pack/cards/$packId', - options: Options( - responseType: ResponseType.bytes, - ), - ); - final data = response.data; - if (data == null) { - return Uint8List.fromList([]); - } - return data; - } -} diff --git a/lib/features/packs/preview_pack_holder.dart b/lib/features/packs/preview_pack_holder.dart deleted file mode 100644 index c129212..0000000 --- a/lib/features/packs/preview_pack_holder.dart +++ /dev/null @@ -1,80 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:developer'; - -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; - -import 'package:rxdart/rxdart.dart'; - -import '../../main.dart'; - -class PreviewPackHolder { - final StreamController _streamController = - StreamController.broadcast(sync: true); - PreviewPackState? _state; - - PreviewPackHolder(); - - Future init() async { - if (_state == null) { - await loadFromCache(); - } - } - - Future loadFromCache() async { - final sp = globalSharedPreferences; - final previewPacksStrings = sp.getStringList('preview_packs'); - if (previewPacksStrings == null || previewPacksStrings.isEmpty) { - return; - } - final List previewPacks = []; - previewPacksStrings.forEach((string) { - try { - final pack = string.decode(CardPackPreviewDto.fromJson); - previewPacks.add(pack); - } catch (e, s) { - log('Cant load preview pack', error: e, stackTrace: s); - } - }); - setPreviewPacks(previewPacks); - } - - Future savePreviewPacks() async { - final packs = state?.previewPacks ?? []; - final stringsList = []; - packs.forEach((pack) { - try { - final string = jsonEncode(pack); - stringsList.add(string); - } catch (e, s) { - log('Cant load preview pack', error: e, stackTrace: s); - } - }); - final sp = globalSharedPreferences; - sp.setStringList('preview_packs', stringsList); - } - - void setPreviewPacks(List packs, {bool save = false}) { - _state = PreviewPackState(packs); - if (save) { - savePreviewPacks(); - } - _streamController.add(_state); - } - - void clear() { - _state = null; - _streamController.add(_state); - } - - PreviewPackState? get state => _state; - - Stream get asStream => - _streamController.stream.startWith(_state); -} - -class PreviewPackState { - final List previewPacks; - - PreviewPackState(this.previewPacks); -} diff --git a/lib/features/packs/preview_packs_poller.dart b/lib/features/packs/preview_packs_poller.dart deleted file mode 100644 index 79dcae1..0000000 --- a/lib/features/packs/preview_packs_poller.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'dart:developer'; - -import 'package:mnemo_cards/features/packs/packs_api.dart'; - -import 'preview_pack_holder.dart'; - -class PreviewPacksPoller { - PacksApi _api; - PreviewPackHolder _previewPackHolder; - bool polling = false; - - PreviewPacksPoller(this._api, this._previewPackHolder); - - Future init() async { - Stream.periodic(Duration(seconds: 600), (_) async { - poll(); - }); - } - - Future poll() async { - if (!polling) { - try { - polling = true; - final r = await _api.getPacksPreviews(null); - _previewPackHolder.setPreviewPacks(r, save: true); - } catch (e) { - log(e.toString()); - } - polling = false; - } - } -} diff --git a/lib/features/purchase/google_purchase_handler.dart b/lib/features/purchase/google_purchase_handler.dart deleted file mode 100644 index 9965212..0000000 --- a/lib/features/purchase/google_purchase_handler.dart +++ /dev/null @@ -1,206 +0,0 @@ -import 'dart:async'; -import 'dart:developer'; -import 'dart:io'; - -import 'package:in_app_purchase/in_app_purchase.dart'; -import 'package:in_app_purchase_android/billing_client_wrappers.dart'; -import 'package:in_app_purchase_android/in_app_purchase_android.dart'; -import 'package:mnemo_cards/features/analytics/analytics.dart'; -import 'package:mnemo_cards/managers/repository/repository.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; - -import 'purchase_handler.dart'; - -class GooglePurchaseHandler implements PurchaseHandler { - final InAppPurchase _inAppPurchase = InAppPurchase.instance; - final Repository _repository; - - StreamSubscription? _subscription; - - GooglePurchaseHandler(this._repository); - - Future initPurchase({ - required String id, - required MnemoCardsProductType productType, - required String paymentId, - String? source, - }) async { - if (productType == MnemoCardsProductType.unknown) { - log('unknown product type'); - return; - } - if (!await isAvailable) { - log('Google play payment not available'); - return; - } - final productId = paymentId.split('/').first; - final subscriptionId = paymentId.split('/').last; - final products = await _getStoreProduct(productId); - if (products.isEmpty) { - Analytics.buyError( - null, - paymentSystem, - paymentId: paymentId, - id: id, - message: 'No products', - ); - return; - } - - switch (productType) { - case MnemoCardsProductType.pack: - final product = products.first; - await _buyInStore(product); - return; - case MnemoCardsProductType.subscription: - // choosing the right subscription plan - final subs = products.first.productDetails.subscriptionOfferDetails; - final index = subs?.indexWhere((s) => s.basePlanId == subscriptionId); - if (index == null) { - Analytics.buyError( - null, - paymentSystem, - paymentId: paymentId, - id: id, - message: 'Unknown subscription id', - ); - return; - } - await _buyInStore(products[index]); - return; - case MnemoCardsProductType.discount: - return; - case MnemoCardsProductType.unknown: - Analytics.buyError( - null, - paymentSystem, - paymentId: paymentId, - id: id, - message: 'Unknown product type', - ); - return; - } - } - - Future> _getStoreProduct( - String googlePlayId) async { - final productDetailResponse = await _inAppPurchase.queryProductDetails({ - googlePlayId, - }); - - if (productDetailResponse.error != null || - productDetailResponse.productDetails.isEmpty) { - Analytics.buyError( - null, - paymentSystem, - paymentId: googlePlayId, - data: { - 'msg': 'cant get product details ${productDetailResponse.error}', - 'not_found': productDetailResponse.notFoundIDs.join(','), - }, - ); - return []; - } - - return productDetailResponse.productDetails.cast(); - } - - Future _buyInStore(ProductDetails product) async { - final PurchaseParam purchaseParam = PurchaseParam(productDetails: product); - product as GooglePlayProductDetails; - if (product.productDetails.productType == ProductType.inapp) { - final result = await InAppPurchase.instance.buyConsumable( - purchaseParam: purchaseParam, - ); - return result; - } else { - final result = InAppPurchase.instance.buyNonConsumable( - purchaseParam: purchaseParam, - ); - return result; - } - } - - Future completePurchase(PurchaseDetails purchaseDetails) async { - await InAppPurchase.instance.completePurchase(purchaseDetails); - } - - Future _onPurchased(PurchaseDetails purchaseDetails) async { - try { - final result = await checkPurchase( - product: MnemoCardsProduct( - id: purchaseDetails.productID, - type: MnemoCardsProductType.unknown, - ), - paymentToken: purchaseDetails.verificationData.serverVerificationData, - ); - if (result) { - Analytics.buyComplete( - null, - paymentSystem, - message: purchaseDetails.productID, - ); - log('Purchased'); - } else { - Analytics.buyError( - null, - PaymentSystem.yookassa, - message: 'payment not verified ${purchaseDetails.productID}', - ); - } - } catch (e, s) { - log(e.toString(), stackTrace: s); - } - } - - Future get isAvailable => _inAppPurchase.isAvailable(); - - @override - PaymentSystem get paymentSystem => PaymentSystem.google; - - @override - Future checkPurchase({ - required MnemoCardsProduct product, - required String paymentToken, - }) async { - final result = await _repository.checkPayment( - product: product, - key: paymentToken, - paymentSystem: paymentSystem, - ); - return result; - } - - Future init() async { - if (Platform.isAndroid) { - _subscription = _inAppPurchase.purchaseStream.listen( - (List events) { - Future.forEach( - events, - (PurchaseDetails purchaseDetails) async { - if (purchaseDetails.pendingCompletePurchase || - purchaseDetails.status == PurchaseStatus.purchased) { - Analytics.buyEvent( - null, - paymentSystem, - message: 'onPurchased', - data: { - 'status': purchaseDetails.status.name, - 'product': purchaseDetails.productID, - 'date': purchaseDetails.transactionDate, - }, - ); - _onPurchased(purchaseDetails); - } - }, - ); - }, - ); - } - } - - Future dispose() async { - await _subscription?.cancel(); - _subscription = null; - } -} diff --git a/lib/features/purchase/in_app_purchase.dart b/lib/features/purchase/in_app_purchase.dart deleted file mode 100644 index 9fa6ba2..0000000 --- a/lib/features/purchase/in_app_purchase.dart +++ /dev/null @@ -1,257 +0,0 @@ -import 'dart:async'; -import 'dart:developer'; -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:in_app_purchase/in_app_purchase.dart'; -import 'package:in_app_purchase_android/in_app_purchase_android.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:url_launcher/url_launcher_string.dart'; -import 'package:webview_flutter/webview_flutter.dart'; -import '../../di/injector.dart'; -import '../../di/locator.dart'; -import '../../flags.dart'; -import '../../main.dart'; -import '../analytics/analytics.dart'; -import 'rustore/mnemo_rustore.dart'; - -enum StoreItemType { - ads, - subscription, - pack, -} - -class InAppPurchaseService { - final InAppPurchase _inAppPurchase = InAppPurchase.instance; - final Stream> storeSubscription = - InAppPurchase.instance.purchaseStream; - - InAppPurchase get instance => _inAppPurchase; - - // Future buySubscription(SubscriptionPlanDto dto) async { - // if (dto.paymentSystem == PaymentSystem.yookassa) { - // return false; - // } - // - // final subscriptionProducts = await _getSubscriptionStoreProduct(dto); - // ProductDetails? product; - // if (Platform.isAndroid && !RUSTORE_BUILD) { - // // final googlePlayProducts = subscriptionProducts.whereType(); - // product = subscriptionProducts.firstWhereOrNull( - // (p) => p.rawPrice == double.parse(dto.price), - // ); - // } else { - // product = subscriptionProducts.first; - // } - // if (product == null) { - // Analytics.buySubscriptionError( - // dto, - // androidPaymentSystem, - // message: 'cant find plan with price ${dto.price}', - // data: { - // 'plans': subscriptionProducts.join(';'), - // }, - // ); - // return false; - // } - // final result = await _buySubscriptionInStore(product); - // return result; - // } - - Future buyPack(CardPackBuyDto dto) async { - try { - if (RUSTORE_BUILD) { - // return RustorePurchaseService().buyPack(dto); - return false; - } else { - final pack = await _getPackStoreProduct(dto); - final result = await _buyItemInStore(pack.first); - return result; - } - } catch (e, s) { - log('err', error: e, stackTrace: s); - Analytics.buyError( - dto, - androidPaymentSystem, - data: { - 'e': e.toString(), - 's': s.toString(), - }, - ); - return false; - } - } - - Future> _getPackStoreProduct(CardPackBuyDto dto) async { - final bool isAvailable = await _inAppPurchase.isAvailable(); - if (!isAvailable) { - Analytics.buyError( - dto, - androidPaymentSystem, - message: 'in app purchase not available', - ); - return []; - } - final ids = { - if (Platform.isAndroid) RUSTORE_BUILD ? dto.rustoreId : dto.googlePlayId, - if (Platform.isIOS) dto.appStoreId, - }.whereNotNull().toSet(); - if (ids.isEmpty) { - Analytics.buyError( - dto, - androidPaymentSystem, - message: 'product id not set', - ); - return []; - } - - final ProductDetailsResponse productDetailResponse = - await _inAppPurchase.queryProductDetails(ids); - - if (productDetailResponse.error != null || - productDetailResponse.productDetails.isEmpty) { - Analytics.buyError( - dto, - androidPaymentSystem, - data: { - 'msg': 'cant get product details ${productDetailResponse.error}', - 'not_found': productDetailResponse.notFoundIDs.join(','), - }, - ); - return []; - } - - return productDetailResponse.productDetails; - } - - // Future> _getSubscriptionStoreProduct( - // SubscriptionPlanDto dto, - // ) async { - // final bool isAvailable = await _inAppPurchase.isAvailable(); - // if (!isAvailable) { - // Analytics.buySubscriptionError( - // dto, - // androidPaymentSystem, - // message: 'in app purchase not available', - // ); - // return []; - // } - // final ids = { - // if (Platform.isAndroid) - // if (RUSTORE_BUILD && dto.paymentSystem == PaymentSystem.rustore) - // dto.paymentId, - // if (!RUSTORE_BUILD && dto.paymentSystem == PaymentSystem.google) - // dto.googlePlaySubscriptionId, - // if (dto.paymentSystem == PaymentSystem.yookassa) dto.paymentId, - // if (Platform.isIOS && dto.paymentSystem == PaymentSystem.apple) - // dto.paymentId, - // }.whereNotNull().toSet(); - // if (ids.isEmpty) { - // Analytics.buySubscriptionError( - // dto, - // androidPaymentSystem, - // message: 'product id not set', - // ); - // return []; - // } - // - // final productDetailResponse = await _inAppPurchase.queryProductDetails(ids); - // if (productDetailResponse.error != null || - // productDetailResponse.productDetails.isEmpty) { - // Analytics.buySubscriptionError( - // dto, - // androidPaymentSystem, - // data: { - // 'msg': 'cant get product details ${productDetailResponse.error}', - // 'not_found': productDetailResponse.notFoundIDs.join(','), - // }, - // ); - // return []; - // } - // - // return productDetailResponse.productDetails; - // } - - Future _buyItemInStore(ProductDetails product) async { - final PurchaseParam purchaseParam = PurchaseParam(productDetails: product); - return InAppPurchase.instance.buyConsumable(purchaseParam: purchaseParam); - } - - Future _buySubscriptionInStore(ProductDetails product) async { - final PurchaseParam purchaseParam = PurchaseParam(productDetails: product); - return InAppPurchase.instance - .buyNonConsumable(purchaseParam: purchaseParam); - } - - Future completePurchase(PurchaseDetails purchaseDetails) async { - await InAppPurchase.instance.completePurchase(purchaseDetails); - } -} - -class PurchaseDetailsStreamSubscription { - final InAppPurchaseService inAppPurchaseService = getIt.get(); - - Future onPurchased(PurchaseDetails purchaseDetails) async { - try { - Analytics.buyEvent( - null, - androidPaymentSystem, - message: 'onPurchased', - data: { - 'status': purchaseDetails.status.name, - 'product': purchaseDetails.productID, - 'date': purchaseDetails.transactionDate, - }, - ); - final result = await locator.repository.checkPayment( - product: MnemoCardsProduct( - id: purchaseDetails.productID, - type: MnemoCardsProductType.unknown, - ), - key: purchaseDetails.verificationData.serverVerificationData, - paymentSystem: androidPaymentSystem, - ); - if (result) { - Analytics.buyComplete( - null, - androidPaymentSystem, - message: purchaseDetails.productID, - ); - log('Purchased'); - } else { - Analytics.buyError( - null, - PaymentSystem.yookassa, - message: 'payment not verified ${purchaseDetails.productID}', - ); - throw Exception('Cant verify purchase'); - } - } catch (e, s) { - log(e.toString(), stackTrace: s); - } - } - - StreamSubscription>? _streamSubscription; - - PurchaseDetailsStreamSubscription(); - - Future init() async { - _streamSubscription = inAppPurchaseService.storeSubscription.listen( - (List events) { - Future.forEach( - events, - (PurchaseDetails purchaseDetails) async { - if (purchaseDetails.pendingCompletePurchase || - purchaseDetails.status == PurchaseStatus.purchased) { - onPurchased(purchaseDetails); - } - }, - ); - }, - ); - } - - void close() { - _streamSubscription?.cancel(); - } -} diff --git a/lib/features/purchase/purchase_deeplink_handler.dart b/lib/features/purchase/purchase_deeplink_handler.dart deleted file mode 100644 index 5ac0f96..0000000 --- a/lib/features/purchase/purchase_deeplink_handler.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:mnemo_cards/di/locator.dart'; -import 'package:mnemo_cards/domain/router/dialogs.dart'; -import 'package:mnemo_cards/features/analytics/analytics.dart'; -import 'package:mnemo_cards/features/deeplinks/deeplinks_manager.dart'; -import 'package:mnemo_cards/main.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; - -import 'purchase_manager.dart'; - -class PurchaseDeeplinkHandler implements DeeplinkHandler { - final PurchaseManager _purchaseManager; - - PurchaseDeeplinkHandler(this._purchaseManager); - - @override - bool canHandle(Uri uri) => uri.host == 'purchase'; - - @override - void handle(Uri uri) { - final path = uri.path.replaceFirst('/', ''); - switch (path) { - case PurchaseInitDeeplink.deeplinkPath: - return _initPurchase(uri); - case PurchaseCheckDeeplink.deeplinkPath: - return _checkPurchase(uri); - } - Analytics.buyDeeplinkError(uri, message: 'Unknown path: ${path}'); - } - - void _initPurchase(Uri uri) { - final model = PurchaseInitDeeplink.parse(uri); - _purchaseManager.initPurchase( - id: model.id, - productType: model.productType, - paymentId: model.paymentId, - paymentSystem: model.paymentSystem, - source: model.source?.notEmptyOrNull, - ); - } - - void _checkPurchase(Uri uri) async { - final model = PurchaseCheckDeeplink.parse(uri); - final success = await _purchaseManager.checkPurchase( - product: MnemoCardsProduct( - id: model.id, - type: model.productType, - ), - token: model.token, - paymentSystem: model.paymentSystem, - ); - if (success) { - if (model.productType == MnemoCardsProductType.pack) { - locator.packUpdater.updatePackAndSaveImages( - model.id, - shouldUpdateHolder: true, - ); - } else { - locator.packUpdater.updateAvailablePacks(); - locator.userManager.updateUser(); - showInfoDialog('Ура! Теперь у вас есть подписка!'); - } - } else { - // showErrorDialog('Не уда'); - } - } -} diff --git a/lib/features/purchase/purchase_handler.dart b/lib/features/purchase/purchase_handler.dart deleted file mode 100644 index 3770ffc..0000000 --- a/lib/features/purchase/purchase_handler.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; - -abstract interface class PurchaseHandler { - PaymentSystem get paymentSystem; - - Future initPurchase({ - required String id, - required MnemoCardsProductType productType, - required String paymentId, - String? source, - }); - - Future checkPurchase({ - required MnemoCardsProduct product, - required String paymentToken, - }); -} diff --git a/lib/features/purchase/purchase_manager.dart b/lib/features/purchase/purchase_manager.dart deleted file mode 100644 index 3e02592..0000000 --- a/lib/features/purchase/purchase_manager.dart +++ /dev/null @@ -1,110 +0,0 @@ -import 'package:mnemo_cards/features/analytics/analytics.dart'; -import 'package:mnemo_cards/features/purchase/yookassa_purchase_handler.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; - -import 'google_purchase_handler.dart'; - -class PurchaseManager { - final GooglePurchaseHandler _googlePurchaseHandler; - final YookassaPurchaseHandler _yookassaPurchaseHandler; - - PurchaseManager( - this._googlePurchaseHandler, - this._yookassaPurchaseHandler, - ); - - Future initPurchaseFromDto({ - required CardPackBuyDto dto, - required PaymentSystem paymentSystem, - String? source = 'dto', - }) async { - String? paymentId; - - switch (paymentSystem) { - case PaymentSystem.google: - paymentId = dto.googlePlayId; - break; - case PaymentSystem.apple: - paymentId = dto.appStoreId; - break; - case PaymentSystem.rustore: - paymentId = dto.rustoreId; - break; - case PaymentSystem.yookassa: - paymentId = dto.id; - break; - case PaymentSystem.unknown: - break; - } - if (paymentId == null) { - Analytics.buyError( - dto, - paymentSystem, - message: 'Cant find payment id', - ); - return; - } - - return initPurchase( - id: dto.id, - productType: MnemoCardsProductType.pack, - paymentId: paymentId, - paymentSystem: paymentSystem, - ); - } - - Future initPurchase({ - required String id, - required MnemoCardsProductType productType, - required String paymentId, - required PaymentSystem paymentSystem, - String? source, - }) async { - switch (paymentSystem) { - case PaymentSystem.google: - return _googlePurchaseHandler.initPurchase( - id: id, - productType: productType, - paymentId: paymentId, - source: source, - ); - case PaymentSystem.apple: - return; - case PaymentSystem.rustore: - return; - case PaymentSystem.yookassa: - return _yookassaPurchaseHandler.initPurchase( - id: id, - productType: productType, - paymentId: paymentId, - ); - case PaymentSystem.unknown: - return; - } - } - - Future checkPurchase({ - required MnemoCardsProduct product, - required String token, - required PaymentSystem paymentSystem, - }) async { - switch (paymentSystem) { - case PaymentSystem.google: - return _googlePurchaseHandler.checkPurchase( - product: product, - paymentToken: token, - ); - case PaymentSystem.apple: - return false; - case PaymentSystem.rustore: - return false; - case PaymentSystem.yookassa: - return _yookassaPurchaseHandler.checkPurchase( - product: product, - paymentToken: token, - ); - case PaymentSystem.unknown: - return false; - } - } -} diff --git a/lib/features/purchase/rustore/mnemo_rustore.dart b/lib/features/purchase/rustore/mnemo_rustore.dart deleted file mode 100644 index 4b43433..0000000 --- a/lib/features/purchase/rustore/mnemo_rustore.dart +++ /dev/null @@ -1,110 +0,0 @@ -// import 'dart:developer'; -// -// import 'package:flutter_rustore_billing/flutter_rustore_billing.dart'; -// import 'package:mnemo_cards/features/analytics/analytics.dart'; -// import 'package:mnemo_cards/flags.dart'; -// import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -// -// import '../../../di/locator.dart'; -// import '../../../domain/router/dialogs.dart'; -// -// class RustorePurchaseService { -// Future init() async { -// return RustoreBillingClient.initialize( -// "2063563896", -// "mnemocards://iamback", -// true, -// ).then((value) { -// print("initialize success: $value"); -// return true; -// }, onError: (err) { -// print("initialize err: $err"); -// return false; -// }); -// } -// -// Future buyPack(CardPackBuyDto dto) async { -// if (dto.rustoreId == null) { -// return false; -// } -// if (!await init()) { -// return false; -// } -// if (!await RustoreBillingClient.available().then((value) { -// print("available success $value"); -// return true; -// }, onError: (err) { -// print("available err: $err"); -// return false; -// })) { -// return false; -// } -// final product = (await RustoreBillingClient.products([dto.rustoreId])) -// .products -// .firstOrNull; -// if (product == null) { -// return false; -// } -// try { -// final result = await RustoreBillingClient.purchase(dto.rustoreId!, null); -// try { -// try { -// // creating payment to check it later -// // in case there will be an error after confirmation -// await locator.repository.checkPayment( -// itemId: result.successPurchase!.productId, -// key: result.successPurchase!.subscriptionToken!, -// paymentSystem: androidPaymentSystem, -// ); -// } catch (e, s) { -// Analytics.buyError( -// dto, -// PaymentSystem.rustore, -// message: 'Error when creating payment', -// ); -// log('Error when creating payment', error: e, stackTrace: s); -// showErrorDialog( -// 'Технические шоколадки...\n\nПопробуйте позже или обновите приложение до последней версии'); -// return false; -// } -// final p = await RustoreBillingClient.confirm( -// result.successPurchase!.subscriptionToken!, -// ); -// if (p.success) { -// final r = await locator.repository.checkPayment( -// itemId: result.successPurchase!.productId, -// key: result.successPurchase!.subscriptionToken!, -// paymentSystem: androidPaymentSystem, -// ); -// if (r) { -// return r; -// } else { -// Analytics.buyError( -// dto, -// PaymentSystem.rustore, -// message: 'Error when confirming payment', -// ); -// showErrorDialog( -// 'Технические шоколадки...\n\nДоступ откроется в течение 5 мин', -// ); -// } -// } -// } catch (e, s) { -// log('Error when confirming purchase', error: e, stackTrace: s); -// Analytics.crashlyticsError( -// e: e, -// s: s, -// message: 'Error when confirming rustore purchase', -// ); -// } -// } catch (e, s) { -// log('Error when processing purchase', error: e, stackTrace: s); -// Analytics.crashlyticsError( -// e: e, -// s: s, -// message: 'Error when processing rustore purchase', -// ); -// } -// return false; -// } -// } diff --git a/lib/features/purchase/yookassa_purchase_handler.dart b/lib/features/purchase/yookassa_purchase_handler.dart deleted file mode 100644 index fc51af0..0000000 --- a/lib/features/purchase/yookassa_purchase_handler.dart +++ /dev/null @@ -1,145 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:mnemo_cards/features/deeplinks/deeplinks_manager_impl.dart'; -import 'package:mnemo_cards/managers/repository/repository.dart'; -import 'package:mnemo_cards_common/src/dtos/payment/payment_dto.dart'; - -import 'package:mnemo_cards_common/src/dtos/payment/product_type.dart'; -import 'package:url_launcher/url_launcher_string.dart'; -import 'package:webview_flutter/webview_flutter.dart'; - -import '../../di/locator.dart'; -import '../../main.dart'; -import '../analytics/analytics.dart'; -import 'purchase_handler.dart'; - -class YookassaPurchaseHandler implements PurchaseHandler { - final Repository _repository; - - const YookassaPurchaseHandler(this._repository); - - @override - Future checkPurchase({ - required MnemoCardsProduct product, - required String paymentToken, - }) async { - return await _repository.checkPayment( - product: product, - key: paymentToken, - paymentSystem: paymentSystem, - ); - } - - @override - Future initPurchase({ - required String id, - required MnemoCardsProductType productType, - required String paymentId, - String? source, - }) async { - await _buyWithYooMoney(id, productType, scaffoldKey.currentContext); - } - - Future _buyWithYooMoney( - String id, - MnemoCardsProductType productType, - BuildContext? context, - ) async { - final dto = await _repository.createPayment( - id, - productType, - PaymentSystem.yookassa, - ); - if (dto == null) { - Analytics.buyError( - null, - id: id, - PaymentSystem.yookassa, - message: 'payment dto is null', - ); - return; - } - final purchaseUrl = dto.purchaseUrl; - final checkUrl = dto.checkUrl; - final completer = Completer(); - bool dialogClosed = false; - final realContext = (context ?? scaffoldKey.currentContext)!; - completer.future.then((_) { - if (!dialogClosed) { - return Navigator.of(realContext).pop(); - } - }); - final controller = WebViewController() - ..setJavaScriptMode(JavaScriptMode.unrestricted) - ..loadRequest(Uri.parse(purchaseUrl)) - ..setNavigationDelegate( - NavigationDelegate( - onNavigationRequest: (request) async { - final url = request.url; - if (url.startsWith('http')) { - return NavigationDecision.navigate; - } else if (url.startsWith('mnemocards')) { - locator.deeplinkManager.handle(Uri.parse(url)); - if (!completer.isCompleted) { - completer.complete(); - } - return NavigationDecision.prevent; - } else { - launchUrlString(url); - return NavigationDecision.prevent; - } - }, - ), - ); - final result = showModalBottomSheet( - context: realContext, - enableDrag: false, - showDragHandle: true, - useSafeArea: true, - isScrollControlled: true, - backgroundColor: Colors.white, - builder: (context) { - return Container( - child: WebViewWidget(controller: controller), - padding: EdgeInsets.only( - bottom: MediaQuery.of(context).viewInsets.bottom, - ), - ); - }, - ).then((_) async { - // final success = await checkPurchase( - // productId: id, - // productType: MnemoCardsProductType.pack, - // paymentToken: id, - // ); - return locator.deeplinkManager.handle(Uri.parse(checkUrl)); - - // if (success) { - // Analytics.buyComplete( - // null, - // id: id, - // PaymentSystem.yookassa, - // ); - // locator.packUpdater.updateAvailablePacks(); - // return true; - // } - // Analytics.buyError( - // null, - // id: id, - // PaymentSystem.yookassa, - // message: 'payment not verified', - // ); - // return false; - }); - await result.then((_) { - dialogClosed = true; - if (!completer.isCompleted) { - completer.complete(); - } - }); - } - - @override - PaymentSystem get paymentSystem => PaymentSystem.yookassa; -} diff --git a/lib/features/stories/stories_page.dart b/lib/features/stories/stories_page.dart deleted file mode 100644 index a21057d..0000000 --- a/lib/features/stories/stories_page.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:story/story.dart'; - -class StoriesPage extends StatelessWidget { - StoriesPage(); - - @override - Widget build(BuildContext context) { - final topPadding = MediaQuery.of(context).padding.top; - return Scaffold( - body: StoryPageView( - indicatorHeight: 4.0, - indicatorPadding: EdgeInsets.symmetric( - vertical: topPadding + 8.0, - horizontal: 8.0, - ), - onPageLimitReached: () { - Navigator.pop(context); - }, - gestureItemBuilder: (context, story, page) => Stack( - children: [ - Positioned( - top: topPadding + 24.0, - left: 12.0, - child: IconButton( - onPressed: () { - Navigator.pop(context); - }, - icon: Icon(Icons.clear), - ), - ), - ], - ), - itemBuilder: (c, pageIndex, storyIndex) { - return Container( - color: Colors.redAccent, - alignment: Alignment.center, - child: Text( - "Index of PageView: $pageIndex Index of story on each page: $storyIndex"), - ); - }, - storyLength: (pageIndex) { - return 1; - }, - pageLength: 2, - ), - ); - } -} diff --git a/lib/features/stories/stories_row.dart b/lib/features/stories/stories_row.dart deleted file mode 100644 index 630dd57..0000000 --- a/lib/features/stories/stories_row.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter/cupertino.dart'; - -import 'stories_widget.dart'; - -class StoriesRow extends StatelessWidget { - final double height; - - StoriesRow(this.height); - - @override - Widget build(BuildContext context) { - return Container( - height: height, - alignment: Alignment.center, - child: ListView( - scrollDirection: Axis.horizontal, - children: [1, 2, 3, 4] - .map((e) => Padding( - padding: const EdgeInsets.all(4.0), - child: StoriesWidget( - width: height - 8, - height: height - 8, - ), - )) - .toList(), - ), - ); - } -} diff --git a/lib/features/stories/stories_widget.dart b/lib/features/stories/stories_widget.dart deleted file mode 100644 index 14a392e..0000000 --- a/lib/features/stories/stories_widget.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:auto_route/auto_route.dart'; -import 'package:flutter/material.dart'; -import 'package:mnemo_cards/features/stories/stories_page.dart'; - -class StoriesWidget extends StatefulWidget { - final double width; - final double height; - - const StoriesWidget({required this.width, required this.height, super.key}); - - @override - State createState() => _StoriesWidgetState(); -} - -class _StoriesWidgetState extends State { - @override - Widget build(BuildContext context) { - return InkWell( - onTap: () { - AutoRouter.of(context).pushWidget(StoriesPage()); - }, - child: SizedBox( - width: widget.width, - height: widget.height, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(32.0), - border: Border.all( - color: Colors.blue, - width: 4.0, - ), - ), - child: Center( - child: Text('Story'), - ), - ), - ), - ); - } -} diff --git a/lib/features/subscription/subscription_api.dart b/lib/features/subscription/subscription_api.dart deleted file mode 100644 index 85ba167..0000000 --- a/lib/features/subscription/subscription_api.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'dart:typed_data'; - -import 'package:dio/dio.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; - -import '../../managers/repository/api.dart'; - -class SubscriptionApi with Api { - final Dio _dio; - - SubscriptionApi(this._dio); - - Future getBuySubscription() async { - final r = await _dio.get( - '$path/subscription/page', - options: Options( - sendTimeout: Duration(seconds: 5), - receiveTimeout: Duration(seconds: 5), - ), - ); - return (r.data as String).decode(SubscriptionDto.fromJson); - } - - /// cards zip archive for available pack - /// unavailable packs transfer images by base64 - Future packImagesArchive(String packId) async { - final response = await _dio.get( - '$path/pack/cards/$packId', - options: Options( - responseType: ResponseType.bytes, - ), - ); - final data = response.data; - if (data == null) { - return Uint8List.fromList([]); - } - return data; - } -} diff --git a/lib/features/subscription/subscription_deeplink_handler.dart b/lib/features/subscription/subscription_deeplink_handler.dart deleted file mode 100644 index d310a9f..0000000 --- a/lib/features/subscription/subscription_deeplink_handler.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'package:mnemo_cards/features/deeplinks/deeplinks_manager.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; - -import '../../di/locator.dart'; - -class SubscriptionDeeplinkHandler implements DeeplinkHandler { - @override - bool canHandle(Uri uri) => uri.host == 'subscription'; - - @override - void handle(Uri uri) => locator.subscriptionManager.openSubscriptionPage(); -} diff --git a/lib/features/subscription/subscription_manager.dart b/lib/features/subscription/subscription_manager.dart deleted file mode 100644 index 8a452b2..0000000 --- a/lib/features/subscription/subscription_manager.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:mnemo_cards/features/subscription/subscription_api.dart'; -import 'package:mnemo_cards/managers/user_state_holder.dart'; - -import '../../domain/router/app_router.dart'; -import '../../main.dart'; -import '../../managers/user_manager.dart'; -import 'subscription_page.dart'; - -class SubscriptionManager { - final UserManager _userManager; - final SubscriptionApi _subscriptionApi; - - SubscriptionManager( - this._userManager, - this._subscriptionApi, - ); - - Future openSubscriptionPage() async { - final user = _userManager.user; - if (user == null) { - AppRouter.openAuthOrProfile(); - return; - } - final buyDto = - await _subscriptionApi.getBuySubscription().catchError((_) => null); - if (buyDto != null) { - showDialog( - context: scaffoldKey.currentContext!, - builder: (_) => SubscriptionPage(buyDto.page!), - ); - } - } -} diff --git a/lib/features/subscription/subscription_page.dart b/lib/features/subscription/subscription_page.dart deleted file mode 100644 index a05058d..0000000 --- a/lib/features/subscription/subscription_page.dart +++ /dev/null @@ -1,187 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart'; -import 'package:url_launcher/url_launcher_string.dart'; -import 'package:mnemo_cards/utils/string_helper.dart'; - -import '../../theme/themes.dart'; - -class SubscriptionPage extends StatelessWidget { - final SubscriptionPageDto dto; - - SubscriptionPage(this.dto); - - @override - Widget build(BuildContext context) { - return Scaffold( - body: Column( - children: [ - Header(dto.title, hasPopButton: true, center: true), - Expanded( - child: Column( - // shrinkWrap: true, - // physics: PageScrollPhysics(), - children: [ - ...dto.items.build(), - ...dto.plans.map(_SubscriptionPlanWidget.new), - ], - ), - ), - BigBackButton(), - ], - ), - ); - } -} - -class _SubscriptionPlanWidget extends StatelessWidget { - final SubscriptionPlanDto plan; - - _SubscriptionPlanWidget(this.plan); - - @override - Widget build(BuildContext context) { - return Container( - padding: EdgeInsets.only(left: 10.0, right: 10.0), - height: (90 + (plan.tip == null ? 0 : 13)).h, - child: GestureDetector( - onTap: () => plan.deeplink?.let((d) => launchUrlString(d)), - child: Stack( - alignment: Alignment.topCenter, - children: [ - Padding( - padding: EdgeInsets.only(top: plan.tip == null ? 0 : 13.h), - child: Container( - height: 80.h, - decoration: BoxDecoration( - color: Theme.of(context).scaffoldBackgroundColor, - border: Border.all(color: borderGray), - borderRadius: BorderRadius.circular(12.0), - ), - padding: EdgeInsets.only( - left: 10.0.w, - right: 20.w, - top: 10.h, - bottom: 10.h, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded(child: _lead(plan)), - SizedBox( - width: 85.w, - child: _trail(plan), - ), - ], - ), - ), - ), - if (plan.tip != null) - Container( - height: 26.h, - width: 110.w, - alignment: Alignment.center, - decoration: BoxDecoration( - color: plan.tipColor?.asColor ?? progressBlue, - border: Border.all(color: borderGray), - borderRadius: BorderRadius.circular(16.0), - ), - padding: EdgeInsets.all(2.0.w), - child: Text( - plan.tip!, - style: TextStyle(color: white), - ), - ), - ], - ), - ), - ); - } - - Widget _lead(SubscriptionPlanDto plan) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text.rich( - TextSpan( - children: [ - if (plan.titleLead != null) - TextSpan( - text: plan.titleLead, - style: TextStyle(fontSize: 32), - ), - if (plan.title != null) - TextSpan( - text: ' ${plan.title}', - style: TextStyle( - fontSize: 16, - ), - ), - ], - style: TextStyle( - height: 0.85, - )), - ), - Text.rich( - TextSpan( - children: [ - if (plan.subtitleCrossed != null) - TextSpan( - text: plan.subtitleCrossed, - style: TextStyle( - fontSize: 20, - color: borderGray, - decoration: TextDecoration.lineThrough, - height: 0.85, - ), - ), - if (plan.subtitleCrossed != null && plan.subtitle != null) - TextSpan(text: ' '), - if (plan.subtitle != null) - TextSpan( - text: plan.subtitle, - style: TextStyle( - fontSize: 20.h, - height: 0.85, - ), - ), - ], - ), - ) - ], - ); - } - - Widget _trail(SubscriptionPlanDto plan) { - if (plan.pricePerMonth != null) - return Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text.rich( - TextSpan(children: [ - TextSpan( - text: plan.pricePerMonth!, - style: TextStyle(fontSize: 24), - ), - TextSpan( - text: ' ${plan.currency}', - style: TextStyle( - fontSize: 14, - ), - ), - ], style: TextStyle(height: 0.85)), - ), - Text( - 'в месяц', - style: TextStyle( - fontSize: 14, - ), - ), - ], - ); - return SizedBox.shrink(); - } -} diff --git a/lib/features/tests/progress_widget.dart b/lib/features/tests/progress_widget.dart deleted file mode 100644 index becefa0..0000000 --- a/lib/features/tests/progress_widget.dart +++ /dev/null @@ -1,213 +0,0 @@ - -import 'package:auto_route/auto_route.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; - - -class ProgressWidget extends StatefulWidget { - final Color color; - final List? results; - final int length; - final Stream? timer; - final bool showMic; - final bool showSound; - final String? popText; - final ValueGetter? popValue; - - @override - State createState() => _ProgressWidgetState(); - - factory ProgressWidget.value(int value, int length, Color color) => - ProgressWidget( - List.generate( - length, - (i) => i < value ? Result.correct : Result.not_visited, - ), - length, - color, - ); - - ProgressWidget( - this.results, - this.length, - this.color, { - this.timer, - this.popValue, - this.showMic = false, - this.showSound = false, - this.popText, - super.key, - }); -} - -enum Result { - correct, - wrong, - skiped, - not_visited, -} - -class _ProgressWidgetState extends State { - List get results => widget.results!; - - int get length => widget.length; - - @override - Widget build(BuildContext context) { - return Row( - children: [ - TapRegion( - onTapInside: AutoRouter.of(context).maybePop, - behavior: HitTestBehavior.opaque, - child: Container( - height: 30.h, - child: Row( - children: [ - Image.asset( - 'icons/back.png', - color: Theme.of(context).colorScheme.primary, - width: 17, - height: 23.h, - ), - if (widget.popText != null) - Padding( - padding: const EdgeInsets.only(left: 4.0), - child: Text( - widget.popText!, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w700, - ), - ), - ), - ], - ), - ), - ), - Expanded( - child: widget.results == null - ? SizedBox.shrink() - : Padding( - padding: EdgeInsets.only(left: 10.0.w), - child: ClipRRect( - borderRadius: BorderRadius.circular(16.h), - child: Container( - height: 16.h, - color: Theme.of(context).scaffoldBackgroundColor, - child: Stack( - alignment: Alignment.center, - children: [ - Row( - children: [ - Expanded( - child: AnimatedFractionallySizedBox( - alignment: Alignment.centerLeft, - widthFactor: length == 0 - ? 0 - : (results - .where((r) => - r != Result.not_visited) - .length) / - length, - duration: Duration(milliseconds: 300), - child: Container( - color: widget.color, - ), - ), - ), - ], - ), - ], - ), - ), - ), - ), - ), - if (widget.showMic) - Padding( - padding: EdgeInsets.symmetric(horizontal: 10.0.w), - child: Image.asset( - 'icons/mic_on.png', - height: 18.h, - width: 13.w, - color: Theme.of(context).colorScheme.primary, - ), - ), - if (widget.showSound) - Padding( - padding: EdgeInsets.symmetric(horizontal: 10.0.w), - child: Image.asset( - 'icons/sound_on.png', - height: 18.h, - width: 24.w, - color: Theme.of(context).colorScheme.primary, - ), - ), - if (widget.timer != null) - Padding( - padding: EdgeInsets.only(left: 10.0.w), - child: _TimeWidget(widget.timer), - ), - ], - ); - } - - Widget _text() { - return Text( - results.where((r) => r != Result.skiped).length < length - ? '${results.where((r) => r != Result.skiped).length + 1} из ${length}' - : '$length', - style: Theme.of(context).textTheme.titleLarge?.copyWith( - fontWeight: FontWeight.w800, - ), - ); - } - - _ProgressWidgetState(); -} - -class _TimeWidget extends StatelessWidget { - final Stream? timer; - - _TimeWidget(Stream? timer) : this.timer = timer; - - @override - Widget build(BuildContext context) { - return SizedBox( - width: 68.w, - child: StreamBuilder( - stream: timer, - builder: (context, snapshot) { - var text = '-:-'; - if (snapshot.hasData) { - final minutes = snapshot.data?.inMinutes ?? 0; - final seconds = (snapshot.data?.inSeconds ?? 0) - 60 * minutes; - text = - "${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}"; - } - return Row( - mainAxisAlignment: MainAxisAlignment.start, - mainAxisSize: MainAxisSize.max, - children: [ - Image.asset( - 'icons/clock.png', - width: 15.w, - height: 15.h, - color: Theme.of(context).colorScheme.primary - ), - SizedBox( - width: 4.0.w, - ), - Text( - text, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400, - ), - ) - ], - ); - }), - ); - } -} diff --git a/lib/features/tests/question_states/input_buttons_test_state.dart b/lib/features/tests/question_states/input_buttons_test_state.dart deleted file mode 100644 index 334c4d8..0000000 --- a/lib/features/tests/question_states/input_buttons_test_state.dart +++ /dev/null @@ -1,29 +0,0 @@ - -import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:copy_with_extension/copy_with_extension.dart'; -import 'package:mnemo_cards/features/tests/question_states/test_question_state.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; - -part 'input_buttons_test_state.g.dart'; - -@JsonSerializable() -@CopyWith() -class InputButtonsTestState extends TestQuestionState { - final List answer; - final List answerIds; - final bool isCorrect; - final bool isAnswered; - final bool isSkipped; - final int seed; - - InputButtonsTestState({ - required this.seed, - required super.word, - this.answer = const [], - this.answerIds = const [], - this.isCorrect = false, - this.isAnswered = false, - this.isSkipped = false, - super.testType = TestQuestionType.input_buttons, - }); -} diff --git a/lib/features/tests/question_states/input_buttons_test_state.g.dart b/lib/features/tests/question_states/input_buttons_test_state.g.dart deleted file mode 100644 index 3735531..0000000 --- a/lib/features/tests/question_states/input_buttons_test_state.g.dart +++ /dev/null @@ -1,185 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'input_buttons_test_state.dart'; - -// ************************************************************************** -// CopyWithGenerator -// ************************************************************************** - -abstract class _$InputButtonsTestStateCWProxy { - InputButtonsTestState seed(int seed); - - InputButtonsTestState word(String word); - - InputButtonsTestState answer(List answer); - - InputButtonsTestState answerIds(List answerIds); - - InputButtonsTestState isCorrect(bool isCorrect); - - InputButtonsTestState isAnswered(bool isAnswered); - - InputButtonsTestState isSkipped(bool isSkipped); - - InputButtonsTestState testType(TestQuestionType testType); - - /// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `InputButtonsTestState(...).copyWith.fieldName(...)` to override fields one at a time with nullification support. - /// - /// Usage - /// ```dart - /// InputButtonsTestState(...).copyWith(id: 12, name: "My name") - /// ```` - InputButtonsTestState call({ - int? seed, - String? word, - List? answer, - List? answerIds, - bool? isCorrect, - bool? isAnswered, - bool? isSkipped, - TestQuestionType? testType, - }); -} - -/// Proxy class for `copyWith` functionality. This is a callable class and can be used as follows: `instanceOfInputButtonsTestState.copyWith(...)`. Additionally contains functions for specific fields e.g. `instanceOfInputButtonsTestState.copyWith.fieldName(...)` -class _$InputButtonsTestStateCWProxyImpl - implements _$InputButtonsTestStateCWProxy { - const _$InputButtonsTestStateCWProxyImpl(this._value); - - final InputButtonsTestState _value; - - @override - InputButtonsTestState seed(int seed) => this(seed: seed); - - @override - InputButtonsTestState word(String word) => this(word: word); - - @override - InputButtonsTestState answer(List answer) => this(answer: answer); - - @override - InputButtonsTestState answerIds(List answerIds) => - this(answerIds: answerIds); - - @override - InputButtonsTestState isCorrect(bool isCorrect) => this(isCorrect: isCorrect); - - @override - InputButtonsTestState isAnswered(bool isAnswered) => - this(isAnswered: isAnswered); - - @override - InputButtonsTestState isSkipped(bool isSkipped) => this(isSkipped: isSkipped); - - @override - InputButtonsTestState testType(TestQuestionType testType) => - this(testType: testType); - - @override - - /// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `InputButtonsTestState(...).copyWith.fieldName(...)` to override fields one at a time with nullification support. - /// - /// Usage - /// ```dart - /// InputButtonsTestState(...).copyWith(id: 12, name: "My name") - /// ```` - InputButtonsTestState call({ - Object? seed = const $CopyWithPlaceholder(), - Object? word = const $CopyWithPlaceholder(), - Object? answer = const $CopyWithPlaceholder(), - Object? answerIds = const $CopyWithPlaceholder(), - Object? isCorrect = const $CopyWithPlaceholder(), - Object? isAnswered = const $CopyWithPlaceholder(), - Object? isSkipped = const $CopyWithPlaceholder(), - Object? testType = const $CopyWithPlaceholder(), - }) { - return InputButtonsTestState( - seed: seed == const $CopyWithPlaceholder() || seed == null - ? _value.seed - // ignore: cast_nullable_to_non_nullable - : seed as int, - word: word == const $CopyWithPlaceholder() || word == null - ? _value.word - // ignore: cast_nullable_to_non_nullable - : word as String, - answer: answer == const $CopyWithPlaceholder() || answer == null - ? _value.answer - // ignore: cast_nullable_to_non_nullable - : answer as List, - answerIds: answerIds == const $CopyWithPlaceholder() || answerIds == null - ? _value.answerIds - // ignore: cast_nullable_to_non_nullable - : answerIds as List, - isCorrect: isCorrect == const $CopyWithPlaceholder() || isCorrect == null - ? _value.isCorrect - // ignore: cast_nullable_to_non_nullable - : isCorrect as bool, - isAnswered: - isAnswered == const $CopyWithPlaceholder() || isAnswered == null - ? _value.isAnswered - // ignore: cast_nullable_to_non_nullable - : isAnswered as bool, - isSkipped: isSkipped == const $CopyWithPlaceholder() || isSkipped == null - ? _value.isSkipped - // ignore: cast_nullable_to_non_nullable - : isSkipped as bool, - testType: testType == const $CopyWithPlaceholder() || testType == null - ? _value.testType - // ignore: cast_nullable_to_non_nullable - : testType as TestQuestionType, - ); - } -} - -extension $InputButtonsTestStateCopyWith on InputButtonsTestState { - /// Returns a callable class that can be used as follows: `instanceOfInputButtonsTestState.copyWith(...)` or like so:`instanceOfInputButtonsTestState.copyWith.fieldName(...)`. - // ignore: library_private_types_in_public_api - _$InputButtonsTestStateCWProxy get copyWith => - _$InputButtonsTestStateCWProxyImpl(this); -} - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -InputButtonsTestState _$InputButtonsTestStateFromJson( - Map json) => - InputButtonsTestState( - seed: (json['seed'] as num).toInt(), - word: json['word'] as String, - answer: (json['answer'] as List?) - ?.map((e) => e as String) - .toList() ?? - const [], - answerIds: (json['answerIds'] as List?) - ?.map((e) => e as String) - .toList() ?? - const [], - isCorrect: json['isCorrect'] as bool? ?? false, - isAnswered: json['isAnswered'] as bool? ?? false, - isSkipped: json['isSkipped'] as bool? ?? false, - testType: - $enumDecodeNullable(_$TestQuestionTypeEnumMap, json['testType']) ?? - TestQuestionType.input_buttons, - ); - -Map _$InputButtonsTestStateToJson( - InputButtonsTestState instance) => - { - 'testType': _$TestQuestionTypeEnumMap[instance.testType]!, - 'word': instance.word, - 'answer': instance.answer, - 'answerIds': instance.answerIds, - 'isCorrect': instance.isCorrect, - 'isAnswered': instance.isAnswered, - 'isSkipped': instance.isSkipped, - 'seed': instance.seed, - }; - -const _$TestQuestionTypeEnumMap = { - TestQuestionType.simple: 'simple', - TestQuestionType.input_buttons: 'input_buttons', - TestQuestionType.matrix: 'matrix', - TestQuestionType.match: 'match', - TestQuestionType.undefined: 'undefined', -}; diff --git a/lib/features/tests/question_states/simple_test_state.dart b/lib/features/tests/question_states/simple_test_state.dart deleted file mode 100644 index 61b8ed5..0000000 --- a/lib/features/tests/question_states/simple_test_state.dart +++ /dev/null @@ -1,27 +0,0 @@ - -import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:copy_with_extension/copy_with_extension.dart'; -import 'package:mnemo_cards/features/tests/question_states/test_question_state.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; - -part 'simple_test_state.g.dart'; - -@JsonSerializable() -@CopyWith() -class SimpleTestState extends TestQuestionState { - final String answer; - final bool isCorrect; - final bool isAnswered; - final bool isSkipped; - final int seed; - - SimpleTestState({ - required this.seed, - required super.word, - this.answer = '', - this.isCorrect = false, - this.isAnswered = false, - this.isSkipped = false, - super.testType = TestQuestionType.simple, - }); -} diff --git a/lib/features/tests/question_states/simple_test_state.g.dart b/lib/features/tests/question_states/simple_test_state.g.dart deleted file mode 100644 index 6219d63..0000000 --- a/lib/features/tests/question_states/simple_test_state.g.dart +++ /dev/null @@ -1,160 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'simple_test_state.dart'; - -// ************************************************************************** -// CopyWithGenerator -// ************************************************************************** - -abstract class _$SimpleTestStateCWProxy { - SimpleTestState seed(int seed); - - SimpleTestState word(String word); - - SimpleTestState answer(String answer); - - SimpleTestState isCorrect(bool isCorrect); - - SimpleTestState isAnswered(bool isAnswered); - - SimpleTestState isSkipped(bool isSkipped); - - SimpleTestState testType(TestQuestionType testType); - - /// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `SimpleTestState(...).copyWith.fieldName(...)` to override fields one at a time with nullification support. - /// - /// Usage - /// ```dart - /// SimpleTestState(...).copyWith(id: 12, name: "My name") - /// ```` - SimpleTestState call({ - int? seed, - String? word, - String? answer, - bool? isCorrect, - bool? isAnswered, - bool? isSkipped, - TestQuestionType? testType, - }); -} - -/// Proxy class for `copyWith` functionality. This is a callable class and can be used as follows: `instanceOfSimpleTestState.copyWith(...)`. Additionally contains functions for specific fields e.g. `instanceOfSimpleTestState.copyWith.fieldName(...)` -class _$SimpleTestStateCWProxyImpl implements _$SimpleTestStateCWProxy { - const _$SimpleTestStateCWProxyImpl(this._value); - - final SimpleTestState _value; - - @override - SimpleTestState seed(int seed) => this(seed: seed); - - @override - SimpleTestState word(String word) => this(word: word); - - @override - SimpleTestState answer(String answer) => this(answer: answer); - - @override - SimpleTestState isCorrect(bool isCorrect) => this(isCorrect: isCorrect); - - @override - SimpleTestState isAnswered(bool isAnswered) => this(isAnswered: isAnswered); - - @override - SimpleTestState isSkipped(bool isSkipped) => this(isSkipped: isSkipped); - - @override - SimpleTestState testType(TestQuestionType testType) => - this(testType: testType); - - @override - - /// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `SimpleTestState(...).copyWith.fieldName(...)` to override fields one at a time with nullification support. - /// - /// Usage - /// ```dart - /// SimpleTestState(...).copyWith(id: 12, name: "My name") - /// ```` - SimpleTestState call({ - Object? seed = const $CopyWithPlaceholder(), - Object? word = const $CopyWithPlaceholder(), - Object? answer = const $CopyWithPlaceholder(), - Object? isCorrect = const $CopyWithPlaceholder(), - Object? isAnswered = const $CopyWithPlaceholder(), - Object? isSkipped = const $CopyWithPlaceholder(), - Object? testType = const $CopyWithPlaceholder(), - }) { - return SimpleTestState( - seed: seed == const $CopyWithPlaceholder() || seed == null - ? _value.seed - // ignore: cast_nullable_to_non_nullable - : seed as int, - word: word == const $CopyWithPlaceholder() || word == null - ? _value.word - // ignore: cast_nullable_to_non_nullable - : word as String, - answer: answer == const $CopyWithPlaceholder() || answer == null - ? _value.answer - // ignore: cast_nullable_to_non_nullable - : answer as String, - isCorrect: isCorrect == const $CopyWithPlaceholder() || isCorrect == null - ? _value.isCorrect - // ignore: cast_nullable_to_non_nullable - : isCorrect as bool, - isAnswered: - isAnswered == const $CopyWithPlaceholder() || isAnswered == null - ? _value.isAnswered - // ignore: cast_nullable_to_non_nullable - : isAnswered as bool, - isSkipped: isSkipped == const $CopyWithPlaceholder() || isSkipped == null - ? _value.isSkipped - // ignore: cast_nullable_to_non_nullable - : isSkipped as bool, - testType: testType == const $CopyWithPlaceholder() || testType == null - ? _value.testType - // ignore: cast_nullable_to_non_nullable - : testType as TestQuestionType, - ); - } -} - -extension $SimpleTestStateCopyWith on SimpleTestState { - /// Returns a callable class that can be used as follows: `instanceOfSimpleTestState.copyWith(...)` or like so:`instanceOfSimpleTestState.copyWith.fieldName(...)`. - // ignore: library_private_types_in_public_api - _$SimpleTestStateCWProxy get copyWith => _$SimpleTestStateCWProxyImpl(this); -} - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -SimpleTestState _$SimpleTestStateFromJson(Map json) => - SimpleTestState( - seed: (json['seed'] as num).toInt(), - word: json['word'] as String, - answer: json['answer'] as String? ?? '', - isCorrect: json['isCorrect'] as bool? ?? false, - isAnswered: json['isAnswered'] as bool? ?? false, - isSkipped: json['isSkipped'] as bool? ?? false, - testType: - $enumDecodeNullable(_$TestQuestionTypeEnumMap, json['testType']) ?? - TestQuestionType.simple, - ); - -Map _$SimpleTestStateToJson(SimpleTestState instance) => - { - 'testType': _$TestQuestionTypeEnumMap[instance.testType]!, - 'word': instance.word, - 'answer': instance.answer, - 'isCorrect': instance.isCorrect, - 'isAnswered': instance.isAnswered, - 'isSkipped': instance.isSkipped, - 'seed': instance.seed, - }; - -const _$TestQuestionTypeEnumMap = { - TestQuestionType.simple: 'simple', - TestQuestionType.input_buttons: 'input_buttons', - TestQuestionType.matrix: 'matrix', - TestQuestionType.match: 'match', - TestQuestionType.undefined: 'undefined', -}; diff --git a/lib/features/tests/question_states/test_question_state.dart b/lib/features/tests/question_states/test_question_state.dart deleted file mode 100644 index 647d975..0000000 --- a/lib/features/tests/question_states/test_question_state.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:copy_with_extension/copy_with_extension.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; - -part 'test_question_state.g.dart'; - -@CopyWith() -class TestQuestionState { - final TestQuestionType testType; - - bool get isCorrect => false; - - // isAnswered == not skipped by scrolling - final bool isAnswered; - final bool isSkipped; - final String word; - - const TestQuestionState({ - required this.testType, - required this.word, - this.isAnswered = false, - this.isSkipped = false, - }); -} diff --git a/lib/features/tests/question_states/test_question_state.g.dart b/lib/features/tests/question_states/test_question_state.g.dart deleted file mode 100644 index ca225d9..0000000 --- a/lib/features/tests/question_states/test_question_state.g.dart +++ /dev/null @@ -1,92 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'test_question_state.dart'; - -// ************************************************************************** -// CopyWithGenerator -// ************************************************************************** - -abstract class _$TestQuestionStateCWProxy { - TestQuestionState testType(TestQuestionType testType); - - TestQuestionState word(String word); - - TestQuestionState isAnswered(bool isAnswered); - - TestQuestionState isSkipped(bool isSkipped); - - /// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `TestQuestionState(...).copyWith.fieldName(...)` to override fields one at a time with nullification support. - /// - /// Usage - /// ```dart - /// TestQuestionState(...).copyWith(id: 12, name: "My name") - /// ```` - TestQuestionState call({ - TestQuestionType? testType, - String? word, - bool? isAnswered, - bool? isSkipped, - }); -} - -/// Proxy class for `copyWith` functionality. This is a callable class and can be used as follows: `instanceOfTestQuestionState.copyWith(...)`. Additionally contains functions for specific fields e.g. `instanceOfTestQuestionState.copyWith.fieldName(...)` -class _$TestQuestionStateCWProxyImpl implements _$TestQuestionStateCWProxy { - const _$TestQuestionStateCWProxyImpl(this._value); - - final TestQuestionState _value; - - @override - TestQuestionState testType(TestQuestionType testType) => - this(testType: testType); - - @override - TestQuestionState word(String word) => this(word: word); - - @override - TestQuestionState isAnswered(bool isAnswered) => this(isAnswered: isAnswered); - - @override - TestQuestionState isSkipped(bool isSkipped) => this(isSkipped: isSkipped); - - @override - - /// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `TestQuestionState(...).copyWith.fieldName(...)` to override fields one at a time with nullification support. - /// - /// Usage - /// ```dart - /// TestQuestionState(...).copyWith(id: 12, name: "My name") - /// ```` - TestQuestionState call({ - Object? testType = const $CopyWithPlaceholder(), - Object? word = const $CopyWithPlaceholder(), - Object? isAnswered = const $CopyWithPlaceholder(), - Object? isSkipped = const $CopyWithPlaceholder(), - }) { - return TestQuestionState( - testType: testType == const $CopyWithPlaceholder() || testType == null - ? _value.testType - // ignore: cast_nullable_to_non_nullable - : testType as TestQuestionType, - word: word == const $CopyWithPlaceholder() || word == null - ? _value.word - // ignore: cast_nullable_to_non_nullable - : word as String, - isAnswered: - isAnswered == const $CopyWithPlaceholder() || isAnswered == null - ? _value.isAnswered - // ignore: cast_nullable_to_non_nullable - : isAnswered as bool, - isSkipped: isSkipped == const $CopyWithPlaceholder() || isSkipped == null - ? _value.isSkipped - // ignore: cast_nullable_to_non_nullable - : isSkipped as bool, - ); - } -} - -extension $TestQuestionStateCopyWith on TestQuestionState { - /// Returns a callable class that can be used as follows: `instanceOfTestQuestionState.copyWith(...)` or like so:`instanceOfTestQuestionState.copyWith.fieldName(...)`. - // ignore: library_private_types_in_public_api - _$TestQuestionStateCWProxy get copyWith => - _$TestQuestionStateCWProxyImpl(this); -} diff --git a/lib/features/tests/scroll_physics.dart b/lib/features/tests/scroll_physics.dart deleted file mode 100644 index 831a957..0000000 --- a/lib/features/tests/scroll_physics.dart +++ /dev/null @@ -1,167 +0,0 @@ -import 'package:flutter/material.dart'; - -/// This [ScrollPhysics] allows you to block the movement in the horizontal axis. -/// You can block both right and left movements. -/// -/// {@tool sample} -/// -/// This sample shows a [HorizontalBlockedScrollPhysics] blocking left movement -/// -/// ```dart -/// HorizontalBlockedScrollPhysics(blockLeftMovent: true, blockRightMovement: false); -/// ``` -/// {@end-tool} -class HorizontalBlockedScrollPhysics extends ScrollPhysics { - /// If [true] it blocks the left movement. - final bool blockLeftMovement; - - /// If [true] it blocks the right movement. - final bool blockRightMovement; - - final ValueGetter? canScroll; - - const HorizontalBlockedScrollPhysics({ - ScrollPhysics? parent, - this.blockLeftMovement = false, - this.blockRightMovement = false, - this.canScroll, - }) : super(parent: parent); - - @override - HorizontalBlockedScrollPhysics applyTo(ScrollPhysics? ancestor) { - return HorizontalBlockedScrollPhysics( - parent: buildParent(ancestor), - blockLeftMovement: blockLeftMovement, - blockRightMovement: blockRightMovement, - ); - } - - @override - bool get allowUserScrolling => (canScroll == null || canScroll!.call()); - - @override - bool get allowImplicitScrolling => (canScroll == null || canScroll!.call()); - - @override - double applyBoundaryConditions(ScrollMetrics position, double value) { - assert(() { - if (value == position.pixels) { - throw FlutterError( - '$runtimeType.applyBoundaryConditions() was called redundantly.\n' - 'The proposed new position, $value, is exactly equal to the current position of the ' - 'given ${position.runtimeType}, ${position.pixels}.\n' - 'The applyBoundaryConditions method should only be called when the value is ' - 'going to actually change the pixels, otherwise it is redundant.\n' - 'The physics object in question was:\n' - ' $this\n' - 'The position object in question was:\n' - ' $position\n'); - } - return true; - }()); - - if (value < position.pixels && - position.pixels <= position.minScrollExtent) { - return value - position.pixels; - } - - if (position.maxScrollExtent <= position.pixels && - position.pixels < value) { - // overscroll - return value - position.pixels; - } - - if (value < position.minScrollExtent && - position.minScrollExtent < position.pixels) { - // hit top edge - return value - position.minScrollExtent; - } - - if (position.pixels < position.maxScrollExtent && - position.maxScrollExtent < value) { - // hit bottom edge - return value - position.maxScrollExtent; - } - - // If true, movement goes to the left. If it's a swipe, it goes to the left. - var isMovingLeft = value > position.pixels; - var screenIndex = (value / position.viewportDimension).floor(); - var pointInScreen = value - (screenIndex * position.viewportDimension); - // If true, the middle point of the screen is in the left side of the screen. - // This will be useful in order to not block some movements when in returning position. - var isPointInScreenLeftRange = - pointInScreen < (position.viewportDimension / 2); - var delta = value - position.pixels; - - // We're moving left and we want to block. - if (isMovingLeft && blockLeftMovement && isPointInScreenLeftRange) { - if (pointInScreen.abs() < delta.abs()) { - // fix for strong movements - return pointInScreen; - } - return delta; - } - - // We're moving right and we want to block. - if (!isMovingLeft && blockRightMovement && !isPointInScreenLeftRange) { - if (pointInScreen.abs() < delta.abs()) { - // fix for strong movements - return pointInScreen; - } - return delta; - } - - if (canScroll != null && !canScroll!.call()) { - return delta; - } - - return super.applyBoundaryConditions(position, value); - } -} - -/// This [ScrollPhysics] blocks the left movement in the horizontal axis allowing only movements to the right. -/// -/// {@tool sample} -/// -/// This sample shows a [LeftBlockedScrollPhysics] blocking left movement -/// -/// ```dart -/// LeftBlockedScrollPhysics(); -/// ``` -/// {@end-tool} -class LeftBlockedScrollPhysics extends HorizontalBlockedScrollPhysics { - const LeftBlockedScrollPhysics({ - ScrollPhysics? parent, - }) : super(parent: parent, blockLeftMovement: true); - - @override - LeftBlockedScrollPhysics applyTo(ScrollPhysics? ancestor) { - return LeftBlockedScrollPhysics( - parent: buildParent(ancestor), - ); - } -} - -// This [ScrollPhysics] blocks the right movement in the horizontal axis allowing only movements to the left. -/// -/// {@tool sample} -/// -/// This sample shows a [RightBlockedScrollPhysics] blocking right movement -/// -/// ```dart -/// RightBlockedScrollPhysics(); -/// ``` -/// {@end-tool} -class RightBlockedScrollPhysics extends HorizontalBlockedScrollPhysics { - const RightBlockedScrollPhysics({ - ScrollPhysics? parent, - ValueGetter? canScroll, - }) : super(parent: parent, canScroll: canScroll, blockRightMovement: true); - - @override - RightBlockedScrollPhysics applyTo(ScrollPhysics? ancestor) { - return RightBlockedScrollPhysics( - parent: buildParent(ancestor), - ); - } -} diff --git a/lib/features/tests/test_complete_widget.dart b/lib/features/tests/test_complete_widget.dart deleted file mode 100644 index 5516c99..0000000 --- a/lib/features/tests/test_complete_widget.dart +++ /dev/null @@ -1,206 +0,0 @@ -import 'dart:math'; - -import 'package:fl_chart/fl_chart.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:mnemo_cards/utils/color_helper.dart'; -import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart'; -import 'package:rxdart/rxdart.dart'; - -import '../../di/locator.dart'; -import 'question_states/test_question_state.dart'; - -class TestCompleteWidget extends StatelessWidget { - final Iterable states; - final Color successColor; - - static final _pieChartKey = GlobalKey(); - - TestCompleteWidget( - this.states, - this.successColor, - ); - - @override - Widget build(BuildContext context) { - final total = states.length; - final correct = states.where((e) => e.isCorrect).length; - final incorrect = states.where((e) => !e.isCorrect && e.isAnswered).length; - final skipped = states.where((e) => !e.isAnswered).length; - - final words = states - .fold( - Map(), - (map, state) => map - ..[state.word] = (map[state.word] ?? WordStat.zero(state.word)) - .inc(state.isCorrect), - ) - .values - .toList() - ..sort( - (p, n) => (p.correctCount.toDouble() / p.totalCount) - .compareTo(n.correctCount.toDouble() / n.totalCount), - ); - return PopScope( - onPopInvoked: (v) async { - if (v) { - await locator.testManager.sendStatistics(); - locator.testManager.triggerPoll(); - } - }, - child: Column( - children: [ - Expanded( - flex: 20, - child: Padding( - padding: const EdgeInsets.only(top: 12.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - width: MediaQuery.of(context).size.width / 1.8, - height: MediaQuery.of(context).size.width / 1.8, - padding: const EdgeInsets.all(20.0), - child: StreamBuilder( - stream: Stream.fromIterable([5]) - .interval(Duration(milliseconds: 0)), - initialData: 0, - builder: (context, snapshot) { - final a = min(snapshot.requireData / 5.0, 1.0); - return PieChart( - key: _pieChartKey, - swapAnimationDuration: Duration(milliseconds: 600), - PieChartData( - pieTouchData: PieTouchData(), - borderData: FlBorderData( - show: false, - ), - sectionsSpace: 0, - centerSpaceRadius: - MediaQuery.of(context).size.width / 8, - sections: [ - PieChartSectionData( - color: successColor, - showTitle: false, - value: correct.toDouble() * a, - badgeWidget: Image.asset( - 'icons/crown.png', - width: 20.w, - color: Theme.of(context).colorScheme.primary - ), - ), - PieChartSectionData( - color: successColor.lighten(0.15), - showTitle: false, - value: incorrect.toDouble() * a, - badgeWidget: Image.asset( - 'icons/skull.png', - width: 20.w, - color: Theme.of(context).colorScheme.primary - ), - ), - PieChartSectionData( - color: Theme.of(context).scaffoldBackgroundColor, - showTitle: false, - value: skipped.toDouble(), - badgeWidget: Image.asset( - 'icons/small_circle.png', - width: 20.w, - color: Theme.of(context).colorScheme.primary, - ), - ), - ], - ), - ); - }), - ), - Text( - '$correct/$total', - style: TextStyle( - fontSize: 36, - fontWeight: FontWeight.w700, - ), - ), - // Text( - // '1:47', - // style: TextStyle( - // fontSize: 20, - // fontWeight: FontWeight.w700, - // height: 0.85, - // ), - // ), - ], - ), - ), - ), - Expanded( - flex: 20, - child: Container( - alignment: Alignment.bottomCenter, - child: ListView.builder( - shrinkWrap: true, - itemCount: words.length, - itemBuilder: (context, index) { - final word = words[index]; - return Padding( - padding: const EdgeInsets.only( - left: 12.0, - right: 24, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - '${word.word}', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.w700, - ), - ), - Flexible( - child: SizedBox( - width: MediaQuery.of(context).size.width / 2.5, - child: HorizontalProgressWidget( - word.correctCount, - word.totalCount, - successColor, - ), - ), - ), - ], - ), - ); - }, - ), - ), - ), - const BigBackButton(), - ], - ), - ); - } -} - -class WordStat { - String word; - int correctCount; - int totalCount; - - WordStat(this.word, this.correctCount, this.totalCount); - - factory WordStat.zero(String word) => WordStat(word, 0, 0); - - WordStat inc(bool isCorrect) => isCorrect ? correct(this) : incorrect(this); - - WordStat correct(WordStat stat) => WordStat( - stat.word, - stat.correctCount + 1, - stat.totalCount + 1, - ); - - WordStat incorrect(WordStat stat) => WordStat( - stat.word, - stat.correctCount, - stat.totalCount + 1, - ); -} diff --git a/lib/features/tests/test_manager.dart b/lib/features/tests/test_manager.dart deleted file mode 100644 index 7f0c032..0000000 --- a/lib/features/tests/test_manager.dart +++ /dev/null @@ -1,207 +0,0 @@ -import 'dart:async'; -import 'dart:developer'; -import 'package:collection/collection.dart' show ListEquality; - -import 'package:flutter/widgets.dart'; -import 'package:mnemo_cards/features/tests/test_state_holder.dart'; -import 'package:mnemo_cards/features/tests/test_widgets/test_image.dart'; -import 'package:mnemo_cards/features/packs/pack_cache_manager.dart'; -import 'package:mnemo_cards/main.dart'; -import 'package:mnemo_cards/theme/themes.dart'; -import 'package:mnemo_cards/utils/string_helper.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:rxdart/rxdart.dart'; - -import '../../managers/repository/http_repository.dart'; -import 'question_states/test_question_state.dart'; - -class TestManager extends ChangeNotifier { - final PackCacheManager packCacheManager; - final HttpRepository _repository; - final PageController pageController = PageController(); - final TestHolder testHolder = TestHolder(); - final StreamController _triggerController = - StreamController.broadcast(); - - TestDto? _test; - - Color get color => _test?.color?.asColor ?? borderGray; - - List get questions => _test?.questions ?? []; - - void triggerPoll() => _triggerController.add(1); - - Future> pollTests({String? packId}) async { - print('polling tests'); - final tests = (await _repository.getTests(packId: packId ?? '')); - return tests; - } - - void skip(int id) { - testHolder.skip(id); - notifyListeners(); - } - - Stream> loadTestsStream({String? packId}) { - return Rx.combineLatest2( - _triggerController.stream.startWith(null), - Stream.periodic( - Duration(seconds: 120), - ).startWith(null), - (_, __) => null, - ) - .throttleTime(Duration(seconds: 1)) - .asyncMap( - (tests) async => pollTests(packId: packId) - .catchError((_) => Future.value([])), - ) - .where((test) => test.isNotEmpty) - .distinct((p, n) { - if (p.length == n.length) { - for (int i = 0; i < p.length; i++) { - if (p[i].id != n[i].id || - p[i].version != n[i].version || - !ListEquality().equals( - p[i].statistics?.words.words, - n[i].statistics?.words.words, - )) { - return false; - } - } - } - return false; - }); - } - - TestQuestionState? getState(int id) => testHolder.testQuestionState(id); - - Iterable get states => testHolder.state.questions.values; - - Future setActiveTest(String id, {String? packId}) async { - clearStates(); - if (packId != null) { - await packCacheManager.loadPackFromCache(packId); - } - try { - _test = await _repository.getTest(id); - } on Object catch (e, s) { - log('error when loading test ${id}', error: e, stackTrace: s); - } - if (_test == null) { - appRouter.maybePop(); - return; - } - // todo use uuid - testHolder.setQuestions( - _test!.questions, - DateTime.timestamp().toString(), - ); - } - - void setState(int id, TestQuestionState state) { - testHolder.setQuestionState(id, state); - log('Set state $id $state'); - notifyListeners(); - } - - TestManager( - this.packCacheManager, - this._repository, - ); - - void clearStates() { - testHolder.clear(); - notifyListeners(); - } - - AbstractTestQuestion getTest(int index) => questions[index]; - - void nextTest() { - pageController.animateToPage( - pageController.page!.toInt() + 1, - duration: Duration(milliseconds: 300), - curve: Curves.ease, - ); - } - - void prevTest() { - pageController.animateToPage( - pageController.page!.toInt() - 1, - duration: Duration(milliseconds: 300), - curve: Curves.ease, - ); - } - - Future preloadImages(BuildContext context) async { - for (final question in questions) { - switch (question.questionType) { - case TestQuestionType.simple: - final images = [ - (question as SimpleTestQuestionBody).image, - ...question.buttons.map((e) => e.image) - ].whereNotNull(); - for (final image in images) { - try { - final testImage = TestImage.image(image); - await precacheImage( - (await testImage.loadImage())!, - context, - ); - } catch (e) { - rethrow; - } - } - case TestQuestionType.input_buttons: - final images = [ - (question as InputButtonsTestQuestionBody).image, - ].whereNotNull(); - for (final image in images) { - try { - final testImage = TestImage.image(image); - await precacheImage( - (await testImage.loadImage())!, - context, - ); - } catch (e) { - rethrow; - } - } - case TestQuestionType.matrix: - // TODO: Handle this case. - case TestQuestionType.match: - // TODO: Handle this case. - case TestQuestionType.undefined: - // TODO: Handle this case. - } - } - } - - Future loadImage(String packId, String path) async { - final bytes = await packCacheManager.loadPackFile(packId, path); - return MemoryImage(bytes!); - } - - Future sendStatistics() { - final words = states - .fold({}, (stats, s) { - final existStat = stats[s.word] ?? WordStatisticsDto.empty(s.word); - stats[s.word] = existStat.copyWith( - correct: existStat.correct + (s.isCorrect ? 1 : 0), - incorrect: - existStat.incorrect + ((!s.isCorrect && s.isAnswered) ? 1 : 0), - skipped: existStat.skipped + (s.isAnswered ? 0 : 1), - questionTypes: {...existStat.questionTypes, s.testType}, - ); - return stats; - }) - .values - .toList(); - - final dto = TestStatisticsDto( - testId: int.tryParse(_test?.id ?? '') ?? -1, - words: AllWordsStatisticsDto(words: words), - sessionToken: testHolder.state.sessionToken, - ); - return _repository.addTestStatistics(dto); - } -} diff --git a/lib/features/tests/test_page.dart b/lib/features/tests/test_page.dart deleted file mode 100644 index 57c133e..0000000 --- a/lib/features/tests/test_page.dart +++ /dev/null @@ -1,193 +0,0 @@ - -import 'package:auto_route/auto_route.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:mnemo_cards/main.dart'; -import 'package:mnemo_cards/managers/audio_player.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:rxdart/rxdart.dart'; - -import '../../di/locator.dart'; -import 'scroll_physics.dart'; -import 'test_complete_widget.dart'; -import 'test_progress_widget.dart'; -import 'test_widgets/input_buttons_test.dart'; -import 'test_widgets/simple_test.dart'; - -@RoutePage() -class TestPage extends StatefulWidget { - final String testId; - - @override - State createState() => _TestPageState(); - - TestPage(this.testId); -} - -class _TestPageState extends State { - PageController get pageController => locator.testManager.pageController; - - int? lastSkipIndex; - int? _lastAudioQuestionId; - - @override - void initState() { - super.initState(); - locator.testManager.setActiveTest(widget.testId); - locator.testManager.preloadImages(context); - pageController.addListener(_pageScrollListener); - } - - @override - void dispose() { - pageController.removeListener(_pageScrollListener); - super.dispose(); - } - - void _pageScrollListener() { - if (!pageController.hasClients) { - return; - } - final index = pageController.page!.floor(); - print('Current index $index, lastAudio $_lastAudioQuestionId'); - - if (index < locator.testManager.questions.length) { - final question = locator.testManager.questions[index]; - if (question.id != _lastAudioQuestionId) { - print('AUDIO $_lastAudioQuestionId ${question.id}'); - _lastAudioQuestionId = question.id; - try { - if (globalSharedPreferences.getBool('auto_play_sound_tests') != - false) { - AudioPlayer.playAudio((question as dynamic).audio); - } - } catch (e) {} - } - } - - if (index == lastSkipIndex) { - return; - } - print('Will try to skip $index, ${locator.testManager.questions.length}'); - if (index > 0 && index <= locator.testManager.questions.length) { - final prevId = locator.testManager.questions[index - 1].id!; - final state = locator.testManager.getState(prevId); - if (state?.isAnswered == false) { - print('Skipping $index ${state?.word}'); - locator.testManager.skip(prevId); - lastSkipIndex = index; - } - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - body: StreamBuilder( - stream: locator.testManager.testHolder.asStream.takeWhileInclusive( - (state) => state?.questions.isNotEmpty != true, - ), - builder: (context, s) { - if (!s.hasData || s.requireData!.questions.isEmpty) { - return Scaffold( - body: Center( - child: Text('Загружаю тест...'), - ), - ); - } - - WidgetsBinding.instance.addPostFrameCallback((_) { - _pageScrollListener(); - }); - - final keys = List.generate( - locator.testManager.questions.length, - (v) => - GlobalKey(debugLabel: locator.testManager.questions[v].word), - ); - - return Scaffold( - backgroundColor: locator.testManager.color.withOpacity(0.25), - body: SafeArea( - child: Builder(builder: (context) { - return Column( - children: [ - SizedBox( - height: - (64.h - 20.h - MediaQuery.of(context).padding.top) / - 2, - ), - Padding( - padding: EdgeInsets.symmetric(horizontal: 10.0.w), - child: const TestProgressWidget(), - ), - Expanded( - child: PageView.builder( - physics: RightBlockedScrollPhysics( - canScroll: _canScroll, - ), - controller: locator.testManager.pageController, - itemBuilder: (context, index) { - if (index > locator.testManager.questions.length) { - return null; - } - if (index == locator.testManager.questions.length) { - return TestCompleteWidget( - locator.testManager.states, - locator.testManager.color, - ); - } - final question = locator.testManager.getTest(index); - return switch (question.questionType) { - TestQuestionType.simple => SimpleTestWidget( - question as SimpleTestQuestionBody, - key: keys[index], - ), - TestQuestionType.input_buttons => - InputButtonsTestWidget( - question as InputButtonsTestQuestionBody, - key: keys[index]), - TestQuestionType.matrix => Container(), - TestQuestionType.match => Container(), - // TODO: Handle this case. - TestQuestionType.undefined => - throw UnimplementedError(), - }; - }, - ), - ), - ], - ); - }), - ), - ); - }), - ); - } - - bool _canScroll() { - return locator.testManager.pageController.hasClients && - locator.testManager.pageController.page != - locator.testManager.questions.length; - } -} - -class _LifeWidget extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Row( - mainAxisAlignment: MainAxisAlignment.end, - mainAxisSize: MainAxisSize.max, - children: [ - Icon(Icons.ac_unit_outlined), - SizedBox( - width: 2, - ), - Text( - '30', - style: TextStyle(fontSize: 22), - ) - ], - ); - } -} diff --git a/lib/features/tests/test_progress_widget.dart b/lib/features/tests/test_progress_widget.dart deleted file mode 100644 index 9d28210..0000000 --- a/lib/features/tests/test_progress_widget.dart +++ /dev/null @@ -1,80 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; -import 'package:mnemo_cards/features/tests/progress_widget.dart'; - -import '../../di/locator.dart'; - -class TestProgressWidget extends StatefulWidget { - @override - State createState() => _TestProgressWidgetState(); - - const TestProgressWidget({super.key}); -} - -class _TestProgressWidgetState extends State { - List results = []; - int length = 0; - - Stream? timer; - - bool _active = false; - - void _updateDataListener() { - if (!_active) { - _active = true; - Timer(Duration(milliseconds: 100), () => _updateData()); - } - } - - void _updateData() { - results = [ - Result.correct, - ]; - length = locator.testManager.questions.length + 1; - final pageController = locator.testManager.pageController; - for (final state in locator.testManager.states) { - if (state.isAnswered) { - if (state.isCorrect) { - results.add(Result.correct); - } else { - // results.add(Result.wrong); - results.add(Result.wrong); - } - } else if (state.isSkipped) { - results.add(Result.skiped); - } else { - results.add(Result.not_visited); - } - } - print('Update data ${results.map((e) => e.name).join(' ')}'); - if (mounted) { - setState(() {}); - } - _active = false; - } - - @override - void initState() { - super.initState(); - locator.testManager.pageController.removeListener(_updateDataListener); - locator.testManager.pageController.addListener(_updateDataListener); - // timer = Stream.periodic( - // Duration(seconds: 1), - // (tick) => Duration(seconds: 300 - tick - 1), - // ).startWith(Duration(seconds: 300)).take(300); - _updateData(); - } - - @override - Widget build(BuildContext context) { - return ProgressWidget( - results, - length, - locator.testManager.color, - timer: timer, - ); - } -} diff --git a/lib/features/tests/test_state_holder.dart b/lib/features/tests/test_state_holder.dart deleted file mode 100644 index f76200d..0000000 --- a/lib/features/tests/test_state_holder.dart +++ /dev/null @@ -1,105 +0,0 @@ -import 'dart:async'; -import 'dart:math'; - -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:rxdart/rxdart.dart'; - -import 'question_states/input_buttons_test_state.dart'; -import 'question_states/simple_test_state.dart'; -import 'question_states/test_question_state.dart'; - -class TestHolder { - final StreamController<_TestState?> _streamController = - StreamController.broadcast(); - _TestState __state; - - TestHolder() : __state = _TestState({}, null); - - void skip(int id) { - final state = testQuestionState(id); - print('Skipping $id ${state} ${state?.testType}'); - switch (state?.testType) { - case TestQuestionType.simple: - state as SimpleTestState; - print('Skipped $id ${state.word}'); - setQuestionState(id, state.copyWith(isSkipped: true)); - break; - case TestQuestionType.input_buttons: - state as InputButtonsTestState; - print('Skipped $id ${state.word}'); - setQuestionState(id, state.copyWith(isSkipped: true)); - break; - case TestQuestionType.matrix: - // TODO: Handle this case. - break; - case TestQuestionType.match: - // TODO: Handle this case. - break; - case TestQuestionType.undefined: - // TODO: Handle this case. - case null: - // TODO: Handle this case. - break; - } - } - - void setQuestionState(int id, TestQuestionState state) { - _state = _TestState( - Map.from(_state.questions..[id] = state), - _state.sessionToken, - ); - } - - void clear() { - _state = _TestState({}, null); - _streamController.add(_state); - } - - void setQuestions( - List questions, String? sessionToken) { - final stateQuestions = {}; - final seed = Random().nextInt(999); - for (final question in questions) { - switch (question.questionType) { - case TestQuestionType.simple: - stateQuestions[question.id!] = SimpleTestState( - seed: seed, - word: question.word, - ); - case TestQuestionType.input_buttons: - stateQuestions[question.id!] = InputButtonsTestState( - seed: seed, - word: question.word, - ); - case TestQuestionType.matrix: - // TODO: Handle this case. - case TestQuestionType.match: - // TODO: Handle this case. - case TestQuestionType.undefined: - // TODO: Handle this case. - } - } - _state = _TestState(stateQuestions, sessionToken); - } - - TestQuestionState? testQuestionState(int id) => _state.questions[id]; - - _TestState get state => _state; - - _TestState get _state => __state; - - void set _state(_TestState state) { - __state = state; - _streamController.add(__state); - } - - Stream<_TestState?> get asStream => - _streamController.stream.startWith(_state); -} - -class _TestState { - final String? sessionToken; - final Map questions; - - _TestState(this.questions, this.sessionToken); -} diff --git a/lib/features/tests/test_widgets/common_test_question_widget.dart b/lib/features/tests/test_widgets/common_test_question_widget.dart deleted file mode 100644 index 161d0ec..0000000 --- a/lib/features/tests/test_widgets/common_test_question_widget.dart +++ /dev/null @@ -1,118 +0,0 @@ -import 'package:auto_size_text/auto_size_text.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:mnemo_cards/managers/audio_player.dart'; - -import '../../../widgets/game_card_widget.dart'; -import 'test_image.dart'; - -class CommonTestQuestionWidget extends StatelessWidget { - final String? text; - final String? image; - final String? audio; - - const CommonTestQuestionWidget({ - this.text, - this.image, - this.audio, - super.key, - }); - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context).textTheme; - - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (image != null) - Flexible( - child: Container( - alignment: Alignment.center, - margin: const EdgeInsets.all(4.0), - decoration: BoxDecoration( - borderRadius: - BorderRadius.circular(GameCardWidget.borderRadius), - ), - child: AspectRatio( - aspectRatio: text == null ? 1 : 1.5, - child: Stack( - alignment: Alignment.center, - children: [ - TestImageWidget( - TestImage.image(image!), - ), - if (audio != null && text == null) - Container( - padding: EdgeInsets.all(4.0.h), - alignment: Alignment.topRight, - child: IconButton( - icon: Image.asset( - 'icons/sound_on.png', - width: 26, - color: Theme.of(context).colorScheme.primary, - ), - onPressed: () => AudioPlayer.playAudio(audio), - ), - ), - ], - ), - ), - ), - ) - else if (text == null && audio != null) - Flexible( - child: Center( - child: IconButton( - icon: Image.asset( - 'icons/sound_on.png', - width: 80, - height: 80, - color: Theme.of(context).colorScheme.primary, - ), - onPressed: () => AudioPlayer.playAudio(audio), - ), - ), - ), - if (text != null && audio != null) - Container( - alignment: Alignment.center, - padding: const EdgeInsets.only(top: 8.0), - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - text!, - style: theme.headlineLarge, - ), - if (audio != null) - Container( - padding: EdgeInsets.only(left: 4.w, top: 4.h), - child: IconButton( - icon: Image.asset( - 'icons/sound_on.png', - width: 26, - color: Theme.of(context).colorScheme.primary, - height: theme.headlineLarge?.fontSize, - ), - onPressed: () => AudioPlayer.playAudio(audio), - ), - ) - ], - ), - ) - else if (text != null) - Container( - alignment: Alignment.center, - padding: const EdgeInsets.only(top: 8.0), - child: AutoSizeText( - text!, - style: theme.headlineLarge, - textAlign: TextAlign.center, - ), - ), - ], - ); - } -} diff --git a/lib/features/tests/test_widgets/input_buttons_test.dart b/lib/features/tests/test_widgets/input_buttons_test.dart deleted file mode 100644 index 8a36985..0000000 --- a/lib/features/tests/test_widgets/input_buttons_test.dart +++ /dev/null @@ -1,279 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; -import 'package:mnemo_cards/features/tests/test_manager.dart'; -import 'package:mnemo_cards/features/tests/test_widgets/common_test_question_widget.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart' - hide NullableIterableHelper; -import '../../../di/locator.dart'; -import '../question_states/input_buttons_test_state.dart'; -import 'test_button.dart'; - -class InputButtonsTestWidget extends StatelessWidget { - bool get isCorrect => state.answer.join() == correctAnswer; - final InputButtonsTestQuestionBody _inputButtonModel; - - InputButtonsTestQuestionBody get model => _inputButtonModel; - - InputButtonsTestState get state => - manager.getState(model.id!) as InputButtonsTestState; - - String get correctAnswer { - String correctAnswer = ''; - for (int i = 0; i < model.template.length; i++) { - if (model.template[i] == '_') { - correctAnswer += model.answer[i]; - } - } - return correctAnswer; - } - - TestManager get manager => locator.testManager; - - Color get testColor => manager.color; - - InputButtonsTestWidget(this._inputButtonModel, {super.key}); - - @override - Widget build(BuildContext context) { - final buttons = model.buttons; //..shuffle(random); - return Padding( - padding: const EdgeInsets.all(8.0), - child: LayoutBuilder(builder: (context, constraints) { - return Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - flex: 2, - child: CommonTestQuestionWidget( - text: model.text, - image: model.image, - audio: model.audio, - ), - ), - StatefulBuilder(builder: (context, setState) { - void buttonTapped(int index, TestButtonDto button) { - if (state.answer.fold(0, (p, n) => p + n.length) >= - correctAnswer.length) { - return; - } - setState(() { - var answer = [...state.answer, button.text!]; - manager.setState( - model.id!, - state.copyWith( - answer: answer, - answerIds: [...state.answerIds, button.id], - ), - ); - manager.setState( - model.id!, - state.copyWith( - isAnswered: isCorrect, - isCorrect: isCorrect, - ), - ); - }); - print('${state.answer} ${correctAnswer} ${state.isCorrect}'); - if (isCorrect) { - Future.delayed( - Duration(milliseconds: 300), - manager.nextTest, - ); - } - } - - int splitIndex = 0; - if (model.template.characters.where((c) => c == '_').length * 32 + - model.template.characters.where((c) => c != '_').length * - 18 > - constraints.maxWidth * 0.9) { - splitIndex = model.template.indexOf( - ' ', - (model.template.length / 2).ceil(), - ); - if (splitIndex < 10) { - splitIndex = model.template.indexOf( - ' ', - (model.template.length / 3).ceil(), - ); - } - if (splitIndex == -1) { - splitIndex = model.template.indexOf( - ' ', - 0, - ); - } - } - if (splitIndex < 0) { - splitIndex = 0; - } - - final stringRows = [ - if (splitIndex > 0) model.template.substring(0, splitIndex), - model.template.substring(splitIndex), - ]; - int stateAnswerIndex = 0; - final letters = - stringRows.map((row) => row.split(RegExp('(?=_)|(?<=_)')).map( - (letter) => letter == '_' - ? _Letter( - text: state.answer.length > stateAnswerIndex - ? state.answer[stateAnswerIndex++] - : ' ', - borderColor: testColor, - ) - : _Letter.readOnly( - text: letter, - ), - )); - final rows = [ - if (splitIndex > 0) - Padding( - padding: const EdgeInsets.only(bottom: 8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: letters.first.toList(), - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: letters.last.toList(), - ) - ]; - - return Expanded( - flex: 3, - child: Column( - children: [ - Expanded( - child: GestureDetector( - onTap: () { - var answer = state.answer.toList(); - var answerIds = state.answerIds.toList(); - if (answer.isNotEmpty) { - answer.removeLast(); - answerIds.removeLast(); - } - - setState(() { - manager.setState( - model.id!, - state.copyWith( - // isAnswered: false, - // isCorrect: false, - answer: answer, - answerIds: answerIds, - ), - ); - }); - }, - behavior: HitTestBehavior.opaque, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: rows, - ), - ), - ), - SizedBox( - height: 16.0, - ), - AbsorbPointer( - absorbing: state.isCorrect, - child: Wrap( - children: [ - ...buttons.mapIndexed( - (index, button) => Container( - margin: EdgeInsets.all(4.0), - width: button.text!.length > 3 - ? null - : constraints.maxWidth / 6, - height: 50, - child: state.answerIds.contains(button.id) - ? SizedBox.shrink() - : TestButton( - text: button.text, - onTap: () => buttonTapped(index, button), - color: state.answer == button - ? isCorrect - ? Colors.green.withOpacity(0.5) - : Colors.red.withOpacity(0.5) - : Theme.of(context) - .scaffoldBackgroundColor, - ), - ), - ), - ], - ), - ), - ], - ), - ); - }) - ], - ); - }), - ); - } -} - -class _Letter extends StatelessWidget { - final String text; - final Color? borderColor; - final Color? backgroundColor; - final double? _width; - final double _padding; - final double _innerPadding; - final double _innerMargin; - - _Letter({ - this.text = '', - this.borderColor, - this.backgroundColor, - }) : this._width = 32, - this._padding = 0, - this._innerPadding = 5, - this._innerMargin = 2.0; - - _Letter.readOnly({ - this.text = '', - }) : this.borderColor = null, - this.backgroundColor = Colors.transparent, - this._width = text == ' ' - ? 10 - : text.length == 1 - ? 20 - : null, - this._padding = 5.0, - this._innerPadding = 0.0, - this._innerMargin = 0.0; - - @override - Widget build(BuildContext context) { - return Container( - width: _width, - height: 45, - alignment: Alignment.center, - margin: EdgeInsets.all(_innerMargin), - padding: EdgeInsets.all(_innerPadding) - .copyWith(right: _padding + _innerPadding), - clipBehavior: Clip.antiAlias, - decoration: BoxDecoration( - color: backgroundColor ?? Theme.of(context).scaffoldBackgroundColor, - borderRadius: BorderRadius.circular(6.0), - border: borderColor != null ? Border.all(color: borderColor!) : null, - ), - child: Material( - color: backgroundColor ?? Theme.of(context).scaffoldBackgroundColor, - child: Text( - text, - style: Theme.of(context).textTheme.titleLarge?.copyWith( - letterSpacing: 1.1, - ), - textAlign: TextAlign.center, - ), - ), - ); - } -} diff --git a/lib/features/tests/test_widgets/simple_test.dart b/lib/features/tests/test_widgets/simple_test.dart deleted file mode 100644 index 94897ae..0000000 --- a/lib/features/tests/test_widgets/simple_test.dart +++ /dev/null @@ -1,134 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:mnemo_cards/features/tests/test_manager.dart'; -import 'package:mnemo_cards/features/tests/test_widgets/common_test_question_widget.dart'; -import 'package:mnemo_cards/features/tests/test_widgets/test_image.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; - -import '../../../di/locator.dart'; -import '../question_states/simple_test_state.dart'; -import 'test_button.dart'; - -class SimpleTestWidget extends StatelessWidget { - final SimpleTestQuestionBody _simpleTestModel; - - SimpleTestQuestionBody get model => _simpleTestModel; - - bool get isCorrect => state.answer.startsWith(model.answer); - - SimpleTestState get state => manager.getState(model.id!) as SimpleTestState; - - TestManager get manager => locator.testManager; - - Color get testColor => manager.color; - - const SimpleTestWidget(this._simpleTestModel, {super.key}); - - @override - Widget build(BuildContext context) { - final random = Random(state.seed); - final buttons = [...model.buttons]..shuffle(random); - - return LayoutBuilder(builder: (context, constraints) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: CommonTestQuestionWidget( - text: model.text, - image: model.image, - audio: model.audio, - ), - ), - SizedBox( - height: 16.0, - ), - StatefulBuilder(builder: (context, setState) { - void setAnswer(String answer) { - setState(() { - manager.setState( - model.id!, - state.copyWith( - answer: answer, - ), - ); - // only setting isCorrect once - manager.setState( - model.id!, - state.copyWith( - isAnswered: true, - isCorrect: state.isAnswered ? state.isCorrect : isCorrect, - ), - ); - }); - if (isCorrect) { - Future.delayed( - Duration(milliseconds: 300), - manager.nextTest, - ); - } - } - - bool buttonLine = (model.image != null && - buttons.where((element) => element.image != null).length != - 2); - final imageSide = buttonLine - ? constraints.maxWidth / 4 - 8 - : constraints.maxWidth / 2 - 16; - final spacing = buttonLine ? 4.0 : 8.0; - - return AbsorbPointer( - absorbing: state.isCorrect, - child: Wrap( - spacing: spacing, - runSpacing: 8.0, - children: buttons - .map( - (e) => e.isTextButton - ? Container( - width: constraints.maxWidth / 2 - 16, - height: 90.h, - child: TestButton( - text: e.text, - onTap: () { - setAnswer(e.id); - }, - // borderColor: testColor, - color: state.answer == e.id - ? isCorrect - ? Colors.green.withOpacity(0.5) - : Colors.red.withOpacity(0.5) - : Theme.of(context).scaffoldBackgroundColor, - ), - ) - : SizedBox( - width: imageSide, - height: imageSide, - child: TestButton( - image: TestImage.image(e.image!), - onTap: () { - setAnswer(e.id); - }, - // borderColor: testColor, - color: e.id == state.answer - ? isCorrect - ? Colors.green.withOpacity(0.5) - : Colors.red.withOpacity(0.5) - : Theme.of(context).scaffoldBackgroundColor, - ), - ), - ) - .toList(), - ), - ); - }) - ], - ), - ); - }); - } -} diff --git a/lib/features/tests/test_widgets/test_button.dart b/lib/features/tests/test_widgets/test_button.dart deleted file mode 100644 index cc69cb0..0000000 --- a/lib/features/tests/test_widgets/test_button.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'test_image.dart'; - -class TestButton extends StatelessWidget { - final String? text; - final TestImage? image; - final VoidCallback onTap; - final Color? color; - final Color? borderColor; - - TestButton({ - this.text, - this.image, - this.color, - this.borderColor, - required this.onTap, - }); - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context).textTheme; - return Material( - borderRadius: BorderRadius.circular(12.0), - child: Container( - padding: const EdgeInsets.all(4.0), - decoration: BoxDecoration( - color: color, - // border: Border.all(color: borderColor ?? borderGray), - borderRadius: BorderRadius.circular(12.0), - boxShadow: const [ - // BoxShadow( - // color: Colors.black26, - // blurRadius: 4.0, - // ), - ], - ), - child: GestureDetector( - // borderRadius: BorderRadius.circular(10.0), - onTap: onTap, - behavior: HitTestBehavior.opaque, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (image != null) - TestImageWidget( - image!, - key: ValueKey(image), - ), - if (image != null && text != null) - SizedBox( - height: 4.0, - ), - if (text != null) - Text( - text!, - textAlign: TextAlign.center, - style: theme.titleLarge, - ) - ], - ), - ), - ), - ); - } -} diff --git a/lib/features/tests/test_widgets/test_image.dart b/lib/features/tests/test_widgets/test_image.dart deleted file mode 100644 index 9e95f6d..0000000 --- a/lib/features/tests/test_widgets/test_image.dart +++ /dev/null @@ -1,115 +0,0 @@ -import 'dart:convert'; -import 'dart:developer'; -import 'dart:math' as m; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:mnemo_cards/features/packs/images_holder.dart'; - -import '../../../di/locator.dart'; - -class TestImageWidget extends StatefulWidget { - final TestImage _testImage; - - const TestImageWidget(this._testImage, {super.key}); - - @override - State createState() => _TestImageWidgetState(); -} - -class _TestImageWidgetState extends State { - TestImage get _testImage => widget._testImage; - bool same = false; - Widget? cached; - - @override - void didUpdateWidget(TestImageWidget oldWidget) { - super.didUpdateWidget(oldWidget); - same = oldWidget._testImage == _testImage; - } - - Future _preloadImage() async { - try { - // final cache = _TestImageCache(); - // final cachedImage = cache.image(_testImage); - // if (cachedImage != null) { - // return cachedImage; - // } - final imageProvider = await _testImage.loadImage(); - if (imageProvider == null) { - return SizedBox.shrink(); - } - // cache.setImage(_testImage, imageProvider); - return Image(image: imageProvider); - } catch (e) { - log('Cant load image ${_testImage}packId: $e'); - } - return SizedBox(); - } - - @override - Widget build(BuildContext context) { - // final cache = _TestImageCache(); - if (same && cached != null) { - return cached!; - } - return FutureBuilder( - future: _preloadImage(), - // initialData: cache.image(_testImage), - builder: (_, s) { - if (s.data == null) { - return Container( - alignment: Alignment.center, - ); - } - cached = Container( - clipBehavior: Clip.antiAlias, - decoration: BoxDecoration( - color: Theme.of(context).scaffoldBackgroundColor, - borderRadius: BorderRadius.circular(12), - ), - child: s.data, - ); - return cached!; - }, - ); - } -} - -class TestImage { - final String data; - final String key; - - TestImage({ - required this.data, - required this.key, - }); - - @override - bool operator ==(Object other) { - return other is TestImage && key == other.key; - } - - Future loadImage() async { - if (data.length > 50) { - Uint8List? bytes; - try { - bytes = await compute(base64Decode, data); - return MemoryImage(bytes!); - } catch (e) { - log('Cant decode base64 test image'); - return null; - } - } else if (data.contains(':')) { - final packId = data.split(':').first; - final path = data.split(':').last; - return await locator.testManager.loadImage(packId, path); - } else { - return data.memoryImage; - } - } - - TestImage.image(String image) - : data = image, - key = image.substring(0, m.min(image.length, 50)); -} \ No newline at end of file diff --git a/lib/features/worker/worker_ext.dart b/lib/features/worker/worker_ext.dart deleted file mode 100644 index 7c2ea8c..0000000 --- a/lib/features/worker/worker_ext.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'dart:convert'; - -import 'package:flutter/foundation.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; - -extension JsonStringExt on String { - - Future isolatedDecode([T Function(Map)? fromJson]) => - compute( - (_) => this.decode(fromJson), - null, - debugLabel: 'decode: $T', - ); - - Future> isolatedDecodeList( - [T Function(Map)? fromJson]) => - compute( - (_) => this.decodeList(fromJson), - null, - debugLabel: 'decodeList: $T', - ); -} diff --git a/lib/features/yandex_ads/yandex_ads.dart b/lib/features/yandex_ads/yandex_ads.dart deleted file mode 100644 index 1a0981f..0000000 --- a/lib/features/yandex_ads/yandex_ads.dart +++ /dev/null @@ -1,128 +0,0 @@ -import 'dart:async'; -import 'dart:developer'; - -import 'package:auto_route/auto_route.dart'; -import 'package:flutter/widgets.dart'; -import 'package:mnemo_cards/domain/router/dialogs.dart'; -import 'package:yandex_mobileads/mobile_ads.dart'; - -import '../../main.dart'; - -class YandexAds { - static BannerAd createBanner( - BoxConstraints constraints, { - required String id, - String? key, - VoidCallback? onLoaded, - }) { - return BannerAd( - adUnitId: id, - // or 'demo-banner-yandex' - adSize: BannerAdSize.inline( - width: constraints.maxWidth.round(), - maxHeight: constraints.maxHeight.round(), - ), - adRequest: const AdRequest(), - onAdLoaded: () { - log('Loaded $key'); - onLoaded?.call(); - }, - onAdFailedToLoad: (error) { - // Ad failed to load with AdRequestError. - // Attempting to load a new ad from the onAdFailedToLoad() method is strongly discouraged. - }, - onAdClicked: () { - // Called when a click is recorded for an ad. - }, - onLeftApplication: () { - // Called when user is about to leave application (e.g., to go to the browser), as a result of clicking on the ad. - }, - onReturnedToApplication: () { - // Called when user returned to application after click. - }, - onImpression: (impressionData) { - // Called when an impression is recorded for an ad. - }); - } - - static RewardedAd? _preloadedAd; - static Completer? _showingAdCompleter; - - static Future loadRewardedAd() async { - final completer = Completer(); - final _rewardedAdLoader = await RewardedAdLoader.create( - onAdLoaded: (RewardedAd rewardedAd) { - _preloadedAd = rewardedAd; - completer.complete(rewardedAd); - }, - onAdFailedToLoad: (error) { - completer.complete(null); - }, - ); - await _rewardedAdLoader.loadAd( - adRequestConfiguration: AdRequestConfiguration( - adUnitId: 'R-M-3761037-1', - ), - ); - return completer.future; - } - - static Future showRewardedAd( - Function(Reward reward) onRewarded, { - maxAttempts = 2, - }) async { - if (_showingAdCompleter?.isCompleted == false) { - return false; - } - try { - _showingAdCompleter = Completer(); - if (_preloadedAd == null) { - // final dialogCompleter = Completer(); - // final dialogFuture = - // showErrorDialog('Загрузка...', completer: dialogCompleter); - try { - final _loadedAd = await loadRewardedAd().timeout( - Duration(seconds: 5), - ); - if (_loadedAd == null) { - return false; - } - } finally {} - // dialogCompleter.complete(); - // await dialogFuture; - // final frameCompleter = Completer(); - // WidgetsBinding.instance.addPostFrameCallback((_) { - // frameCompleter.complete(); - // }); - // await frameCompleter; - } - final _ad = _preloadedAd; - _preloadedAd = null; - await _ad?.setAdEventListener( - eventListener: RewardedAdEventListener( - onAdDismissed: () { - _ad.destroy(); - }, - onAdFailedToShow: (e) { - _ad.destroy(); - if (maxAttempts-- > 0) { - showRewardedAd(onRewarded, maxAttempts: maxAttempts); - } - }, - onAdShown: () { - // _ad.destroy(); - }, - onRewarded: (r) { - onRewarded(r); - _ad.destroy(); - }, - ), - ); - await _ad?.show(); - final reward = await _ad?.waitForDismiss(); - return reward != null; - } finally { - _showingAdCompleter?.complete(); - } - } -} diff --git a/lib/firebase_options.dart b/lib/firebase_options.dart deleted file mode 100644 index f0ff4d2..0000000 --- a/lib/firebase_options.dart +++ /dev/null @@ -1,86 +0,0 @@ -// File generated by FlutterFire CLI. -// ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members -import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; -import 'package:flutter/foundation.dart' - show defaultTargetPlatform, kIsWeb, TargetPlatform; - -/// Default [FirebaseOptions] for use with your Firebase apps. -/// -/// Example: -/// ```dart -/// import 'firebase_options.dart'; -/// // ... -/// await Firebase.initializeApp( -/// options: DefaultFirebaseOptions.currentPlatform, -/// ); -/// ``` -class DefaultFirebaseOptions { - static FirebaseOptions get currentPlatform { - if (kIsWeb) { - return web; - } - switch (defaultTargetPlatform) { - case TargetPlatform.android: - return android; - case TargetPlatform.iOS: - return ios; - case TargetPlatform.macOS: - return macos; - case TargetPlatform.windows: - throw UnsupportedError( - 'DefaultFirebaseOptions have not been configured for windows - ' - 'you can reconfigure this by running the FlutterFire CLI again.', - ); - case TargetPlatform.linux: - throw UnsupportedError( - 'DefaultFirebaseOptions have not been configured for linux - ' - 'you can reconfigure this by running the FlutterFire CLI again.', - ); - default: - throw UnsupportedError( - 'DefaultFirebaseOptions are not supported for this platform.', - ); - } - } - - static const FirebaseOptions web = FirebaseOptions( - apiKey: 'AIzaSyCq0V8JebjtqSgGSueouoLiprBROgPRBQw', - appId: '1:701767851968:web:714eb4fb5e3fd1ab2f7225', - messagingSenderId: '701767851968', - projectId: 'mnemo-cards', - authDomain: 'mnemo-cards.firebaseapp.com', - storageBucket: 'mnemo-cards.appspot.com', - measurementId: 'G-C37MMWS6WC', - ); - - static const FirebaseOptions android = FirebaseOptions( - apiKey: 'AIzaSyBGn7PVVDX-o7WipivtuBjdoH5nYEPsHms', - appId: '1:701767851968:android:6190df55346394732f7225', - messagingSenderId: '701767851968', - projectId: 'mnemo-cards', - storageBucket: 'mnemo-cards.appspot.com', - ); - - static const FirebaseOptions ios = FirebaseOptions( - apiKey: 'AIzaSyAodY8s0ntALNeeGiUiTx6eg4g2Ar6C1to', - appId: '1:701767851968:ios:5c9040634eac8c152f7225', - messagingSenderId: '701767851968', - projectId: 'mnemo-cards', - storageBucket: 'mnemo-cards.appspot.com', - androidClientId: '701767851968-3jgootslus3ie76t682j4v7glletloud.apps.googleusercontent.com', - iosClientId: '701767851968-8dqcmk706p08gujqbl2m9s4sq1aljibs.apps.googleusercontent.com', - iosBundleId: 'com.cinnabarflower.mnemoCards', - ); - - static const FirebaseOptions macos = FirebaseOptions( - apiKey: 'AIzaSyAodY8s0ntALNeeGiUiTx6eg4g2Ar6C1to', - appId: '1:701767851968:ios:5c9040634eac8c152f7225', - messagingSenderId: '701767851968', - projectId: 'mnemo-cards', - storageBucket: 'mnemo-cards.appspot.com', - androidClientId: '701767851968-dvbvmte4m90dkqitp013s4nd5c0df1n8.apps.googleusercontent.com', - iosClientId: '701767851968-8dqcmk706p08gujqbl2m9s4sq1aljibs.apps.googleusercontent.com', - iosBundleId: 'com.cinnabarflower.mnemoCards', - ); - -} \ No newline at end of file diff --git a/lib/flags.dart b/lib/flags.dart deleted file mode 100644 index 18f0a14..0000000 --- a/lib/flags.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:mnemo_cards/admin/admin_api.dart'; -import 'package:mnemo_cards/di/injector.dart'; -import 'package:mnemo_cards/main.dart'; -import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart'; - -import 'di/locator.dart'; -import 'managers/user_manager.dart'; - -bool get ADMIN_BUILD => - kDebugMode || - (getIt.isRegistered() && - locator.userManager.user?.admin == true); -const RUSTORE_BUILD = CommonPrefs.RUSTORE_BUILD; - -final androidPaymentSystem = CommonPrefs.androidPaymentSystem; - -bool get SHOW_ADMIN => - getIt.isRegistered() && - ADMIN_BUILD && - (globalSharedPreferences.getBool('show_admin') ?? true); diff --git a/lib/main.dart b/lib/main.dart deleted file mode 100644 index e7d1d18..0000000 --- a/lib/main.dart +++ /dev/null @@ -1,308 +0,0 @@ -import 'dart:async'; -import 'dart:developer'; -import 'dart:io'; - -import 'package:auto_route/auto_route.dart'; -import 'package:device_info_plus/device_info_plus.dart'; -import 'package:firebase_core/firebase_core.dart'; -import 'package:firebase_crashlytics/firebase_crashlytics.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; -import 'package:jailbreak_root_detection/jailbreak_root_detection.dart'; -import 'package:mnemo_cards/di/locator.dart'; -import 'package:mnemo_cards/domain/router/app_router.dart'; -import 'package:mnemo_cards/managers/repository/firebase_config_repository.dart'; -import 'package:mnemo_cards/features/packs/pack_manager.dart'; -import 'package:mnemo_cards/theme/themes.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:no_screenshot/no_screenshot.dart'; -import 'package:shared_preferences/shared_preferences.dart'; -import 'package:yandex_mobileads/mobile_ads.dart'; - -import 'di/injector.dart'; -import 'domain/router/app_router.gr.dart'; -import 'firebase_options.dart'; -import 'flags.dart'; -import 'managers/repository/repository.dart'; - -final scaffoldKey = GlobalKey(); -final scaffoldMessengerKey = GlobalKey(); -late final SharedPreferences globalSharedPreferences; -late final FlutterSecureStorage secureStorage; - -final appRouter = AppRouter(ADMIN_BUILD); - -AndroidDeviceInfo? androidDeviceInfo; - -Future _screenOff() async => ADMIN_BUILD - ? await NoScreenshot.instance.screenshotOn() - : await NoScreenshot.instance.screenshotOff(); - -void main() async { - WidgetsFlutterBinding.ensureInitialized(); - final stopwatch = Stopwatch()..start(); - print('ensureInitialized ${stopwatch.elapsedMilliseconds}'); - SystemChrome.setPreferredOrientations([ - DeviceOrientation.portraitUp, - ]); - androidDeviceInfo = kIsWeb ? null : await DeviceInfoPlugin().androidInfo; - print('deviceInfo ${stopwatch.elapsedMilliseconds}'); - if (!kDebugMode) { - final isNotTrust = await JailbreakRootDetection.instance.isNotTrust; - print('No trusted ${stopwatch.elapsedMilliseconds}'); - if (isNotTrust && !ADMIN_BUILD) { - print('Not trusted'); - return; - } - final off = await _screenOff(); - print('screenshot off ${stopwatch.elapsedMilliseconds}'); - if (!off) { - print('Cant disable screenhsot'); - return; - } - } - - FlutterError.onError = (errorDetails) { - FirebaseCrashlytics.instance.recordFlutterFatalError(errorDetails); - }; - - try { - secureStorage = FlutterSecureStorage( - aOptions: const AndroidOptions( - encryptedSharedPreferences: true, - ), - ); - globalSharedPreferences = await SharedPreferences.getInstance(); - - await _fillSp(); - print('global sp ${stopwatch.elapsedMilliseconds}'); - await setInjections(); - print('injections ${stopwatch.elapsedMilliseconds}'); - unawaited(initFirebase()); - print('initing... ${stopwatch.elapsedMilliseconds}'); - await Future.wait([ - locator.packCacheManager - .init() - .then((_) => print('packCacheManager inited')), - locator.previewPackHolder - .init() - .then((_) => print('previewPackHolder inited')), - locator.previewPackPoller - .init() - .then((_) => print('previewPackPoller inited')), - locator.packManager.init().then((_) => print('packManager inited')), - locator.userManager.init().then((_) => print('userManager inited')), - locator.favoriteCardsController - .init() - .then((_) => print('favoriteCardsController inited')), - locator.previewPackPoller - .init() - .then((_) => print('previewPackPoller inited')), - locator.deeplinkManager - .init() - .then((_) => print('deeplinkManager inited')), - ]); - await locator.packUpdater.init().then((_) => print('packUpdater inited')); - print('initing complete ${stopwatch.elapsedMilliseconds}'); - if (!kIsWeb) unawaited(MobileAds.initialize()); - print('mobile ads ${stopwatch.elapsedMilliseconds}'); - print('runApp ${stopwatch.elapsedMilliseconds}'); - runApp(const MyApp()); - } on Object catch (e, s) { - print('err: ${e.toString()}'); - print('stacktrace: ${s.toString()}'); - log(e.toString(), stackTrace: s); - exit(0); - } -} - -Future _fillSp() async { - final showAdmin = globalSharedPreferences.getBool('show_admin'); - if (showAdmin == null && ADMIN_BUILD) { - await globalSharedPreferences.setBool('show_admin', true); - } - final soundOn = globalSharedPreferences.getBool('sound_on'); - if (soundOn == null) { - await globalSharedPreferences.setBool('sound_on', true); - } - final soundSpeed = globalSharedPreferences.getDouble('sound_speed'); - if (soundSpeed == null) { - await globalSharedPreferences.setDouble('sound_speed', 0.8); - } - final autoPlay = globalSharedPreferences.getBool('auto_play_sound_tests'); - if (autoPlay == null) { - await globalSharedPreferences.setBool('auto_play_sound_tests', true); - } - final autoPlayView = globalSharedPreferences.getBool('auto_play_sound_view'); - if (autoPlayView == null) { - await globalSharedPreferences.setBool('auto_play_sound_view', false); - } -} - -Future initFirebase() async { - try { - await Firebase.initializeApp( - options: DefaultFirebaseOptions.currentPlatform, - ); - await FirebaseRepository.update(); - log('Firebase inited'); - } on Object catch (e, s) { - log('Firebase not inited', error: e, stackTrace: s); - } -} - -class MyApp extends StatelessWidget with WidgetsBindingObserver { - const MyApp({super.key}); - - @override - Widget build(BuildContext context) { - WidgetsBinding.instance.addObserver(this); - return ScreenUtilInit( - designSize: const Size(370, 800), - minTextAdapt: true, - splitScreenMode: true, - child: ValueListenableBuilder( - builder: (context, themeMode, child) { - return MaterialApp.router( - theme: theme2, - darkTheme: darkTheme2, - themeMode: themeMode, - routerConfig: appRouter.config( - deepLinkBuilder: (link) { - return DeepLink.defaultPath; - }, - ), - scaffoldMessengerKey: scaffoldMessengerKey, - debugShowCheckedModeBanner: false, - ); - }, - valueListenable: themeNotifier, - ), - ); - } - - @override - void didChangeAppLifecycleState(AppLifecycleState state) { - switch (state) { - case AppLifecycleState.resumed: - _screenOff(); - break; - case AppLifecycleState.inactive: - _screenOff(); - break; - case AppLifecycleState.paused: - _screenOff(); - break; - case AppLifecycleState.detached: - break; - case AppLifecycleState.hidden: - break; - } - } -} - -@RoutePage() -class MainTabsPage extends StatefulWidget { - const MainTabsPage({super.key}); - - @override - State createState() => _MainTabsPageState(); -} - -class _MainTabsPageState extends State { - late List cardPacks; - late List cards; - CardPackDto? pack; - - late Repository repository; - late PackManager packManager; - - _Tabs _currentTab = _Tabs.home; - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - print('CHANGE DEPS'); - } - - @override - void initState() { - super.initState(); - repository = locator.repository; - packManager = locator.packManager; - } - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context).textTheme; - return AutoTabsRouter.tabBar( - key: scaffoldKey, - homeIndex: 0, - routes: [ - PageRouteInfo(HomePage.name), - // PageRouteInfo(ExplorePage.name), - // PageRouteInfo(ProfilePage.name), - ], - builder: (_, child, tabsController) => Scaffold( - body: child, - // bottomNavigationBar: Container( - // child: DotNavigationBar( - // currentIndex: tabsController.index, - // dotIndicatorColor: Colors.white, - // unselectedItemColor: Colors.grey[300], - // marginR: EdgeInsets.symmetric(horizontal: 32), - // itemPadding: EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0), - // duration: Duration(milliseconds: 300), - // paddingR: EdgeInsets.zero, - // borderRadius: 32, - // enableFloatingNavBar: true, - // enablePaddingAnimation: false, - // splashBorderRadius: 32, - // boxShadow: [ - // BoxShadow( - // color: Colors.black26, - // blurRadius: 8.0, - // ) - // ], - // onTap: (i) { - // tabsController.index = i; - // }, - // items: [ - // /// Home - // DotNavigationBarItem( - // icon: Icon( - // Icons.home, - // size: 32, - // ), - // selectedColor: Colors.redAccent, - // ), - // - // /// Search - // DotNavigationBarItem( - // icon: Icon( - // Icons.search, - // size: 32, - // ), - // selectedColor: Colors.redAccent, - // ), - // - // /// Profile - // DotNavigationBarItem( - // icon: Icon( - // Icons.person, - // size: 32, - // ), - // selectedColor: Colors.redAccent, - // ), - // ], - // ), - // ), - ), - ); - } -} - -enum _Tabs { home, packs, profile } diff --git a/lib/managers/audio_player.dart b/lib/managers/audio_player.dart deleted file mode 100644 index a4754c1..0000000 --- a/lib/managers/audio_player.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:flutter_tts/flutter_tts.dart'; - -import '../main.dart'; - -class AudioPlayer { - static bool get soundOn => - globalSharedPreferences.getBool('sound_on') ?? true; - - static Future playAudio(String? audio, {String? lang}) async { - if (soundOn && audio != null) { - FlutterTts flutterTts = FlutterTts(); - var playText = audio; - if (audio.contains('_')) { - final language = audio.split('_').first; - playText = audio.replaceFirst('${language}_', ''); - if (lang == null) { - await flutterTts.setLanguage(language); - } - } - if (lang != null) { - await flutterTts.setLanguage(lang); - } - var speed = globalSharedPreferences.getDouble('sound_speed'); - if (speed == null) { - speed = 0.8; - globalSharedPreferences.setDouble('sound_speed', speed); - } - await flutterTts.setSpeechRate(speed); - flutterTts.speak(playText); - } - } -} diff --git a/lib/managers/favorite_cards.dart b/lib/managers/favorite_cards.dart deleted file mode 100644 index 25fa0fc..0000000 --- a/lib/managers/favorite_cards.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'dart:async'; - -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:rxdart/rxdart.dart'; -import 'package:shared_preferences/shared_preferences.dart'; - -class FavoriteCardsController { - late final SharedPreferences _sharedPreferences; - final StreamController> controller = - StreamController.broadcast(); - List _state = []; - - FavoriteCardsController(); - - void set state(List cards) { - _state = cards; - _sharedPreferences.setStringList( - 'favorites', - cards.toList(), - ); - controller.add(_state); - } - - List get state { - _state = (_sharedPreferences.getStringList('favorites') ?? []); - return _state; - } - - Future init() async { - _sharedPreferences = await SharedPreferences.getInstance(); - } - - Future switchFavoriteCard(GameCardDto cardDto) async { - final favorites = _sharedPreferences.getStringList('favorites') ?? []; - Set updatedFavorites = favorites.toSet(); - if (favorites.contains(cardDto.id.toString())) { - updatedFavorites.remove(cardDto.id.toString()); - } else { - updatedFavorites.add(cardDto.id.toString()); - } - state = updatedFavorites.toList(); - } - - bool isFavoriteCardString(String id) => state.contains(id); - - bool isFavoriteCard(int? id) => - id == null ? false : isFavoriteCardString(id.toString()); - - Stream> get asStream => controller.stream.startWith(_state); -} diff --git a/lib/managers/repository/api.dart b/lib/managers/repository/api.dart deleted file mode 100644 index 8fbdd5a..0000000 --- a/lib/managers/repository/api.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:mnemo_cards/flags.dart'; - -import '../../main.dart'; - -mixin Api { - String get path => - !ADMIN_BUILD || (globalSharedPreferences.getBool('env') ?? true) - //prod - ? 'https://89.19.210.178:8080' //FirebaseRepository.appConfig.path - : 'https://192.168.31.158:8080'; -} diff --git a/lib/managers/repository/certificates.dart b/lib/managers/repository/certificates.dart deleted file mode 100644 index 201c9ef..0000000 --- a/lib/managers/repository/certificates.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'dart:convert'; - -class Certificates { - static get spKey => - 'qAcZM68Q7cR6gmxvB0ar0tr6axm520BNRLv66vmpWZnMHLCmzvEKNZkqL1VD1XHt'; - - static final crt = utf8.encode( - ''' ------BEGIN CERTIFICATE----- -MIIB8zCCAZmgAwIBAgIUHgV3sAY8MClrgLTwmO5OG4JsMyAwCgYIKoZIzj0EAwIw -UTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxFDASBgNVBAoMC01u -ZW1vIGNhcmRzMRcwFQYDVQQDDA4xOTIuMTY4LjMxLjE1ODAeFw0yNDA1MTgxMTUy -MTJaFw0yNzAyMTIxMTUyMTJaMF4xCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21l -LVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxFzAVBgNV -BAMMDjE5Mi4xNjguMzEuMTU4MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvO1/ -PU/ayMFgZw9KBLKpLHUpKQseY6Q6R4G51DaT8LBMfxMgAALf7PMj5IMNcW8ThjLY -MpYL8jZhufOtxCXdPKNCMEAwHQYDVR0OBBYEFBHLVqG6EfK9nrOeE1+JpUcUiEHv -MB8GA1UdIwQYMBaAFDJb2MW2vBxpKETey+ful5v7Jp3nMAoGCCqGSM49BAMCA0gA -MEUCIDwUzh9oXPE2xhArNimIpkCNyJViUf7B4kt9dgZtMLAtAiEAg3rqCqZXyys4 -mozimzUqDNc811KH3IFhC/Je4zQ3g68= ------END CERTIFICATE----- -''' - .trim(), - ); - - static final key = utf8.encode( - ''' ------BEGIN EC PRIVATE KEY----- -MHcCAQEEIDFGWXnWiqxMCYs34r5pJq2YVT3x1MoA07fnaj9iy1SYoAoGCCqGSM49 -AwEHoUQDQgAEvO1/PU/ayMFgZw9KBLKpLHUpKQseY6Q6R4G51DaT8LBMfxMgAALf -7PMj5IMNcW8ThjLYMpYL8jZhufOtxCXdPA== ------END EC PRIVATE KEY----- -''' - .trim(), - ); -} diff --git a/lib/managers/repository/dio_provider.dart b/lib/managers/repository/dio_provider.dart deleted file mode 100644 index d4f8633..0000000 --- a/lib/managers/repository/dio_provider.dart +++ /dev/null @@ -1,117 +0,0 @@ -import 'dart:developer'; -import 'dart:io'; - -import 'package:dio/dio.dart'; -import 'package:dio/io.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:package_info_plus/package_info_plus.dart'; -import 'package:collection/collection.dart'; - -import '../../di/locator.dart'; -import '../../main.dart'; - -class DioProvider { - late final Dio _dio; - - DioProvider(); - - Dio get dio => _dio; - - Future init() async { - PackageInfo appPackageInfo = await PackageInfo.fromPlatform(); - String appVersion = appPackageInfo.version; - - _dio = Dio() - ..interceptors.addAll([ - // CertificatePinningInterceptor(allowedSHAFingerprints: [ - // '28:21:5C:FB:54:4F:4A:DC:F8:5E:4C:FE:44:C8:E0:7B:0B:09:9D:D0:A9:FA:60:1D:3B:03:FF:EC:25:1A:C5:CC', - // ]), - InterceptorsWrapper(onError: (exception, handler) { - if (kDebugMode) { - log(exception.requestOptions.path ?? ''); - log(exception.toString()); - final snackBar = SnackBar( - content: Text( - '${exception.requestOptions.path}\n${exception.requestOptions.data}\n${exception.toString()}'), - ); - ScaffoldMessenger.of(scaffoldKey.currentContext!) - .showSnackBar(snackBar); - } - // if (exception.response?.statusCode == 401 && - // !locator.userManager.hasUser) { - // try { - // if (!AutoRouter.of(scaffoldKey.currentContext!) - // .isRouteActive(ProfilePage.name)) { - // AutoRouter.of(scaffoldKey.currentContext!).push( - // PageRouteInfo(ProfilePage.name), - // ); - // } - // } catch (e) {} - // } - handler.next(exception); - }), - InterceptorsWrapper(onRequest: (request, handler) async { - final String? requestToken; - try { - requestToken = TokenGenerator.generateRequestToken( - requestBody: (request.data as Object?)?.encode() ?? '', - appVersion: appVersion, - requestPath: request.uri.path, - userToken: await locator.userManager.getAuthToken(), - ); - } catch (e, s) { - log( - 'Exception while generating request token', - error: e, - stackTrace: s, - ); - throw Exception( - 'Exception while generating request token ${e} ${s}', - ); - } - request.headers = { - ...request.headers, - AppHeaders.appVersion: appVersion, - AppHeaders.requestToken: requestToken, - }; - handler.next(request); - }) - ]); - - if (!kIsWeb) { - final securityContext = SecurityContext(withTrustedRoots: true); - - // securityContext.setTrustedCertificatesBytes(Certificates.crt); - // securityContext.usePrivateKeyBytes(Certificates.key); - - _dio.httpClientAdapter = IOHttpClientAdapter( - createHttpClient: () { - HttpClient httpClient = HttpClient(context: securityContext) - ..badCertificateCallback = - (X509Certificate cert, String host, int port) { - print(cert.issuer); - print('$host $port'); - final now = DateTime.now(); - final valid = cert.startValidity.isBefore(now) && - cert.endValidity.isAfter(now); - return valid && - const DeepCollectionEquality().equals( - cert.sha1, - _cert, - ); - }; - return httpClient; - }, - ); - } - return this; - } - - static final _cert = - '0A:82:77:1E:97:98:05:76:FC:ED:76:89:87:12:E8:9E:92:14:94:D3' - .split(':') - .map((v) => int.parse(v, radix: 16)) - .toList(); -} diff --git a/lib/managers/repository/firebase_config_repository.dart b/lib/managers/repository/firebase_config_repository.dart deleted file mode 100644 index 8713c8e..0000000 --- a/lib/managers/repository/firebase_config_repository.dart +++ /dev/null @@ -1,28 +0,0 @@ - -import 'package:cloud_firestore/cloud_firestore.dart'; - -class FirebaseRepository { - static FirebaseFirestore get _firestore => FirebaseFirestore.instance; - - static ConfigModel? _lastConfig; - - static ConfigModel get appConfig { - // update(); - return _lastConfig!; - } - - static Future update() async { - _lastConfig = await _firestore - .collection('config') - .get() - .then((v) => v.docs.first) - .then((value) => value.data()) - .then((value) => ConfigModel(value['path'] as String)); - } -} - -class ConfigModel { - final String path; - - ConfigModel(this.path); -} diff --git a/lib/managers/repository/http_repository.dart b/lib/managers/repository/http_repository.dart deleted file mode 100644 index 142f365..0000000 --- a/lib/managers/repository/http_repository.dart +++ /dev/null @@ -1,170 +0,0 @@ -import 'dart:convert'; -import 'dart:developer'; -import 'dart:io'; -import 'package:dio/dio.dart'; -import 'package:mnemo_cards/features/worker/worker_ext.dart'; -import 'package:mnemo_cards/managers/repository/repository.dart'; -import 'package:mnemo_cards/managers/user_manager.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; - -import 'api.dart'; - -class HttpRepository extends Repository with Api { - final Dio _dio; - - HttpRepository(this._dio); - - @override - Future get user async => UserDto.empty; - - @override - Stream get userStream => - Stream.periodic(const Duration(seconds: 5)).map((event) => UserDto.empty); - - @override - Future updateUser(UserDto dto) async {} - - @override - Future applyPromocode(String code) async { - try { - final r = await _dio.post( - '$path/user/promocode', - data: PromoCodeDto(code: code).encode(), - ); - return (r.data as String).isolatedDecode(PromoCodeDto.fromJson); - } on DioException catch (e, s) { - log('User create error', error: e, stackTrace: s); - rethrow; - } - } - - @override - Future<(UserDto, String)> createOrGetUser( - String externalId, - ExternalIdType idType, - String email, - String? name, - ) async { - try { - final r = await _dio.post('$path/user/create', data: { - 'token': externalId, - 'tokenType': idType.name, - 'email': email, - 'name': name, - }); - //todo add client header encryption ?? - final authToken = r.headers[HttpHeaders.authorizationHeader]!.last; - return ((r.data as String).decode(UserDto.fromJson), authToken); - } on DioException catch (e, s) { - log('User create error', error: e, stackTrace: s); - rethrow; - } - } - - @override - void setAuthToken(String? authToken) { - _dio.interceptors.add( - InterceptorsWrapper(onRequest: (options, handler) { - options.headers[AppHeaders.userToken] = authToken; - handler.next(options); - }), - ); - } - - @override - Future getUser() async { - final r = await _dio.get('$path/user'); - if (r.statusCode == 200) { - return (r.data as String).isolatedDecode(UserDto.fromJson); - } - return null; - } - - @override - Future updateUserData(UserDataDto data) async { - final r = await _dio.post('$path/user/data', data: data.encode()); - } - - @override - Future updateUserSettings(UserSettingsDto settings) async { - final r = await _dio.post('$path/user/settings', data: settings.encode()); - } - - @override - Future addTestStatistics(TestStatisticsDto testStatistics) async { - final r = await _dio.post( - '$path/user/data/add/test-statistics', - data: testStatistics.encode(), - ); - } - - @override - Future> getTests({String packId = ''}) async { - final r = await _dio.get( - '$path/tests/$packId', - ); - return (r.data as String).isolatedDecodeList(TestDto.fromJson); - } - - @override - Future getTest(String id) async { - final r = await _dio.get( - '$path/test/$id', - ); - try { - return (r.data as String).isolatedDecode(TestDto.fromJson); - } catch (e, s) { - log(e.toString(), stackTrace: s); - return null; - } - } - - @override - Future checkPayment({ - required MnemoCardsProduct product, - required String key, - required PaymentSystem paymentSystem, - }) async { - final r = await _dio.get( - '$path/check_payment/', - queryParameters: { - 'type': product.type.name, - 'id': product.id, - 'token': key, - 'system': paymentSystem.name, - }, - ); - return jsonDecode(r.data ?? '{}')['result'] ?? false; - } - - @override - Future checkUserPayment() async { - final r = await _dio.get('$path/check_payments/'); - final result = jsonDecode(r.data ?? '{}')?['result'] ?? false; - return result; - } - - Future createPayment( - String id, - MnemoCardsProductType productType, - PaymentSystem paymentSystem, - ) async { - final r = await _dio.get( - '$path/create_payment/', - queryParameters: { - 'id': id, - 'type': productType.name, - 'system': paymentSystem.name, - }, - ); - return (r.data as String).isolatedDecode(YookassaPaymentDto.fromJson); - } - - Future deleteUser(String id) async { - final r = await _dio.post( - '$path/users/delete', - data: jsonEncode({'id': id}), - ); - return r.statusCode == 200; - } -} diff --git a/lib/managers/repository/repository.dart b/lib/managers/repository/repository.dart deleted file mode 100644 index 4e1247f..0000000 --- a/lib/managers/repository/repository.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:mnemo_cards/managers/user_manager.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; - -abstract class Repository { - Future get user; - - Stream get userStream; - - Future updateUser(UserDto dto); - - Future applyPromocode(String promocode); - - Future<(UserDto, String)> createOrGetUser( - String externalId, - ExternalIdType idType, - String email, - String? name, - ); - - Future getUser(); - - void setAuthToken(String? authToken); - - Future updateUserData(UserDataDto data); - - Future updateUserSettings(UserSettingsDto settings); - - Future addTestStatistics(TestStatisticsDto testStatistics); - - Future> getTests(); - - Future getTest(String id); - - Future checkPayment({ - required MnemoCardsProduct product, - required String key, - required PaymentSystem paymentSystem, - }); - - Future checkUserPayment(); - - Future createPayment( - String id, - MnemoCardsProductType productType, - PaymentSystem type, - ); - - Future deleteUser(String id); -} diff --git a/lib/managers/user_manager.dart b/lib/managers/user_manager.dart deleted file mode 100644 index ed39687..0000000 --- a/lib/managers/user_manager.dart +++ /dev/null @@ -1,190 +0,0 @@ -import 'dart:async'; -import 'dart:developer'; - -import 'package:dio/dio.dart'; -import 'package:flutter/foundation.dart'; -import 'package:google_sign_in/google_sign_in.dart'; -import 'package:mnemo_cards/di/locator.dart'; -import 'package:mnemo_cards/main.dart'; -import 'package:mnemo_cards/managers/repository/http_repository.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:rxdart/rxdart.dart'; -import 'package:shared_preferences/shared_preferences.dart'; - -import '../domain/router/app_router.dart'; -import '../features/analytics/analytics.dart'; -import 'user_state_holder.dart'; - -class UserManager { - CompositeSubscription? _subscription; - final HttpRepository _repository; - GoogleSignIn _googleSignIn = GoogleSignIn( - scopes: [ - 'email', - // 'https://www.googleapis.com/auth/contacts.readonly', - ], - clientId: kIsWeb - ? '701767851968-bud97rud1d9qtqju96addn31nhm0oofu.apps.googleusercontent.com' - : null, - // "701767851968-bud97rud1d9qtqju96addn31nhm0oofu.apps.googleusercontent.com", - ); - - late final SharedPreferences? _sharedPreferences; - final UserStateHolder userStateHolder; - - UserManager(this._repository) : userStateHolder = UserStateHolder(); - - bool get hasUser => user != null; - - UserDto? get user => userStateHolder.user; - - int? get userId => userStateHolder.user?.id; - - Future login() async { - try { - if (await _googleSignIn.isSignedIn()) { - await _googleSignIn.signOut(); - } - final account = await _googleSignIn.signIn(); - await Future.delayed(Duration(milliseconds: 300)); - } catch (e, s) { - log('Error in login', error: e, stackTrace: s); - } - } - - Future logout() async { - if (await _googleSignIn.isSignedIn()) { - await _googleSignIn.signOut(); - } - Analytics.logout(userStateHolder.user); - userStateHolder.clear(); - } - - Future _createUser( - String externalId, - ExternalIdType idType, - String email, - String? name, - ) async { - final (user, token) = - await _repository.createOrGetUser(externalId, idType, email, name); - await _setAuthToken(token); - userStateHolder.setUser(user); - } - - Future _setAuthToken(String authToken) async { - _repository.setAuthToken(authToken); - await secureStorage.write(key: 'authToken', value: authToken); - } - - Future _clearAuthToken() async { - await secureStorage.delete(key: 'authToken'); - } - - Future getAuthToken() async { - return secureStorage.read(key: 'authToken'); - } - - Future applyPromocode(String code) async { - final data = await _repository.applyPromocode(code); - return data; - } - - Future updateUser() async { - String? authToken = await getAuthToken(); - if (authToken != null) { - await _setAuthToken(authToken); - try { - final fetchedUser = await _repository.getUser(); - userStateHolder.setUser(fetchedUser!); - return fetchedUser; - } catch (e) { - _clearAuthToken(); - AppRouter.openAuthOrProfile(); - } - } - // not creating empty user - // if (Platform.isAndroid) { - // final deviceInfoPlugin = DeviceInfoPlugin(); - // final deviceInfo = await deviceInfoPlugin.androidInfo; - // final id = deviceInfo.id; - // await _createUser(id, ExternalIdType.device); - // } - return null; - } - - Future deleteUser() async { - final user = await updateUser(); - if (user?.id != null) { - final result = await _repository.deleteUser(user!.id.toString()); - if (result) { - logout(); - _clearAuthToken(); - } - return result; - } - return false; - } - - Future init() async { - _sharedPreferences = await SharedPreferences.getInstance(); - try { - await updateUser(); - } on Object catch (e, s) { - log('User manager init error', error: e, stackTrace: s); - } - var firstSkipped = false; - _subscription = CompositeSubscription() - ..add(_googleSignIn.onCurrentUserChanged - .startWith(_googleSignIn.currentUser) - .distinct((p, n) => p?.id == n?.id) - .listen((account) async { - if (account != null) { - final email = account.email; - final name = account.displayName; - final auth = await account.authentication; - final externalToken = auth.idToken; - Analytics.login(ExternalIdType.google); - await _createUser(externalToken!, ExternalIdType.google, email, name); - } - })) - ..add( - userStateHolder.asNullableStream - .distinct((a, b) => a?.id == b?.id) - // don't want to clear cache on login - .skipWhile((user) => user != null && !firstSkipped) - .listen( - (user) async { - firstSkipped = true; - try { - if (user == null) { - AppRouter.openAuthOrProfile(); - } else { - try { - AppRouter.closeAuthPage(); - await locator.previewPackPoller.poll(); - await locator.packManager.clearCache(); - await locator.packUpdater.updateAvailablePacks(); - } catch (err) { - log(err.toString()); - } - } - } catch (e) { - log(e.toString()); - _clearAuthToken(); - if (e is DioException) { - if (e.response?.statusCode == 401) { - AppRouter.openProfile(); - } - } - } - }, - ), - ); - } - - Future dispose() async { - await _subscription?.cancel(); - _subscription = null; - } -} diff --git a/lib/managers/user_state_holder.dart b/lib/managers/user_state_holder.dart deleted file mode 100644 index 777d1bd..0000000 --- a/lib/managers/user_state_holder.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/widgets.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:rxdart/rxdart.dart'; - -import '../features/analytics/analytics.dart'; - - -class UserStateHolder extends ChangeNotifier { - UserDto? _dto; - final _streamController = StreamController.broadcast(); - - static final _instance = UserStateHolder._(); - - UserStateHolder._(); - - factory UserStateHolder() => _instance; - - void setUser(UserDto dto) { - _dto = dto.copyWith(); - Analytics.setUser(dto); - - notifyListeners(); - _streamController.add(_dto); - } - - void clear() { - _dto = null; - notifyListeners(); - _streamController.add(null); - } - - Stream get asStream => asNullableStream.whereNotNull(); - - Stream get asNullableStream => - _streamController.stream.startWith(_dto); - - UserDto? get user => _dto; -} diff --git a/lib/pages/auth_page.dart b/lib/pages/auth_page.dart deleted file mode 100644 index 63e2b65..0000000 --- a/lib/pages/auth_page.dart +++ /dev/null @@ -1,107 +0,0 @@ -import 'package:auto_route/annotations.dart'; -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:mnemo_cards/di/locator.dart'; -import 'package:mnemo_cards/flags.dart'; -import 'package:url_launcher/url_launcher.dart'; - -import '../theme/themes.dart'; -import '../widgets/shared_pref_button.dart'; - -@RoutePage() -class AuthPage extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Scaffold( - body: SafeArea( - child: Column( - children: [ - SizedBox( - height: 64.h - MediaQuery.of(context).padding.top, - ), - Text( - 'Mnemo cards', - style: TextStyle( - fontSize: 36.sp, - fontWeight: FontWeight.w700, - height: 0.85, - ), - textAlign: TextAlign.start, - ), - SizedBox( - height: 60.h, - ), - Expanded( - child: Image.asset( - 'images/cerdo.jpg', - fit: BoxFit.fitWidth, - ), - ), - if (ADMIN_BUILD) - MobileSharedPrefButton( - builder: (v, _) => (v ?? false) ? Text('PROD') : Text('TEST'), - spKey: 'env', - setOnTap: (v) => !(v ?? false), - ), - Padding( - padding: - EdgeInsets.symmetric(horizontal: 10.0.w, vertical: 5.0.h), - child: InkWell( - onTap: locator.userManager.login, - child: Container( - height: 90.h, - alignment: Alignment.center, - decoration: BoxDecoration( - border: Border.all(color: borderGray), - borderRadius: BorderRadius.circular(16.0), - ), - padding: EdgeInsets.all(10.0.w), - child: Image.asset( - 'icons/google.png', - height: 88.h, - ), - ), - ), - ), - Padding( - padding: - EdgeInsets.symmetric(horizontal: 10.0.w, vertical: 10.0.h), - child: RichText( - text: TextSpan( - children: [ - TextSpan( - text: - 'Присоединяясь, я заявляю что прочитал и принимаю '), - // TextSpan( - // text: 'условия', - // recognizer: TapGestureRecognizer() - // ..onTap = - // () => launchUrl(Uri.parse('https://www.google.com')), - // style: TextStyle(decoration: TextDecoration.underline), - // ), - // TextSpan(text: ' и '), - TextSpan( - text: 'политику конфиденциальности', - recognizer: TapGestureRecognizer() - ..onTap = () => launchUrl(Uri.parse( - 'https://www.freeprivacypolicy.com/live/1c2bce51-86c6-4a36-b226-b6c6f50ebf0f')), - style: - TextStyle(decoration: TextDecoration.underline)), - TextSpan(text: '.'), - ], - style: TextStyle( - fontSize: 14.sp, - fontWeight: FontWeight.w300, - height: 1.1, - color: borderGray, - ), - ), - textAlign: TextAlign.center), - ), - ], - ), - ), - ); - } -} diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart deleted file mode 100644 index 961a227..0000000 --- a/lib/pages/home_page.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:auto_route/auto_route.dart'; -import 'package:flutter/material.dart'; -import 'package:mnemo_cards/di/locator.dart'; - -import '../widgets/packs_menu_widget.dart'; - -@RoutePage() -class HomePage extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Scaffold( - body: SafeArea( - child: PacksMenuWidget( - locator.repository, - locator.packManager, - ), - ), - ); - } - - const HomePage(); -} diff --git a/lib/pages/profile_page.dart b/lib/pages/profile_page.dart deleted file mode 100644 index e52cdd3..0000000 --- a/lib/pages/profile_page.dart +++ /dev/null @@ -1,192 +0,0 @@ -import 'dart:io'; - -import 'package:auto_route/auto_route.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:mnemo_cards/admin/add_plan.dart'; -import 'package:mnemo_cards/admin/add_promocode_campaign.dart'; -import 'package:mnemo_cards/domain/router/app_router.dart'; -import 'package:mnemo_cards/domain/router/app_router.gr.dart'; -import 'package:mnemo_cards/features/yandex_ads/yandex_ads.dart'; -import 'package:mnemo_cards/flags.dart'; -import 'package:mnemo_cards/main.dart'; -import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart'; -import 'package:shared_preferences/shared_preferences.dart'; -import 'package:url_launcher/url_launcher.dart'; - -import '../admin/all_cards.dart'; -import '../admin/all_users.dart'; -import '../di/locator.dart'; -import '../domain/router/dialogs.dart'; -import '../features/analytics/analytics.dart'; -import '../theme/themes.dart'; -import '../widgets/buy_subscription_widget.dart'; -import '../widgets/enter_promo_code_widget.dart'; -import '../widgets/shared_pref_button.dart'; - -@RoutePage() -class ProfilePage extends StatelessWidget { - @override - Widget build(BuildContext context) { - Analytics.profilePageOpened(locator.userManager.userStateHolder.user); - locator.userManager.updateUser(); - final theme = Theme.of(context).textTheme; - int i = 0; - context.visitAncestorElements((visitor) { - if (visitor.widget.runtimeType == SizedBox) { - if ((visitor.widget as SizedBox).width == 200) { - print('found'); - } - i++; - } - return true; - }); - return Scaffold( - body: SafeArea( - child: Column( - children: [ - Header( - 'Профиль', - popText: 'назад', - hasPopButton: true, - trail: GestureDetector( - onTap: () async { - await locator.userManager.logout(); - final showAdmin = SHOW_ADMIN; - final sp = await SharedPreferences.getInstance(); - sp.clear(); - if (showAdmin) { - globalSharedPreferences.setBool('show_admin', true); - } - AppRouter.openAuthOrProfile(); - }, - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - mainAxisSize: MainAxisSize.max, - children: [ - Image.asset( - 'icons/exit.png', - width: 25.w, - color: Theme.of(context).colorScheme.primary, - ), - ], - ), - ), - ), - SizedBox( - height: 8.h, - ), - Expanded( - child: ListView( - physics: PageScrollPhysics(), - children: [ - SizedBox( - height: 320.h, - child: StreamBuilder( - stream: locator.userManager.userStateHolder.asStream, - builder: (context, snapshot) { - final userData = snapshot.data?.userDataDto; - return Padding( - padding: const EdgeInsets.only( - left: 10.0, - right: 10.0, - bottom: 8.0, - ), - child: UserStatistics(userData), - ); - }, - ), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10.0), - child: Column( - children: [ - Divider( - height: 5, - color: borderGray.withOpacity(0.2), - ), - const MobileEnterPromoCodeWidget(), - Divider( - height: 5, - color: borderGray.withOpacity(0.2), - ), - const MobileBuySubscriptionWidget(), - Divider( - height: 5, - color: borderGray.withOpacity(0.2), - ), - if (SHOW_ADMIN) ...[ - SimpleTile( - title: Text('Посмотреть рекламу'), - onTap: () { - YandexAds.showRewardedAd((r) { - showInfoDialog( - 'Вы получили\n${r.amount} ${r.type}'); - }); - }, - ), - Divider( - height: 5, - color: borderGray.withOpacity(0.2), - ), - ], - SimpleTile.text( - text: 'Написать в телеграм', - onTap: () => launchUrl( - Uri.parse('https://t.me/mnemo_cards_bot'), - ), - ), - Divider( - height: 5, - color: borderGray.withOpacity(0.2), - ), - SimpleTile( - onTap: () => appRouter.push( - PageRouteInfo(SettingsPage.name), - ), - title: Row( - children: [ - Image.asset( - 'icons/settings.png', - width: 16.w, - color: Theme.of(context).colorScheme.primary, - ), - SizedBox(width: 8.w), - Expanded(child: Text('Настройки там')), - ], - ), - trail: Image.asset( - 'icons/next.png', - width: 16.w, - color: Theme.of(context).colorScheme.primary, - ), - ), - Divider( - height: 5, - color: borderGray.withOpacity(0.2), - ), - SizedBox( - height: 10.0.h, - ), - ], - ), - ), - ], - ), - ), - if (MediaQuery.of(context).viewInsets.bottom < 30.0) - const BigBackButton(), - ], - ), - ), - ); - } - - Future clearAndExit() async { - final sp = await SharedPreferences.getInstance(); - sp.clear(); - exit(0); - } - - const ProfilePage(); -} diff --git a/lib/pages/settgins_page.dart b/lib/pages/settgins_page.dart deleted file mode 100644 index b498786..0000000 --- a/lib/pages/settgins_page.dart +++ /dev/null @@ -1,232 +0,0 @@ -import 'dart:io'; - -import 'package:auto_route/auto_route.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:mnemo_cards/domain/router/dialogs.dart'; -import 'package:mnemo_cards/flags.dart'; -import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart'; -import 'package:shared_preferences/shared_preferences.dart'; -import 'package:url_launcher/url_launcher.dart'; - -import '../admin/add_plan.dart'; -import '../admin/add_promocode_campaign.dart'; -import '../admin/admin_api.dart'; -import '../admin/all_cards.dart'; -import '../admin/all_users.dart'; -import '../di/injector.dart'; -import '../di/locator.dart'; -import '../features/analytics/analytics.dart'; -import '../managers/repository/dio_provider.dart'; -import '../theme/themes.dart'; -import '../widgets/shared_pref_button.dart'; - -@RoutePage() -class SettingsPage extends StatelessWidget { - void _initAdmin() { - if (!getIt.isRegistered()) { - getIt.registerLazySingleton( - () => AdminApi(getIt.get().dio)); - print('set admin'); - } - } - - @override - Widget build(BuildContext context) { - Analytics.settingsPageOpened(locator.userManager.userStateHolder.user); - return Scaffold( - body: SafeArea( - child: Column( - children: [ - Header( - 'Настройки', - popText: 'назад', - hasPopButton: true, - ), - SizedBox( - height: 8.h, - ), - Divider( - height: 1, - color: borderGray.withOpacity(0.2), - ), - Expanded( - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 10.0.w), - child: SingleChildScrollView( - reverse: true, - child: Column( - children: [ - ...[ - SimpleTile.text( - onLongTap: () async { - final r = await locator.userManager.deleteUser(); - if (!r) { - showInfoDialog( - 'Не удалось удалить аккаунт\nПопробуйте перелогиниться', - ); - } - }, - text: 'Удалить аккаунт (зажми)', - ), - MobileSharedPrefButton.builder( - spKey: 'dark_theme', - builder: (enabled, _) => AbsorbPointer( - child: SwitchTile( - text: 'Темная тема', - value: enabled ?? false, - ), - ), - setOnTap: (v) { - final darkTheme = !(v ?? false); - themeNotifier.value = - darkTheme ? ThemeMode.dark : ThemeMode.light; - return darkTheme; - }, - ), - MobileSharedPrefButton.builder( - spKey: 'sound_on', - builder: (enabled, _) => AbsorbPointer( - child: SwitchTile( - text: 'Звук', - value: enabled ?? false, - ), - ), - setOnTap: (v) => !(v ?? false), - ), - MobileSharedPrefButton.builder( - spKey: 'auto_play_sound_view', - builder: (enabled, _) => AbsorbPointer( - child: SwitchTile( - text: 'Авто воспроизведение при просмотре', - value: enabled ?? false, - ), - ), - setOnTap: (v) => !(v ?? false), - ), - MobileSharedPrefButton.builder( - spKey: 'auto_play_sound_tests', - builder: (enabled, _) => AbsorbPointer( - child: SwitchTile( - text: 'Авто воспроизведение в тестах', - value: enabled ?? false, - ), - ), - setOnTap: (v) => !(v ?? false), - ), - SimpleTile.text( - text: 'Скорость чтения', - trail: MobileSharedPrefButton.builder( - spKey: 'sound_speed', - builder: (v, s) => MnemoSlider( - onChanged: (value) => s(value), - value: v ?? 1.0, - ), - shouldRebuild: (v) => false, - ), - ), - if (ADMIN_BUILD) - MobileSharedPrefButton.builder( - spKey: 'show_admin', - builder: (enabled, _) => AbsorbPointer( - child: SwitchTile( - text: 'Show admin', - value: enabled ?? false, - ), - ), - setOnTap: (v) { - final val = !(v ?? false); - if (val) { - _initAdmin(); - } - return val; - }, - ), - if (ADMIN_BUILD) ..._adminTiles(context), - ].expand((widget) => [ - widget, - Divider( - height: 1, - color: borderGray.withOpacity(0.2), - ), - ]), - Padding( - padding: - EdgeInsets.only(left: 10.0.w, right: 10.w, top: 32.h), - child: LinkButton( - 'Политика конфиденциальности', - () { - launchUrl( - Uri.parse( - 'https://www.freeprivacypolicy.com/live/1c2bce51-86c6-4a36-b226-b6c6f50ebf0f'), - ); - }, - ), - ), - ], - ), - ), - ), - ), - const BigBackButton(), - ], - ), - ), - ); - } - - List _adminTiles(BuildContext context) => [ - SimpleTile.text( - text: 'Cards', - onTap: () { - _initAdmin(); - showDialog(context: context, builder: (c) => const AllCards()); - }, - ), - SimpleTile.text( - text: 'Users', - onTap: () { - _initAdmin(); - showDialog(context: context, builder: (c) => const AllUsers()); - }, - ), - SimpleTile.text( - text: 'Plans', - onTap: () { - _initAdmin(); - AddPlan().addPlan(context); - }, - ), - SimpleTile.text( - text: 'Promo codes', - onTap: () { - _initAdmin(); - AddPromocodeCampaign().addCampaign(context); - }, - ), - SimpleTile.text( - text: 'Clear cache', - onTap: () { - locator.packManager.clearCache(); - }, - ), - MobileSharedPrefButton( - builder: (enabled, _) => AbsorbPointer( - child: SwitchTile( - text: 'Use prod', - value: enabled ?? false, - ), - ), - spKey: 'env', - setOnTap: (v) => !(v ?? false), - ), - ]; - - Future clearAndExit() async { - final sp = await SharedPreferences.getInstance(); - sp.clear(); - exit(0); - } - - const SettingsPage(); -} diff --git a/lib/theme/themes.dart b/lib/theme/themes.dart deleted file mode 100644 index c2145df..0000000 --- a/lib/theme/themes.dart +++ /dev/null @@ -1,107 +0,0 @@ -import 'package:flutter/material.dart'; -// import 'package:google_fonts/google_fonts.dart'; - -final ValueNotifier themeNotifier = ValueNotifier(ThemeMode.light); - -const Color peach = Color(0xffffc994); -const Color golden = Color(0xffffea00); -const Color green = Color(0xff3d5309); -const Color greenAccent = Color(0xff688d11); -const Color black = Color(0xff000000); -const Color mainBackgroundLight = Color(0xffffffff); -const Color mainBackgroundDark = Color(0xff000000); -const Color white = Color(0xffffffff); -const Color yellow = Colors.yellowAccent; -const Color progressBlue = Color(0xff8CCBFF); -const Color menuBlue = Color(0xff99C4E9); -const Color backgroundBlue = Color(0xFFf0f0f0); -const Color testBlue = Color(0xffD0EAFF); -const Color borderGray = Color(0xffABABAB); - -final Map color = { - 50: const Color.fromRGBO(0, 0, 0, .1), - 100: const Color.fromRGBO(0, 0, 0, .2), - 200: const Color.fromRGBO(0, 0, 0, .3), - 300: const Color.fromRGBO(0, 0, 0, .4), - 400: const Color.fromRGBO(0, 0, 0, .5), - 500: const Color.fromRGBO(0, 0, 0, .6), - 600: const Color.fromRGBO(0, 0, 0, .7), - 700: const Color.fromRGBO(0, 0, 0, .8), - 800: const Color.fromRGBO(0, 0, 0, .9), - 900: const Color.fromRGBO(0, 0, 0, 1), -}; - -final Map colorWhite = { - 50: const Color.fromRGBO(255, 255, 255, .1), - 100: const Color.fromRGBO(255, 255, 255, .2), - 200: const Color.fromRGBO(255, 255, 255, .3), - 300: const Color.fromRGBO(255, 255, 255, .4), - 400: const Color.fromRGBO(255, 255, 255, .5), - 500: const Color.fromRGBO(255, 255, 255, .6), - 600: const Color.fromRGBO(255, 255, 255, .7), - 700: const Color.fromRGBO(255, 255, 255, .8), - 800: const Color.fromRGBO(255, 255, 255, .9), - 900: const Color.fromRGBO(255, 255, 255, 1), -}; - -final theme2 = ThemeData( - useMaterial3: true, - brightness: Brightness.light, - fontFamily: 'Nunito', - // textTheme: GoogleFonts.exo2TextTheme(), - colorScheme: ColorScheme.light( - secondaryContainer: Colors.grey, - primary: MaterialColor(0xFF000000, color), - surface: MaterialColor(0xFFFFFFFF, colorWhite), - surfaceContainerHighest: MaterialColor(0xFFFFFFFF, colorWhite), - onSurface: Colors.black, - ), - - tabBarTheme: TabBarTheme( - labelColor: MaterialColor(0xFF000000, color), - ), - appBarTheme: AppBarTheme( - iconTheme: IconThemeData( - color: MaterialColor(0xFF000000, color), - ), - ), - textTheme: TextTheme( - titleLarge: TextStyle(fontWeight: FontWeight.w700), - titleMedium: TextStyle(fontWeight: FontWeight.w700), - titleSmall: TextStyle(fontWeight: FontWeight.w700), - bodyLarge: TextStyle(fontWeight: FontWeight.w700), - bodyMedium: TextStyle(fontWeight: FontWeight.w700), - bodySmall: TextStyle(fontWeight: FontWeight.w700), - labelLarge: TextStyle(fontWeight: FontWeight.w700), - labelMedium: TextStyle(fontWeight: FontWeight.w700), - labelSmall: TextStyle(fontWeight: FontWeight.w700), - displayLarge: TextStyle(fontWeight: FontWeight.w700), - displayMedium: TextStyle(fontWeight: FontWeight.w700), - displaySmall: TextStyle(fontWeight: FontWeight.w700), - headlineLarge: TextStyle(fontWeight: FontWeight.w700), - headlineMedium: TextStyle(fontWeight: FontWeight.w700), - headlineSmall: TextStyle(fontWeight: FontWeight.w700), - ), - primarySwatch: MaterialColor(0xFF000000, color), - visualDensity: VisualDensity.adaptivePlatformDensity, -); -final darkTheme2 = ThemeData( - useMaterial3: true, - brightness: Brightness.dark, - fontFamily: 'Nunito', - // textTheme: GoogleFonts.exo2TextTheme(), - colorScheme: ColorScheme.dark( - primary: MaterialColor(0xFFFFFFFF, colorWhite), - secondary: Colors.lightBlue, - tertiary: Colors.lightBlue, - ), - tabBarTheme: TabBarTheme( - labelColor: MaterialColor(0xFFFFFFFF, colorWhite), - ), - appBarTheme: AppBarTheme( - iconTheme: IconThemeData( - color: MaterialColor(0xFFFFFFFF, colorWhite), - )), - primarySwatch: MaterialColor(0xFFFFFFFF, colorWhite), - visualDensity: VisualDensity.adaptivePlatformDensity, -); diff --git a/lib/utils/color_helper.dart b/lib/utils/color_helper.dart deleted file mode 100644 index 443b728..0000000 --- a/lib/utils/color_helper.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:flutter/painting.dart'; - -extension ColorBrightness on Color { - Color darken([double amount = .1]) { - assert(amount >= 0 && amount <= 1); - - final hsl = HSLColor.fromColor(this); - final hslDark = hsl.withLightness((hsl.lightness - amount).clamp(0.0, 1.0)); - - return hslDark.toColor(); - } - - Color lighten([double amount = .1]) { - assert(amount >= 0 && amount <= 1); - - final hsl = HSLColor.fromColor(this); - final hslLight = - hsl.withLightness((hsl.lightness + amount).clamp(0.0, 1.0)); - - return hslLight.toColor(); - } -} diff --git a/lib/utils/string_helper.dart b/lib/utils/string_helper.dart deleted file mode 100644 index c856d38..0000000 --- a/lib/utils/string_helper.dart +++ /dev/null @@ -1,25 +0,0 @@ - -import 'dart:ui'; - -extension StringHelper on String { - String get capitalize => - "${this[0].toUpperCase()}${this.substring(1).toLowerCase()}"; - - Color? get asColor { - if (startsWith('#')) { - return replaceFirst('#', '0x').asColor; - } - if (startsWith('0x')) { - if (length > '0x001122'.length) { - return Color(int.parse(this)); - } else { - return Color(0xff000000 + int.parse(this)); - } - } else { - if (length == 'aabbcc'.length || length == 'ffaabbcc'.length) { - return '0x$this'.asColor; - } - } - return Color(0xffffffff); - } -} diff --git a/lib/widgets/buy_subscription_widget.dart b/lib/widgets/buy_subscription_widget.dart deleted file mode 100644 index c15400b..0000000 --- a/lib/widgets/buy_subscription_widget.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:mnemo_cards/main.dart'; -import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart'; - -import '../di/locator.dart'; -import '../domain/router/app_router.dart'; -import '../features/subscription/subscription_page.dart'; - -class MobileBuySubscriptionWidget extends StatelessWidget { - @override - Widget build(BuildContext context) { - return BuySubscriptionWidget( - onBuySubscription: () => - locator.subscriptionManager.openSubscriptionPage(), - ); - } - - const MobileBuySubscriptionWidget(); -} diff --git a/lib/widgets/card_game/card_game_header.dart b/lib/widgets/card_game/card_game_header.dart deleted file mode 100644 index cade818..0000000 --- a/lib/widgets/card_game/card_game_header.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart'; - -import '../../features/card_swiper/src/card_swiper_controller.dart'; - -class CardGameHeader extends StatelessWidget { - final CardSwiperController controller; - final CardPackDto cardPackDto; - - const CardGameHeader( - this.controller, - this.cardPackDto, - ); - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context).textTheme; - - return StreamBuilder( - stream: controller.asStream, - builder: (context, snapshot) => Header( - '${cardPackDto.title}', - // trailing: Text( - // '${snapshot.data}/${cardPackDto.size}', - // style: theme.headlineSmall, - // ), - ), - ); - } -} diff --git a/lib/widgets/card_game/card_game_main.dart b/lib/widgets/card_game/card_game_main.dart deleted file mode 100644 index ad971f7..0000000 --- a/lib/widgets/card_game/card_game_main.dart +++ /dev/null @@ -1,146 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; -import 'package:mnemo_cards/features/packs/images_holder.dart'; -import 'package:mnemo_cards/utils/color_helper.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:rxdart/rxdart.dart'; - -import '../../features/card_flipper/flip_card_controllers.dart'; -import '../../features/card_swiper/src/allowed_swipe_direction.dart'; -import '../../features/card_swiper/src/card_swiper.dart'; -import '../../features/card_swiper/src/card_swiper_controller.dart'; -import '../../main.dart'; -import '../game_card_widget.dart'; - -class CardGameMainWidget extends StatelessWidget { - final List cards; - final Color? color; - final int? startCardId; - final CardSwiperController cardSwiperController; - final FlipCardController flipCardController; - final int length; - - const CardGameMainWidget( - this.cards, - this.color, - this.startCardId, - this.cardSwiperController, - this.flipCardController, - this.length, - ); - - @override - Widget build(BuildContext context) { - final numberOfCardsDisplayed = min(2, cards.length); - final initialScale = 0.9; - - GameCardDto? activeCard() => cardSwiperController.currentIndex >= 0 - ? cards[cardSwiperController.currentIndex % cards.length] - : null; - - return PopScope( - canPop: false, - onPopInvoked: (v) { - if (!v) { - appRouter.popForced(activeCard()?.id); - } - }, - child: Material( - type: MaterialType.transparency, - child: LayoutBuilder(builder: (context, constraints) { - final cardWidth = constraints.maxWidth; - final cardHeight = cardWidth; - bool enabled = true; - return StatefulBuilder(builder: (context, setState) { - return Stack( - children: [ - _EndWidget(), - if (enabled) - Hero( - tag: activeCard()?.id ?? 'empty', - child: StreamBuilder( - stream: cardSwiperController.asStream - .map((index) => /*cards[index].isAd*/ false) - .pairwise() - .asyncMap((pair) async { - if (pair.first) { - await Future.delayed(Duration(seconds: 5)); - return false; - } - return pair.last; - }).timeout(Duration(seconds: 5), - onTimeout: (_) => false), - builder: (context, snapshot) { - return AbsorbPointer( - absorbing: snapshot.data ?? false, - child: CardSwiper( - controller: cardSwiperController, - padding: const EdgeInsets.symmetric( - vertical: 0.0, - horizontal: 0.0, - ), - cardsCount: length, - numberOfCardsDisplayed: numberOfCardsDisplayed, - allowedSwipeDirection: - AllowedSwipeDirection.all(), - scale: initialScale, - backCardOffset: Offset(0, cardHeight * 0.1 / 2), - isLoop: false, - cardBuilder: ( - context, - cardIndex, - percentThresholdX, - percentThresholdY, - scale, - ) { - final index = cardIndex % cards.length; - final isFirstCard = - (cardSwiperController.currentIndex == - cardIndex); - if (cardSwiperController.currentIndex == - index) { - // lastThreshold = percentThresholdX; - } - print('${cards[index].translation} $scale'); - final cardWidget = Center( - child: GameCardWidget( - cards[index], - cards[index].memoryImage, - flipCardController, - flipCardController.state - // || cards[index].isAd - , - isFirstCard, - isFirstCard - ? color - : color?.darken( - (scale - 1) / - (initialScale - 1) * - 0.075, - ), - ), - ); - return cardWidget; - }, - ), - ); - }), - ), - ], - ); - }); - }), - ), - ); - } -} - -class _EndWidget extends StatelessWidget { - _EndWidget(); - - @override - Widget build(BuildContext context) { - return Container(); - } -} diff --git a/lib/widgets/card_game/card_game_page.dart b/lib/widgets/card_game/card_game_page.dart deleted file mode 100644 index 68ea191..0000000 --- a/lib/widgets/card_game/card_game_page.dart +++ /dev/null @@ -1,303 +0,0 @@ -import 'dart:async'; - -import 'package:auto_route/auto_route.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:mnemo_cards/features/tests/progress_widget.dart'; -import 'package:mnemo_cards/main.dart'; -import 'package:mnemo_cards/managers/audio_player.dart'; -import 'package:mnemo_cards/theme/themes.dart'; -import 'package:mnemo_cards/utils/color_helper.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import '../../di/locator.dart'; -import '../../features/card_flipper/flip_card_controllers.dart'; -import '../../features/card_swiper/flutter_card_swiper.dart'; -import 'card_game_main.dart'; - -@RoutePage() -class CardGamePage extends StatefulWidget { - final int? startCardId; - final List cards; - final Color? color; - final bool infinite; - final bool shuffle; - - const CardGamePage({ - required this.cards, - required this.color, - this.startCardId, - this.infinite = false, - this.shuffle = true, - super.key, - }); - - @override - State createState() => _CardGamePageState(); -} - -class _CardGamePageState extends State { - final CardSwiperController cardSwiperController = CardSwiperController(); - final FlipCardController flipCardController = FlipCardController(); - - StreamSubscription? _sub; - - List shuffledCards = []; - - GameCardDto? get activeCard { - final index = cardSwiperController.currentIndex; - if (index >= 0) { - return shuffledCards[index % shuffledCards.length]; - } - return null; - } - - int get gameLength => widget.infinite ? 10000000 : shuffledCards.length; - - void playSound(int index) { - if (globalSharedPreferences.getBool('auto_play_sound_view') == true) { - final text = activeCard?.original; - if (text != null) { - AudioPlayer.playAudio(text, lang: 'es-ES'); - } - } - } - - @override - Widget build(BuildContext context) { - // todo move to controller - // final timer = Stream.periodic(Duration(seconds: 1), (tick) { - // return Duration(seconds: tick + 1); - // }).asBroadcastStream().take(3600).startWith(Duration.zero); - - return Scaffold( - body: PopScope( - child: SafeArea( - child: LayoutBuilder(builder: (context, constraints) { - return Column( - children: [ - Expanded( - child: LayoutBuilder( - builder: (context, constraints) { - final aspectRatio = - constraints.maxHeight / constraints.maxWidth; - return Column( - children: [ - if (aspectRatio > 14 / 9) - SizedBox( - height: (64.h - - 20.h - - MediaQuery.of(context).padding.top) / - 2, - ), - if (aspectRatio > 14 / 9) - Padding( - padding: EdgeInsets.symmetric(horizontal: 10.w), - child: StreamBuilder( - stream: cardSwiperController.asStream, - builder: (context, snapshot) { - if (!snapshot.hasData) { - return SizedBox( - height: 30, - ); - } - return ProgressWidget( - widget.infinite - ? null - : List.filled( - cardSwiperController.progress, - Result.correct, - ), - shuffledCards.length, - widget.color ?? borderGray, - popText: widget.infinite ? 'к теме' : null, - key: ValueKey('CARDS PROGRESS'), - ); - }, - ), - ), - if (aspectRatio > 14 / 9) - SizedBox( - height: (64.h - - 20.h - - MediaQuery.of(context).padding.top) / - 2, - ), - Expanded( - child: Stack( - children: [ - CardGameMainWidget( - shuffledCards, - widget.color, - widget.startCardId, - cardSwiperController, - flipCardController, - gameLength, - ), - if (aspectRatio <= 14 / 9) - Align( - alignment: Alignment.topLeft, - child: IconButton( - onPressed: - AutoRouter.of(context).maybePop, - icon: Icon( - Icons.arrow_back_ios, - color: Colors.grey[400], - ), - ), - ) - ], - ), - ), - ], - ); - }, - ), - ), - if (constraints.maxHeight / constraints.maxWidth > 17 / 9) - Hero( - tag: 'bottom', - createRectTween: (a, b) => RectTween(begin: a, end: b), - child: Material( - color: Colors.transparent, - child: StreamBuilder( - stream: cardSwiperController.asStream, - builder: (context, snapshot) { - return AnimatedContainer( - duration: Duration(milliseconds: 200), - height: activeCard == null ? 0 : 90.h, - padding: EdgeInsets.only( - left: 10.0.w, - right: 10.0.w, - bottom: 10.0.h, - top: 10.0.h, - ), - child: Row( - children: [ - Expanded( - flex: 1, - child: GestureDetector( - onTap: () async { - if (activeCard != null) - await locator.favoriteCardsController - .switchFavoriteCard(activeCard!); - }, - child: StreamBuilder( - stream: locator - .favoriteCardsController.asStream, - initialData: locator - .favoriteCardsController.state, - builder: (context, snapshot) { - return Container( - alignment: Alignment.center, - decoration: BoxDecoration( - color: Theme.of(context) - .scaffoldBackgroundColor - .lighten(0.05), - border: Border.all( - color: borderGray), - borderRadius: - BorderRadius.circular(16.0), - ), - padding: EdgeInsets.all(10.0.w), - child: Image.asset( - locator.favoriteCardsController - .isFavoriteCard( - activeCard?.id, - ) - ? "icons/heart_fill.png" - : "icons/heart.png", - width: 29.w, - height: 29.h, - color: locator - .favoriteCardsController - .isFavoriteCard( - activeCard?.id, - ) - ? Colors.red - : Colors.grey, - ), - ); - }), - ), - ), - SizedBox( - width: 10.0.w, - ), - Expanded( - flex: 3, - child: InkWell( - onTap: () async { - cardSwiperController.swipe( - direction: CardSwiperState.swipeLeft, - ); - }, - child: Container( - alignment: Alignment.center, - decoration: BoxDecoration( - color: Theme.of(context) - .scaffoldBackgroundColor - .lighten(0.05), - border: Border.all(color: borderGray), - borderRadius: - BorderRadius.circular(16.0), - ), - padding: EdgeInsets.all(10.0.w), - child: Text( - 'Дальше', - style: TextStyle( - fontSize: 25.sp, - fontWeight: FontWeight.w700, - height: 0.85, - ), - ), - ), - ), - ), - ], - ), - ); - }), - ), - ) - ], - ); - }), - ), - ), - ); - } - - @override - void initState() { - super.initState(); - shuffledCards = widget.cards.toList(); - if (widget.shuffle) { - shuffledCards..shuffle(); - } - if (widget.startCardId != null) { - final startCardIndex = shuffledCards.indexWhere( - (card) => card.id == widget.startCardId, - ); - - if (startCardIndex >= 0) { - shuffledCards = [ - ...shuffledCards.sublist(startCardIndex), - ...shuffledCards.sublist(0, startCardIndex), - ]; - } else { - shuffledCards = []; - } - } else { - shuffledCards = widget.cards.toList(); - } - _sub = cardSwiperController.asStream.distinct().listen(playSound); - } - - @override - void dispose() { - _sub?.cancel().then((_) => _sub = null); - // cardSwiperController.removeListener(cardSwiperListener); - cardSwiperController.dispose(); - super.dispose(); - } -} diff --git a/lib/widgets/card_game/card_game_swipe_helper.dart b/lib/widgets/card_game/card_game_swipe_helper.dart deleted file mode 100644 index 46a11b7..0000000 --- a/lib/widgets/card_game/card_game_swipe_helper.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../features/card_swiper/src/card_swiper_controller.dart'; - -class CardGameSwipeHelper extends StatelessWidget { - final CardSwiperController cardSwiperController; - - const CardGameSwipeHelper(this.cardSwiperController); - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context).textTheme; - - return Container( - padding: const EdgeInsets.only( - left: 16.0, - right: 16.0, - bottom: 0.0, - ), - child: StreamBuilder( - stream: cardSwiperController.asStream, - builder: (context, snapshot) { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - Icon( - Icons.circle, - color: Colors.green, - ), - SizedBox( - width: 4.0, - ), - Text( - 'Помню ${cardSwiperController.swipedLeftIds.isEmpty ? '' : cardSwiperController.swipedLeftIds.length}', - style: theme.labelMedium?.copyWith( - color: Colors.green, - fontWeight: FontWeight.w600, - ), - ) - ], - ), - Row( - children: [ - Text( - 'Повторить ${cardSwiperController.swipedRightIds.isEmpty ? '' : cardSwiperController.swipedRightIds.length}', - style: theme.labelMedium?.copyWith( - color: Colors.yellow[600], - fontWeight: FontWeight.w600, - ), - ), - SizedBox( - width: 4.0, - ), - Icon( - Icons.circle, - color: Colors.yellow[600], - ), - ], - ), - ], - ); - } - ), - ); - } -} diff --git a/lib/widgets/card_pack/available_pack.dart b/lib/widgets/card_pack/available_pack.dart deleted file mode 100644 index 53307ce..0000000 --- a/lib/widgets/card_pack/available_pack.dart +++ /dev/null @@ -1,466 +0,0 @@ -import 'dart:math'; - -import 'package:auto_route/auto_route.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:mnemo_cards/main.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart'; -import 'package:yandex_mobileads/mobile_ads.dart'; -import '../../domain/router/app_router.gr.dart'; -import '../../features/analytics/analytics.dart'; -import '../../features/yandex_ads/yandex_ads.dart'; - -import '../../di/locator.dart'; -import '../../theme/themes.dart'; -import 'card_pack_header.dart'; -import 'package:mnemo_cards/utils/string_helper.dart'; - -import 'cards_view.dart'; -import 'cards_view_controller.dart'; - -final _mainScrollController = ScrollController(); -final _reorderableTests = GlobalKey(); - -class AvailablePack extends StatelessWidget { - final CardPackDto cardPack; - late final CardsViewController _cardsViewController = - CardsViewController(cardPack); - - AvailablePack(this.cardPack); - - @override - Widget build(BuildContext context) { - Analytics.availablePackOpened(cardPack); - return Column( - children: [ - CardPackHeader.fromDto(cardPack), - Expanded( - child: ListView( - controller: _mainScrollController, - children: [ - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - Container( - decoration: BoxDecoration( - color: cardPack.color?.asColor?.withOpacity(0.1), - ), - alignment: Alignment.center, - clipBehavior: Clip.antiAlias, - child: CardsView(_cardsViewController, cardPack), - ), - Container( - height: 1, - color: cardPack.color?.asColor ?? - Colors.grey.withOpacity(0.5), - ), - SizedBox( - height: 10.h, - ), - Padding( - padding: EdgeInsets.symmetric( - horizontal: 10.w, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - StreamBuilder( - stream: _cardsViewController.asStream - .map((s) => s.expanded), - builder: (context, snapshot) { - return _DownButton( - !(snapshot.data ?? false), - () async { - if (snapshot.hasData) { - _cardsViewController.setExpanded( - !snapshot.requireData, - ); - } - }, - key: ValueKey('DownButton'), - ); - }), - _Button('shuffle.png', () async { - final cards = _cardsViewController.state.packCards; - if (cards.isEmpty) return; - final index = (_cardsViewController.offsetCards + - Random().nextInt( - (cards.length / 2).ceil(), - ) + - 3) % - cards.length; - await _cardsViewController.animateTo(index); - AutoRouter.of(context).push( - PageRouteInfo( - CardGamePage.name, - args: CardGamePageArgs( - cards: cards, - color: cardPack.color?.asColor, - startCardId: cards[index].id, - infinite: true, - shuffle: true, - // id: cardPack.dto.id.toString(), - ), - ), - ); - }), - // _Button('chat.png', () {}), - StreamBuilder( - stream: _cardsViewController.asStream, - builder: (context, snapshot) { - return _Button( - (snapshot.data?.favorite ?? false) - ? 'heart_fill.png' - : 'heart.png', () { - if (snapshot.hasData) { - _cardsViewController.setFavorite( - !snapshot.requireData.favorite, - ); - } - }); - }), - ], - ), - ), - ], - ); - }), - if (locator.userManager.user?.subscriptionFeatures - .contains(SubscriptionFeatureEnum.ads) != - true) - _AdTile(), - _TestsSection(cardPack.id.toString()), - ], - ), - ), - Hero( - tag: 'bottom', - child: SizedBox( - height: 0, - width: 300.w, - ), - ), - ], - ); - } -} - -class _TestsSection extends StatelessWidget { - final String packId; - - const _TestsSection(this.packId); - - @override - Widget build(BuildContext context) { - final order = (globalSharedPreferences - .getString('tests_order_${packId}') - ?.decodeList() ?? - []) - .toList(); - - void saveOrder() { - globalSharedPreferences.setString( - 'tests_order_${packId}', - order.encode(), - ); - } - - return StreamBuilder( - stream: locator.testManager.loadTestsStream(packId: packId), - builder: (context, snapshot) { - final tests = snapshot.data ?? []; - if (tests.isEmpty) { - return SizedBox.shrink(); - } - - if (order.isEmpty) { - order.addAll(tests.map((t) => t.name)); - saveOrder(); - } - return FadeInWidget( - animationDuration: Duration(milliseconds: 400), - child: StatefulBuilder(builder: (context, setState) { - tests..sortInOrder(order, (t) => t.name); - return ReorderableListView.builder( - key: _reorderableTests, - itemExtent: 100.h + 2 * 5.h, - padding: EdgeInsets.symmetric( - horizontal: 10.w, - vertical: 5.h, - ), - shrinkWrap: true, - scrollController: _mainScrollController, - // physics: NeverScrollableScrollPhysics(), - proxyDecorator: (_, index, animation) { - final dto = tests[index]; - return Material( - elevation: 5, - borderRadius: BorderRadius.circular(16), - color: Colors.transparent, - child: _PackTestButton( - title: dto.name, - subtitle: null, - stats: dto.statistics, - color: dto.color?.asColor ?? borderGray, - time: dto.time, - timeSubtitle: dto.timeSubtitle, - onTap: () {}, - ), - ); - }, - onReorder: (int from, int to) { - if (to - from > 1) { - to--; - } - order.move(from, to); - saveOrder(); - setState(() {}); - }, - itemCount: tests.length, - itemBuilder: (context, index) { - final dto = tests[index]; - return Padding( - key: ValueKey(dto.id), - padding: EdgeInsets.symmetric( - vertical: 5.0.h, - ), - child: _PackTestButton( - title: dto.name, - subtitle: null, - stats: dto.statistics, - color: dto.color?.asColor ?? borderGray, - time: dto.time, - timeSubtitle: dto.timeSubtitle, - onTap: () { - AutoRouter.of(context).push( - PageRouteInfo(TestPage.name, - args: TestPageArgs(testId: dto.id!)), - ); - }, - ), - ); - }, - ); - }), - ); - }, - ); - } -} - -class _AdTile extends StatelessWidget { - const _AdTile(); - - @override - Widget build(BuildContext context) { - if (locator.userManager.userStateHolder.user?.subscription == true) - return SizedBox.shrink(); - if (kIsWeb) return SizedBox.shrink(); - return LayoutBuilder(builder: (context, constraints) { - final banner = YandexAds.createBanner( - BoxConstraints.tight(Size(constraints.maxWidth - 16, 80 - 8)), - id: 'R-M-3761037-2'); - return FadeInWidget( - future: banner.loadAd(), - animationDuration: Duration(milliseconds: 1000), - child: Container( - height: 80, - margin: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 8.0), - decoration: BoxDecoration( - border: Border.all( - color: Theme.of(context).scaffoldBackgroundColor, - width: 4.0, - ), - borderRadius: BorderRadius.circular(12.0), - ), - child: Center( - child: AdWidget(bannerAd: banner), - ), - ), - ); - }); - } -} - -class _Button extends StatelessWidget { - final String asset; - final VoidCallback onTap; - - _Button(this.asset, this.onTap); - - @override - Widget build(BuildContext context) { - return InkWell( - onTap: onTap, - child: Container( - width: 110.w, - height: 60.h, - decoration: BoxDecoration( - color: Theme.of(context).scaffoldBackgroundColor, - border: Border.all(color: borderGray), - borderRadius: BorderRadius.circular(16.0), - ), - alignment: Alignment.center, - child: Image.asset( - 'icons/$asset', - width: 29.w, - color: Theme.of(context).colorScheme.primary, - // height: 29.h, - fit: BoxFit.scaleDown, - ), - ), - ); - } -} - -class _DownButton extends StatelessWidget { - final VoidCallback onTap; - final bool isDown; - - _DownButton(bool this.isDown, this.onTap, {super.key}); - - @override - Widget build(BuildContext context) { - return InkWell( - onTap: onTap, - child: Container( - width: 110.w, - height: 60.h, - decoration: BoxDecoration( - color: Theme.of(context).scaffoldBackgroundColor, - border: Border.all(color: borderGray), - borderRadius: BorderRadius.circular(16.0), - ), - alignment: Alignment.center, - child: AnimatedRotation( - duration: Duration(milliseconds: 300), - turns: isDown ? 0.0 : 0.5, - child: Image.asset( - 'icons/down.png', - width: 29.w, - color: Theme.of(context).colorScheme.primary, - // height: 29.h, - fit: BoxFit.scaleDown, - ), - ), - ), - ); - } -} - -class _PackTestButton extends StatelessWidget { - final VoidCallback onTap; - final String title; - final String? subtitle; - final String? time; - final String? timeSubtitle; - final Color color; - final TestStatisticsDto? stats; - - _PackTestButton({ - required this.title, - required this.onTap, - required this.color, - this.subtitle, - this.time, - this.timeSubtitle, - this.stats, - }); - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: onTap, - child: Container( - height: 80.h, - alignment: Alignment.center, - decoration: BoxDecoration( - color: Theme.of(context).scaffoldBackgroundColor, - border: Border.all(color: borderGray), - borderRadius: BorderRadius.circular(16.0), - ), - padding: EdgeInsets.only( - top: 12.0.h, - left: 10.0.w, - right: 10.0.w, - bottom: 20.h, - ), - child: Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - title, - style: TextStyle( - fontSize: 25.sp, - fontWeight: FontWeight.w700, - height: 0.85, - ), - ), - if (subtitle != null) - Padding( - padding: const EdgeInsets.only(top: 8.0), - child: Text( - subtitle!, - style: TextStyle( - fontSize: 16.sp, - fontWeight: FontWeight.w500, - height: 0.85, - ), - ), - ), - Spacer(), - if (stats != null) - Padding( - padding: const EdgeInsets.only(right: 50.0), - child: Row( - children: [ - Expanded( - child: HorizontalProgressWidget( - stats!.words.correct.round(), - stats!.words.total.round(), - color, - borderColor: color, - ), - ), - ], - ), - ), - ], - ), - ), - Container( - width: 75.w, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (time != null) - Text( - time!, - style: TextStyle( - fontSize: 40.sp, - fontWeight: FontWeight.w700, - height: 0.85), - ), - if (timeSubtitle != null) - Text( - timeSubtitle!, - style: TextStyle( - fontSize: 24.sp, - fontWeight: FontWeight.w700, - height: 0.85), - ), - ], - ), - ), - ], - ), - ), - ); - } -} diff --git a/lib/widgets/card_pack/buy_pack_page.dart b/lib/widgets/card_pack/buy_pack_page.dart deleted file mode 100644 index cb11ce9..0000000 --- a/lib/widgets/card_pack/buy_pack_page.dart +++ /dev/null @@ -1,231 +0,0 @@ -import 'dart:convert'; -import 'dart:developer'; -import 'package:mnemo_cards/flags.dart'; -import 'package:mnemo_cards/utils/string_helper.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart'; -import '../../features/analytics/analytics.dart'; -import '../../main.dart'; - -import '../../di/locator.dart'; -import '../../domain/router/app_router.dart'; -import '../../theme/themes.dart'; -import 'card_pack_header.dart'; - -class BuyPack extends StatelessWidget { - final CardPackBuyDto dto; - - const BuyPack(this.dto); - - @override - Widget build(BuildContext context) { - bool loading = false; - Analytics.buyPackOpened(dto); - return Container( - // color: dto.color?.asColor, - child: StatefulBuilder(builder: (context, setState) { - if (loading) { - return _Loading(dto); - } - return Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - CardPackHeader(dto.title, dto.subtitle, dto.color?.asColor), - Expanded( - child: Column( - // physics: PageScrollPhysics(), - // shrinkWrap: true, - children: [ - Padding( - padding: EdgeInsets.symmetric( - horizontal: 10.0.w, vertical: 18.h), - child: LayoutBuilder(builder: (context, constraints) { - final cardWidth = constraints.maxWidth / 3; - final imageWidth = cardWidth * 0.9; - return SizedBox( - height: 150.h, - child: ListView.builder( - scrollDirection: Axis.horizontal, - itemCount: dto.cards.length, - itemExtent: cardWidth, - itemBuilder: (BuildContext context, int index) { - final card = dto.cards[index]; - if (card.image != null) { - return Container( - alignment: Alignment.center, - width: imageWidth, - margin: EdgeInsets.symmetric( - horizontal: cardWidth - imageWidth - 1, - vertical: 1, - ), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(8.0), - border: Border.all( - color: borderGray, - ), - ), - child: Image.memory(base64Decode(card.image!)), - ); - } - return SizedBox.shrink(); - }, - ), - ); - }), - ), - SizedBox( - height: 32.h, - ), - ...?dto.items?.build(), - ], - ), - ), - // _BuyButton( - // dto, - // (v) => setState(() { - // loading = v != null; - // }), - // ), - SizedBox( - height: 16.h, - ), - ], - ); - }), - ); - } -} - -class _Loading extends StatelessWidget { - final CardPackBuyDto dto; - - _Loading(this.dto); - - @override - Widget build(BuildContext context) { - final memoryImage = locator.imagesHolder.get('pack_${dto.id}'); - return Column( - children: [ - CardPackHeader(dto.title, dto.subtitle, dto.color?.asColor), - Expanded( - child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (memoryImage != null) - Padding( - padding: const EdgeInsets.all(8.0), - child: Image( - image: memoryImage, - width: 100, - ), - ), - Text('Загрузка...'), - ], - ), - ), - ), - ], - ); - } -} - -class _BuyButton extends StatelessWidget { - final CardPackBuyDto dto; - final Function(String? loadingText) callback; - - _BuyButton(this.dto, this.callback); - - @override - Widget build(BuildContext context) { - return Padding( - padding: EdgeInsets.symmetric( - vertical: 5.0.h, - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - mainAxisSize: MainAxisSize.max, - children: [ - TextButton( - style: ButtonStyle( - overlayColor: - WidgetStateColor.resolveWith((states) => Colors.white), - ), - onPressed: () async { - if (locator.userManager.hasUser) { - Analytics.buyPackPressed(dto, PaymentSystem.yookassa); - await locator.purchaseManager.initPurchaseFromDto( - dto: dto, - paymentSystem: PaymentSystem.yookassa, - ); - // if (result) { - // _updatePack(); - // } - } else { - AppRouter.openAuth(); - } - }, - child: Text( - 'Оплатить российской картой', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400, - decoration: TextDecoration.underline, - ), - ), - ), - MnemoCardsButton( - onTap: () async { - if (locator.userManager.hasUser) { - Analytics.buyPackPressed(dto, androidPaymentSystem); - await locator.purchaseManager.initPurchaseFromDto( - dto: dto, - paymentSystem: androidPaymentSystem, - ); - } else { - AppRouter.openAuth(); - } - }, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Оплатить через', - style: TextStyle( - fontSize: 18.sp, - fontWeight: FontWeight.w700, - height: 1.0, - ), - ), - Text( - 'Google pay', - style: TextStyle( - fontSize: 18.sp, - fontWeight: FontWeight.w700, - height: 0.85, - ), - ), - SizedBox( - height: 10.0.h, - ), - if (dto.price != null) - Text( - dto.price!, - style: TextStyle( - fontSize: 17.sp, - fontWeight: FontWeight.w400, - height: 1.0, - ), - ), - ], - ), - height: 120.h, - ) - ], - ), - ); - } -} diff --git a/lib/widgets/card_pack/card_pack_header.dart b/lib/widgets/card_pack/card_pack_header.dart deleted file mode 100644 index 31960f2..0000000 --- a/lib/widgets/card_pack/card_pack_header.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:mnemo_cards/utils/string_helper.dart'; -import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart'; - -class CardPackHeader extends StatelessWidget { - final String? title; - final String? subtitle; - final Color? color; - - CardPackHeader(this.title, this.subtitle, this.color); - - CardPackHeader.fromDto(CardPackDto? dto) - : title = dto?.title, - subtitle = dto?.subtitle, - color = dto?.color?.asColor; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Header( - title, - subtitle: subtitle, - popText: 'к темам', - hasPopButton: true, - color: null,// color?.withOpacity(0.5), - ), - Container( - color: null, //color?.withOpacity(0.5), - height: 25.h, - ), - Container( - color: color ?? Colors.grey.withOpacity(0.5), - height: 1, - ) - ], - ); - } -} diff --git a/lib/widgets/card_pack/card_pack_page.dart b/lib/widgets/card_pack/card_pack_page.dart deleted file mode 100644 index fbd7392..0000000 --- a/lib/widgets/card_pack/card_pack_page.dart +++ /dev/null @@ -1,77 +0,0 @@ -import 'dart:convert'; - -import 'package:auto_route/annotations.dart'; -import 'package:auto_route/auto_route.dart'; -import 'package:flutter/material.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import '../../features/analytics/analytics.dart'; - -import '../../di/locator.dart'; -import 'available_pack.dart'; -import 'buy_pack_page.dart'; -import 'card_pack_header.dart'; -import 'loading_pack.dart'; - -@RoutePage() -class CardPackPage extends StatefulWidget { - final CardPackPreviewDto previewDto; - - const CardPackPage(this.previewDto, {super.key}); - - @override - State createState() => _CardPackPage(); -} - -class _CardPackPage extends State { - @override - Widget build(BuildContext context) { - return Scaffold( - body: SafeArea( - child: StreamBuilder( - stream: locator.packManager.getCardPackStream( - widget.previewDto, - ), - initialData: null, - builder: (context, snapshot) => AnimatedSwitcher( - duration: const Duration(milliseconds: 300), - child: _buildPackPage(snapshot), - ), - ), - ), - ); - } - - Widget _buildPackPage(AsyncSnapshot snapshot) { - if (!snapshot.hasData) return SizedBox.shrink(); - final response = snapshot.requireData; - if (response is CardPackLoadingDto) { - return Loading(response); - } else if (response is CardPackBuyDto) { - return BuyPack(response); - } else if (response is CardPackDto) { - return AvailablePack(response); - } - Analytics.crashlyticsError( - data: {'response': jsonEncode(snapshot.data?.toJson() ?? '{}')}, - message: 'cant load pack ${widget.previewDto.id}', - ); - - return Column( - children: [ - CardPackHeader.fromDto(null), - Expanded( - child: Center( - child: Text( - 'Ошибка, попробуйте еще раз или попробуйте обновить приложение', - ), - ), - ), - ], - ); - } - - @override - void initState() { - super.initState(); - } -} diff --git a/lib/widgets/card_pack/cards_view.dart b/lib/widgets/card_pack/cards_view.dart deleted file mode 100644 index 3db00a0..0000000 --- a/lib/widgets/card_pack/cards_view.dart +++ /dev/null @@ -1,274 +0,0 @@ -import 'package:auto_route/auto_route.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:mnemo_cards/features/packs/images_holder.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart' - as frontend; -import 'package:reorderables/reorderables.dart'; -import 'package:rxdart/rxdart.dart'; - -import '../../utils/string_helper.dart'; -import '../../domain/router/app_router.gr.dart'; -import '../../main.dart'; -import '../game_card_widget.dart'; -import 'cards_view_controller.dart'; - -class CardsView extends StatefulWidget { - final CardPackDto cardPack; - final CardsViewController controller; - final List cardsOrder = []; - - CardsView(this.controller, this.cardPack); - - @override - State createState() => _CardsViewState(); -} - -class _CardsViewState extends State - with SingleTickerProviderStateMixin { - CardPackDto get cardPack => widget.cardPack; - - CardsViewController get controller => widget.controller; - var _subscription = CompositeSubscription(); - - late AnimationController _animationController; - - bool expanded = false; - bool favorite = false; - - final wrapKey = GlobalKey(); - - void _onStateChanged(CardsViewState state) async { - favorite = state.favorite; - if (state.expanded != expanded) { - expanded = state.expanded; - if (expanded) { - await controller.animateTo(0); - _animationController.animateTo( - 1.0, - duration: - Duration(milliseconds: (100 * state.packCards.length / 3).ceil()), - ); - } else { - _animationController.animateBack( - 0.0, - duration: - Duration(milliseconds: (100 * state.packCards.length / 3).ceil()), - ); - } - } - } - - @override - void didUpdateWidget(CardsView oldWidget) { - super.didUpdateWidget(oldWidget); - _subscription.cancel(); - _subscription = CompositeSubscription(); - controller.asStream.listen(_onStateChanged).addTo(_subscription); - } - - @override - void initState() { - super.initState(); - expanded = controller.state.expanded; - favorite = controller.state.favorite; - _animationController = AnimationController(vsync: this); - _subscription.cancel(); - _subscription = CompositeSubscription(); - controller.asStream.listen(_onStateChanged).addTo(_subscription); - } - - @override - void dispose() { - _subscription.cancel(); - _animationController.dispose(); - super.dispose(); - } - - void onCardTap(List cards, GameCardDto card) async { - final playing = appRouter.push( - PageRouteInfo( - CardGamePage.name, - args: CardGamePageArgs( - cards: cards, - color: cardPack.color?.asColor, - startCardId: card.id, - infinite: true, - shuffle: false, - // id: cardPack.dto.id.toString(), - ), - ), - ); - final id = await playing; - // if (id is int) { - // final index = cardPack.cards.indexWhere((c) => c.id == id); - // if (index >= 0) { - // print('jump to $index'); - // controller.jumpTo(index); - // } - // } - } - - @override - Widget build(BuildContext context) { - final cardWidth = MediaQuery.of(context).size.width / 3; - final imageWidth = cardWidth * 0.93; - - return StreamBuilder( - stream: controller.cardsStream, - builder: (context, snapshot) { - if (!snapshot.hasData) { - return SizedBox(); - } - final packCards = snapshot.requireData; - - if (packCards.isEmpty) { - return _NoCards( - favorite: favorite, - height: cardWidth * 4 / 3, - ); - } - - controller.imageWidth = imageWidth; - final animation = _animationController.view; - return AnimatedBuilder( - animation: animation, - builder: (context, child) { - return Column( - children: [ - SingleChildScrollView( - controller: controller.cardsScrollController, - scrollDirection: - animation.value > 0 ? Axis.vertical : Axis.horizontal, - physics: animation.value > 0 - ? NeverScrollableScrollPhysics() - : null, - child: Container( - alignment: Alignment.topCenter, - height: cardWidth * 4 / 3 + - ((packCards.length / 3).ceil() * 4 / 3 * cardWidth - - cardWidth * 4 / 3) * - animation.value, - child: Builder(builder: (context) { - return ReorderableWrap( - key: wrapKey, - onReorder: (int from, int to) { - controller.onOrderChanged(from, to); - }, - buildDraggableFeedback: - frontend.FeedbackBuilder.simple, - padding: EdgeInsets.symmetric( - horizontal: (cardWidth - imageWidth - 1), - vertical: (cardWidth - imageWidth - 1), - ), - runSpacing: (cardWidth - imageWidth - 1) * 2, - spacing: (cardWidth - imageWidth - 1) * 2, - children: List.generate(packCards.length, (index) { - final card = packCards[index]; - final memoryImage = card.memoryImage; - if (memoryImage == null) { - return SizedBox(); - } - return Container( - height: 2 * imageWidth - cardWidth * 2 / 3 + 2, - width: 2 * imageWidth - cardWidth + 2, - alignment: Alignment.topCenter, - child: Container( - height: imageWidth * 4 / 3, - child: _CardBuilder( - cardPack, - card, - memoryImage, - (card) => onCardTap(packCards, card), - ), - ), - ); - }), - ); - }), - ), - ), - ], - ); - }); - }); - } -} - -class _CardBuilder extends StatelessWidget { - final GameCardDto card; - final CardPackDto cardPack; - final MemoryImage image; - final Function(GameCardDto) onTap; - - _CardBuilder( - this.cardPack, - this.card, - this.image, - this.onTap, - ); - - @override - Widget build(BuildContext context) { - return GestureDetector( - key: ObjectKey(card), - onTap: () => onTap(card), - child: Hero( - key: ValueKey('${card.id}'), - tag: card.id, - child: GameCardDecoration( - alpha: 0.0, - borderColor: cardPack.color?.asColor, - child: FrontCard( - card, - image, - alpha: 0, - ), - ), - createRectTween: (a, b) => RectTween(begin: a, end: b), - flightShuttleBuilder: (flightC, a, dir, fromC, toC) { - return AnimatedBuilder( - animation: a, - builder: (context, child) => GameCardDecoration( - alpha: a.value, - borderColor: cardPack.color?.asColor, - child: FrontCard( - card, - card.memoryImage!, - alpha: a.value, - ), - ), - ); - }, - ), - ); - } -} - -class _NoCards extends StatelessWidget { - final bool favorite; - final double height; - - _NoCards({ - required this.favorite, - required this.height, - }); - - @override - Widget build(BuildContext context) { - return Container( - height: height, - alignment: Alignment.center, - child: Text( - favorite ? 'Нет любимых карточек' : 'Пока нет карточек', - style: TextStyle( - fontSize: 16.sp, - fontWeight: FontWeight.w700, - ), - textAlign: TextAlign.center, - ), - ); - } -} diff --git a/lib/widgets/card_pack/cards_view_controller.dart b/lib/widgets/card_pack/cards_view_controller.dart deleted file mode 100644 index 2f18f6a..0000000 --- a/lib/widgets/card_pack/cards_view_controller.dart +++ /dev/null @@ -1,197 +0,0 @@ -import 'dart:async'; -import 'dart:math'; - -import 'package:flutter/widgets.dart'; -import 'package:mnemo_cards/main.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:rxdart/rxdart.dart'; - -import '../../di/locator.dart'; - -class CardsViewController extends ChangeNotifier { - bool _expanded = false; - bool _favorite = false; - double imageWidth = 0.0; - List cards = []; - List allCardsOrder = []; - List favoriteCardsOrder = []; - - List get cardsOrder => _favorite ? favoriteCardsOrder : allCardsOrder; - - final CardPackDto cardPack; - - final ScrollController _cardsScrollController = - ScrollController(keepScrollOffset: true); - - ScrollController get cardsScrollController => _cardsScrollController; - - StreamController _streamController = - StreamController.broadcast(); - - Stream get asStream => - _streamController.stream.startWith(state).distinct(); - - Stream get expandedStream => asStream.map((s) => s.expanded).distinct(); - - Stream get favoriteStream => asStream.map((s) => s.favorite).distinct(); - - Stream> get cardsStream => - asStream.map((s) => s.packCards).distinct((p, n) => - (p.length == n.length) && - p.indexed.every((p) => n[p.$1].id == p.$2.id)); - - CardsViewState get state => CardsViewState( - expanded: _expanded, - favorite: _favorite, - packCards: cards, - ); - - List _filterPackCards() => cardPack.cards - .where( - (card) => - !_favorite || - locator.favoriteCardsController.isFavoriteCard(card.id), - ) - .toList(); - - void setExpanded(bool expanded) { - if (cards.isNotEmpty) { - _expanded = expanded; - notifyListeners(); - } - } - - Future _saveCardsOrder() async { - if (_favorite) { - await globalSharedPreferences.setString( - 'sort_fav_${cardPack.id}', - favoriteCardsOrder.encode(), - ); - } else { - await globalSharedPreferences.setString( - 'sort_${cardPack.id}', - allCardsOrder.encode(), - ); - } - } - - void _loadCardsOrder() { - favoriteCardsOrder = globalSharedPreferences - .getString( - 'sort_fav_${cardPack.id}', - ) - ?.decodeList() ?? - []; - allCardsOrder = globalSharedPreferences - .getString( - 'sort_${cardPack.id}', - ) - ?.decodeList() ?? - []; - if (allCardsOrder.isEmpty) { - allCardsOrder = cardPack.cards.map((c) => c.id).toList(); - } - if (favoriteCardsOrder.isEmpty) { - favoriteCardsOrder = allCardsOrder.toList(); - } - } - - void setCardsOrder(List order) { - if (_favorite) { - favoriteCardsOrder = order.toList(); - } else { - allCardsOrder = order.toList(); - } - _saveCardsOrder(); - _updateCards(); - } - - void onOrderChanged(int from, int to) { - if (_favorite) { - favoriteCardsOrder.move(from, to); - } else { - allCardsOrder.move(from, to); - } - _saveCardsOrder(); - _updateCards(); - } - - void setFavorite(bool favorite) { - _favorite = favorite; - _updateCards(); - notifyListeners(); - } - - void _addToStream() { - _streamController.add(state); - } - - double get offset => _cardsScrollController.offset; - - int get offsetCards => (_cardsScrollController.offset / imageWidth).floor(); - - void toggleExpanded() => setExpanded(!_expanded); - - CardsViewController(this.cardPack) { - addListener(_addToStream); - _loadCardsOrder(); - _updateCards(); - } - - void _updateCards() { - cards = _filterPackCards(); - cards - ..sort((p, n) { - final pIndex = cardsOrder.indexOf(p.id); - final nIndex = cardsOrder.indexOf(n.id); - if (pIndex == -1) { - return nIndex == -1 ? p.id.compareTo(n.id) : 1; - } else if (nIndex == -1) { - return -1; - } - return pIndex.compareTo(nIndex); - }); - notifyListeners(); - } - - Future animateTo(int index) { - final millis = - ((_cardsScrollController.offset / imageWidth - index).abs() * 30.0) - .ceil(); - return _cardsScrollController.animateTo( - imageWidth * index, - duration: Duration(milliseconds: max(millis, 100)), - curve: Curves.easeInOut, - ); - } - - void jumpTo(int index) { - return _cardsScrollController.jumpTo( - imageWidth * index, - ); - } -} - -class CardsViewState { - final bool expanded; - final bool favorite; - final List packCards; - - CardsViewState({ - required this.expanded, - required this.favorite, - required this.packCards, - }); - - @override - int get hashCode => expanded.hashCode ^ expanded.hashCode ^ packCards.length; - - @override - bool operator ==(Object other) { - return (other is CardsViewState) && - expanded == other.expanded && - favorite == other.favorite && - (packCards.length == other.packCards.length) && - packCards.indexed.every((p) => other.packCards[p.$1].id == p.$2.id); - } -} diff --git a/lib/widgets/card_pack/heart_button.dart b/lib/widgets/card_pack/heart_button.dart deleted file mode 100644 index c9c1974..0000000 --- a/lib/widgets/card_pack/heart_button.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:flutter/cupertino.dart'; - -class HeartButton extends StatelessWidget { - @override - Widget build(BuildContext context) { - // TODO: implement build - throw UnimplementedError(); - } - -} \ No newline at end of file diff --git a/lib/widgets/enter_promo_code_widget.dart b/lib/widgets/enter_promo_code_widget.dart deleted file mode 100644 index 302493c..0000000 --- a/lib/widgets/enter_promo_code_widget.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart'; - -import '../di/locator.dart'; - -class MobileEnterPromoCodeWidget extends StatelessWidget { - - @override - Widget build(BuildContext context) { - return EnterPromoCodeWidget(onApplyPromoCode: onApplyPromoCode); - } - - Future<(bool, String?)> onApplyPromoCode(String code) async { - final result = await locator.userManager.applyPromocode(code); - return (result.success ?? false, result.message); - } - - const MobileEnterPromoCodeWidget(); -} \ No newline at end of file diff --git a/lib/widgets/game_card_widget.dart b/lib/widgets/game_card_widget.dart deleted file mode 100644 index 79832d7..0000000 --- a/lib/widgets/game_card_widget.dart +++ /dev/null @@ -1,390 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:mnemo_cards/di/locator.dart'; -import 'package:mnemo_cards/features/yandex_ads/yandex_ads.dart'; -import 'package:mnemo_cards/managers/audio_player.dart'; -import 'package:mnemo_cards/utils/color_helper.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart'; -import 'package:yandex_mobileads/mobile_ads.dart'; - -import '../features/card_flipper/flip_card.dart'; -import '../features/card_flipper/flip_card_controllers.dart'; -import '../features/card_flipper/flip_side.dart'; -import '../theme/themes.dart'; - -class GameCardWidget extends StatefulWidget { - static const borderRadius = 32.0; - - final GameCardDto card; - final MemoryImage? image; - final bool showFront; - final bool isFirstCard; - final FlipCardController? flipCardController; - final Color? backColor; - - const GameCardWidget( - this.card, - this.image, - this.flipCardController, - this.showFront, - this.isFirstCard, - this.backColor, { - super.key, - }); - - @override - State createState() => _GameCardWidgetState(); -} - -class GameCardDecoration extends StatelessWidget { - final Widget child; - final double alpha; - final Color? borderColor; - - GameCardDecoration({ - required this.child, - this.alpha = 1.0, - this.borderColor, - }); - - @override - Widget build(BuildContext context) => Container( - padding: EdgeInsets.all(8.0 * alpha), - margin: EdgeInsets.only( - left: 10.0 * alpha, - right: 10.0 * alpha, - bottom: 2.0 * alpha, - top: 0.0 * alpha, - ), - decoration: BoxDecoration( - // color: Theme.of(context).scaffoldBackgroundColor, - color: Theme.of(context).scaffoldBackgroundColor.lighten(0.05), - // .withOpacity(alpha), - boxShadow: [ - BoxShadow( - color: Theme.of(context) - .colorScheme - .primary - .withOpacity(0.25 * alpha), - blurRadius: 4.0 * alpha, - ), - ], - borderRadius: BorderRadius.circular( - 12 + (GameCardWidget.borderRadius - 12) * alpha), - border: Border.all( - color: (borderColor ?? borderGray).withOpacity( - Theme.of(context).brightness == Brightness.dark - ? 1.0 - : 1.0 - alpha * alpha, - ), - ), - ), - child: ClipRRect( - borderRadius: BorderRadius.circular( - 8 + (GameCardWidget.borderRadius - 8) * alpha), - child: child, - ), - ); -} - -class _GameCardWidgetState extends State { - FlipCardController? get _flipCardController => widget.flipCardController; - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context).textTheme; - print('${widget.card.original}'); - - final frontWidget = FrontCard(widget.card, widget.image); - final backWidget = _BackWidget( - widget.card, - widget.backColor, - ); - Duration? dragStart; - Duration? dragUpdate; - - return GameCardDecoration( - child: Material( - color: Colors.transparent, - child: GestureDetector( - onVerticalDragStart: (start) { - dragStart = start.sourceTimeStamp; - }, - onVerticalDragUpdate: (update) { - dragUpdate = update.sourceTimeStamp; - }, - onVerticalDragEnd: (update) { - final pps = update.velocity.pixelsPerSecond; - if (widget.isFirstCard && - pps.dy.abs() > 2 * pps.dx.abs() && - pps.dy.abs() > 70 && - (dragStart != null && - dragUpdate != null && - (dragUpdate!.inMilliseconds - dragStart!.inMilliseconds) > - 50)) { - _flipCardController?.flipcard(); - } - }, - behavior: HitTestBehavior.deferToChild, - onDoubleTap: () async { - await locator.favoriteCardsController - .switchFavoriteCard(widget.card); - }, - child: (widget.isFirstCard) - ? FlipCard( - rotateSide: RotateSide.left, - onTapFlipping: false, - axis: FlipAxis.horizontal, - controller: _flipCardController!, - frontWidget: frontWidget, - backWidget: backWidget, - ) - : widget.showFront - ? frontWidget - : backWidget, - ), - ), - ); - } -} - -class _BackWidget extends StatelessWidget { - final GameCardDto card; - final Color? _backColor; - - const _BackWidget( - this.card, - this._backColor, - ); - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context).textTheme; - final backColor = _backColor ?? Colors.lightBlueAccent[200]; - return Container( - padding: const EdgeInsets.all(8.0), - decoration: BoxDecoration( - color: backColor, - boxShadow: const [BoxShadow()], - borderRadius: BorderRadius.circular(32.0), - ), - child: Center( - child: MnemoText( - card.back?.isNotEmpty == true ? card.back : card.translation ?? '', - textStyle: theme.displayMedium!.copyWith(color: Colors.white), - maxLines: 2, - softWrap: true, - ), - ), - ); - } -} - -class FrontCard extends StatelessWidget { - final GameCardDto card; - final MemoryImage? image; - final double alpha; - - FrontCard(this.card, this.image, {super.key, this.alpha = 1.0}); - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context).textTheme; - return StatefulBuilder(builder: (context, setState) { - return AbsorbPointer( - absorbing: alpha != 1, - child: LayoutBuilder(builder: (context, constrains) { - const targetTextAr = 10 / 9; - const targetImageAr = 16 / 9; - double ar = constrains.maxHeight / constrains.maxWidth; - if (constrains.maxHeight.isInfinite) { - ar = 16 / 9; - } - final textScale = min(1.0, ar / targetTextAr) * alpha * alpha; - final imageScale = - 0.9 * min(1.0, pow(ar / targetImageAr, 1.5 * alpha)); - return Material( - borderOnForeground: false, - type: MaterialType.transparency, - child: Stack( - alignment: Alignment.center, - children: [ - Padding( - padding: EdgeInsets.all(8.0 * imageScale * (alpha / 2 + 0.5)), - child: Column( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: ar > 1 - ? MainAxisAlignment.spaceEvenly - : MainAxisAlignment.spaceAround, - children: [ - Column( - children: [ - GestureDetector( - child: MnemoText( - card.original ?? '', - textStyle: TextStyle( - fontSize: 15 * textScale + 12, - fontWeight: FontWeight.w600, - ), - maxLines: 1, - ), - onTap: () async { - if (card.original != null) { - AudioPlayer.playAudio( - card.original!, - lang: 'es-ES', - ); - } - }, - ), - GestureDetector( - onTap: () async { - if (card.translation != null) { - if (card.original != null) { - AudioPlayer.playAudio( - card.translation!, - lang: 'ru', - ); - } - } - }, - child: MnemoText( - card.translation, - textStyle: TextStyle( - fontSize: 12 * textScale * alpha + 8, - fontWeight: FontWeight.w700, - color: theme.headlineSmall!.color! - .withOpacity(0.5), - ), - maxLines: null, - ), - ), - if (alpha > 0 && - ar > 1 && - card.transcription != null && - card.transcriptionMnemo != null) - Padding( - padding: EdgeInsets.only(top: 4.0.h * alpha), - child: GestureDetector( - onTap: () async { - if (card.transcription != null && - card.transcriptionMnemo != null) { - AudioPlayer.playAudio( - '${card.transcription} - ${card.transcriptionMnemo}', - lang: 'ru', - ); - } - }, - child: MnemoText( - "[${card.transcription}] - ${card.transcriptionMnemo}", - textStyle: TextStyle( - fontSize: 20 * textScale * alpha, - fontWeight: FontWeight.w700, - ), - textAlign: TextAlign.center, - maxLines: 2, - // group: smallSize, - ), - ), - ), - ], - ), - Container( - width: constrains.maxHeight / ar * imageScale, - height: constrains.maxHeight / ar * imageScale, - clipBehavior: Clip.antiAlias, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular( - GameCardWidget.borderRadius * - imageScale * - imageScale * - imageScale, - ), - ), - child: ImageFade( - key: ValueKey(card.id), - image: image, - // fix for elapsedInSeconds < 0 in debug mode - syncDuration: Duration.zero, - ), - ), - GestureDetector( - onTap: () async { - if (card.mnemo?.trim().isNotEmpty != true) { - return; - } - AudioPlayer.playAudio( - card.mnemo!.replaceAll(RegExp(r'[\]\[]'), ''), - lang: 'ru', - ); - }, - child: MnemoText( - card.mnemo?.isNotEmpty == true ? card.mnemo : ' ', - textStyle: TextStyle( - fontSize: 20 * textScale, - fontWeight: FontWeight.w700, - ), - ), - ), - ], - ), - ), - if (alpha > 0) - Align( - alignment: ar > 4 / 3 - ? Alignment.bottomCenter - : Alignment.topCenter, - child: Text( - "mnemo cards", - style: TextStyle( - fontSize: 10 * textScale, - color: Colors.grey[400], - ), - maxLines: 1, - // group: smallSize, - ), - ), - if (false && - alpha > 0 && - locator.favoriteCardsController.isFavoriteCard(card.id)) - Align( - alignment: - ar > 4 / 3 ? Alignment.bottomLeft : Alignment.topLeft, - child: Padding( - padding: EdgeInsets.all(8.0.w), - child: Image.asset( - "icons/heart.png", - width: 8.w, - height: 8.h, - ), - ), - ), - if (alpha > 0 && AudioPlayer.soundOn) - Opacity( - opacity: alpha, - child: Container( - padding: EdgeInsets.all(4.0.h * alpha), - alignment: Alignment.topRight, - child: IconButton( - icon: Image.asset( - 'icons/sound_on.png', - width: 24 * textScale, - color: Theme.of(context).colorScheme.primary, - ), - onPressed: () async { - AudioPlayer.playAudio(card.original, lang: 'es-ES'); - }, - ), - ), - ), - ], - ), - ); - }), - ); - }); - } -} diff --git a/lib/widgets/packs_menu_widget.dart b/lib/widgets/packs_menu_widget.dart deleted file mode 100644 index 04bf289..0000000 --- a/lib/widgets/packs_menu_widget.dart +++ /dev/null @@ -1,274 +0,0 @@ -import 'dart:convert'; -import 'dart:developer'; - -import 'package:auto_route/auto_route.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:mnemo_cards/features/packs/pack_manager.dart'; -import 'package:mnemo_cards/utils/string_helper.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart'; - -import '../di/locator.dart'; -import '../domain/router/app_router.dart'; -import '../domain/router/app_router.gr.dart'; -import '../managers/repository/repository.dart'; -import 'package:collection/collection.dart'; - -class PacksMenuWidget extends StatefulWidget { - final Repository repository; - final PackManager packManager; - final ScrollController? scrollController; - - const PacksMenuWidget( - this.repository, - this.packManager, { - this.scrollController, - super.key, - }); - - @override - State createState() => _PacksMenuWidgetState(); -} - -class _PacksMenuWidgetState extends State { - @override - Widget build(BuildContext context) { - return LayoutBuilder(builder: (context, constraints) { - final cardHeight = 130.h; - final theme = Theme.of(context); - bool isFirstElement = true; - return StreamBuilder( - stream: widget.packManager.packsPreviewStream.asyncMap((packs) async { - if (packs != null) { - if (isFirstElement) { - isFirstElement = false; - _precache(packs); - } else { - await _precache(packs); - } - } - return packs; - }), - builder: (context, snapshot) { - return Column( - children: [ - Header( - 'Темы', - trail: GestureDetector( - onTap: () { - AppRouter.openAuthOrProfile(); - }, - child: Image.asset( - 'icons/profile.png', - width: 27.w, - height: 27.h, - color: Theme.of(context).colorScheme.primary, - ), - ), - ), - Expanded( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: RefreshIndicator( - onRefresh: locator.previewPackPoller.poll, - backgroundColor: Colors.white, - color: snapshot.data?.firstOrNull?.color?.asColor ?? - Colors.black, - child: (!snapshot.hasData) - ? Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: Image.asset( - 'images/cerdo.jpg', - width: 100, - ), - ), - Text('Загрузка...'), - ], - ), - ) - : ListView( - physics: BouncingScrollPhysics( - decelerationRate: ScrollDecelerationRate.fast, - parent: AlwaysScrollableScrollPhysics(), - ), - scrollDirection: Axis.vertical, - children: snapshot.requireData! - .map((pack) => _PackButton4(pack, cardHeight)) - .toList(), - ), - ), - ), - ), - ], - ); - }, - ); - }); - } - - Future _precache(List packs) async { - for (final pack in packs) { - if (pack.imageBase64 != null) { - try { - final packImageId = 'pack_${pack.id}'; - // - if (locator.imagesHolder.havePackImage(packImageId)) { - _decodeImage(pack, packImageId).then((image) { - locator.imagesHolder.setImage(packImageId, image); - precacheImage(image, context); - }); - final image = locator.imagesHolder.getPackImage(packImageId)!; - await precacheImage(image, context); - } else { - final image = await _decodeImage(pack, packImageId); - locator.imagesHolder.setImage(packImageId, image); - await precacheImage(image, context); - } - } catch (e) { - log('${pack.id} cover not cached'); - } - } - } - } - - Future _decodeImage( - CardPackPreviewDto pack, - String packImageId, - ) async { - final bytes = await compute(base64Decode, pack.imageBase64!); - final image = MemoryImage(bytes); - return image; - } -} - -class _PackButton4 extends StatelessWidget { - final CardPackPreviewDto pack; - final double cardHeight; - - const _PackButton4( - this.pack, - this.cardHeight, - ); - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - - return InkWell( - onTap: () { - AutoRouter.of(context).push( - PageRouteInfo( - CardPackPage.name, - args: CardPackPageArgs( - previewDto: pack, - ), - ), - ); - }, - borderRadius: BorderRadius.circular(12.0), - child: Container( - margin: EdgeInsets.all(4.0.h), - alignment: Alignment.center, - height: cardHeight, - decoration: BoxDecoration( - // color: main, - border: Border.all( - color: pack.color?.asColor?.withOpacity(0.9) ?? - Colors.grey.withOpacity(0.5), - width: 1, - ), - borderRadius: BorderRadius.circular(12.0), - ), - child: Row( - mainAxisSize: MainAxisSize.max, - children: [ - Container( - decoration: BoxDecoration( - image: pack.imageBase64 == null - ? null - : DecorationImage( - image: locator.imagesHolder.get('pack_${pack.id}') ?? - MemoryImage(base64Decode(pack.imageBase64!)), - ), - ), - margin: EdgeInsets.all(2.0.w), - width: cardHeight, - height: cardHeight, - ), - Expanded( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - FittedBox( - fit: BoxFit.scaleDown, - child: Text( - pack.title, - style: TextStyle( - fontWeight: FontWeight.w700, - fontSize: 24.sp, - height: 0.9, - ), - ), - ), - if (pack.subtitle != null && pack.subtitle!.isNotEmpty) - FittedBox( - fit: BoxFit.scaleDown, - child: Text( - pack.subtitle!, - style: TextStyle( - fontWeight: FontWeight.w300, - fontSize: 16.sp, - ), - ), - ), - Spacer(), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Row( - children: [ - Text( - pack.cards.toString(), - style: TextStyle( - fontWeight: FontWeight.w200, - fontSize: 15.sp, - ), - ), - Image.asset( - 'icons/cards.png', - width: 18.w, - height: 16.h, - color: Theme.of(context).colorScheme.primary, - ) - ], - ), - ), - if (pack.price != null && !pack.isAvailable) - Text( - pack.price!, - style: TextStyle( - fontWeight: FontWeight.w400, - fontSize: 20.sp, - ), - ), - ], - ) - ], - ), - ), - ), - ], - ), - ), - ); - } -} diff --git a/lib/widgets/shared_pref_button.dart b/lib/widgets/shared_pref_button.dart deleted file mode 100644 index 1e0ff86..0000000 --- a/lib/widgets/shared_pref_button.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/cupertino.dart'; -import 'package:mnemo_cards/main.dart'; -import 'package:mnemo_cards_common/mnemo_cards_common.dart'; -import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart'; -import 'package:shared_preferences/shared_preferences.dart'; - -class MobileSharedPrefButton extends StatelessWidget { - final Function(T)? onClick; - final String spKey; - final T Function(T? current)? setOnTap; - final bool Function(T? current)? shouldRebuild; - final Widget Function(T? value, ValueSetter setValue)? builder; - - MobileSharedPrefButton({ - this.builder, - required this.spKey, - this.setOnTap, - this.onClick, - this.shouldRebuild, - }); - - MobileSharedPrefButton.builder({ - this.builder, - required this.spKey, - this.setOnTap, - this.onClick, - this.shouldRebuild, - }); - - @override - Widget build(BuildContext context) => SharedPrefButton( - sharedPreferences: globalSharedPreferences, - spKey: spKey, - setOnTap: setOnTap, - onClick: onClick, - shouldRebuild: shouldRebuild, - builder: builder, - ); -} diff --git a/images/cerdo.jpg b/mnemo_cards/gen_images/cerdo.jpg similarity index 100% rename from images/cerdo.jpg rename to mnemo_cards/gen_images/cerdo.jpg diff --git a/images/cerdo_big.jpg b/mnemo_cards/gen_images/cerdo_big.jpg similarity index 100% rename from images/cerdo_big.jpg rename to mnemo_cards/gen_images/cerdo_big.jpg diff --git a/images/icon.png b/mnemo_cards/gen_images/icon.png similarity index 100% rename from images/icon.png rename to mnemo_cards/gen_images/icon.png diff --git a/mnemo_cards/gen_images/icon_512.png b/mnemo_cards/gen_images/icon_512.png new file mode 100644 index 0000000..2dfe098 Binary files /dev/null and b/mnemo_cards/gen_images/icon_512.png differ diff --git a/mnemo_cards/gen_images/luna.png b/mnemo_cards/gen_images/luna.png new file mode 100644 index 0000000..0825a73 Binary files /dev/null and b/mnemo_cards/gen_images/luna.png differ diff --git a/mnemo_cards/gen_images/luna_big_dark.jpg b/mnemo_cards/gen_images/luna_big_dark.jpg new file mode 100644 index 0000000..775ffd0 Binary files /dev/null and b/mnemo_cards/gen_images/luna_big_dark.jpg differ diff --git a/mnemo_cards/gen_images/luna_dark.jpg b/mnemo_cards/gen_images/luna_dark.jpg new file mode 100644 index 0000000..82c0196 Binary files /dev/null and b/mnemo_cards/gen_images/luna_dark.jpg differ diff --git a/mnemo_cards/icons/ad.webp b/mnemo_cards/icons/ad.webp new file mode 100644 index 0000000..e88f337 Binary files /dev/null and b/mnemo_cards/icons/ad.webp differ diff --git a/mnemo_cards/icons/back.webp b/mnemo_cards/icons/back.webp new file mode 100644 index 0000000..7e0aba6 Binary files /dev/null and b/mnemo_cards/icons/back.webp differ diff --git a/mnemo_cards/icons/cards.webp b/mnemo_cards/icons/cards.webp new file mode 100644 index 0000000..5002c95 Binary files /dev/null and b/mnemo_cards/icons/cards.webp differ diff --git a/mnemo_cards/icons/chat.webp b/mnemo_cards/icons/chat.webp new file mode 100644 index 0000000..1345684 Binary files /dev/null and b/mnemo_cards/icons/chat.webp differ diff --git a/mnemo_cards/icons/clock.webp b/mnemo_cards/icons/clock.webp new file mode 100644 index 0000000..a04b34a Binary files /dev/null and b/mnemo_cards/icons/clock.webp differ diff --git a/mnemo_cards/icons/crown.webp b/mnemo_cards/icons/crown.webp new file mode 100644 index 0000000..63bffc8 Binary files /dev/null and b/mnemo_cards/icons/crown.webp differ diff --git a/mnemo_cards/icons/down.webp b/mnemo_cards/icons/down.webp new file mode 100644 index 0000000..6ce685b Binary files /dev/null and b/mnemo_cards/icons/down.webp differ diff --git a/mnemo_cards/icons/download.webp b/mnemo_cards/icons/download.webp new file mode 100644 index 0000000..6008778 Binary files /dev/null and b/mnemo_cards/icons/download.webp differ diff --git a/mnemo_cards/icons/exit.webp b/mnemo_cards/icons/exit.webp new file mode 100644 index 0000000..3aaa5d7 Binary files /dev/null and b/mnemo_cards/icons/exit.webp differ diff --git a/mnemo_cards/icons/exit2.webp b/mnemo_cards/icons/exit2.webp new file mode 100644 index 0000000..272562d Binary files /dev/null and b/mnemo_cards/icons/exit2.webp differ diff --git a/mnemo_cards/icons/exit3.webp b/mnemo_cards/icons/exit3.webp new file mode 100644 index 0000000..d677859 Binary files /dev/null and b/mnemo_cards/icons/exit3.webp differ diff --git a/mnemo_cards/icons/gift.webp b/mnemo_cards/icons/gift.webp new file mode 100644 index 0000000..b2757ff Binary files /dev/null and b/mnemo_cards/icons/gift.webp differ diff --git a/mnemo_cards/icons/google.webp b/mnemo_cards/icons/google.webp new file mode 100644 index 0000000..088aa58 Binary files /dev/null and b/mnemo_cards/icons/google.webp differ diff --git a/mnemo_cards/icons/heart.webp b/mnemo_cards/icons/heart.webp new file mode 100644 index 0000000..d4415fa Binary files /dev/null and b/mnemo_cards/icons/heart.webp differ diff --git a/mnemo_cards/icons/heart_fill.webp b/mnemo_cards/icons/heart_fill.webp new file mode 100644 index 0000000..bf53614 Binary files /dev/null and b/mnemo_cards/icons/heart_fill.webp differ diff --git a/mnemo_cards/icons/lock.webp b/mnemo_cards/icons/lock.webp new file mode 100644 index 0000000..c3e60ad Binary files /dev/null and b/mnemo_cards/icons/lock.webp differ diff --git a/mnemo_cards/icons/mic_off.webp b/mnemo_cards/icons/mic_off.webp new file mode 100644 index 0000000..4cec1a9 Binary files /dev/null and b/mnemo_cards/icons/mic_off.webp differ diff --git a/mnemo_cards/icons/mic_on.webp b/mnemo_cards/icons/mic_on.webp new file mode 100644 index 0000000..edd52f0 Binary files /dev/null and b/mnemo_cards/icons/mic_on.webp differ diff --git a/mnemo_cards/icons/next.webp b/mnemo_cards/icons/next.webp new file mode 100644 index 0000000..8a1c91f Binary files /dev/null and b/mnemo_cards/icons/next.webp differ diff --git a/mnemo_cards/icons/profile.webp b/mnemo_cards/icons/profile.webp new file mode 100644 index 0000000..4e4d3f3 Binary files /dev/null and b/mnemo_cards/icons/profile.webp differ diff --git a/mnemo_cards/icons/settings.webp b/mnemo_cards/icons/settings.webp new file mode 100644 index 0000000..e8045ba Binary files /dev/null and b/mnemo_cards/icons/settings.webp differ diff --git a/mnemo_cards/icons/shuffle.webp b/mnemo_cards/icons/shuffle.webp new file mode 100644 index 0000000..6a88c1a Binary files /dev/null and b/mnemo_cards/icons/shuffle.webp differ diff --git a/mnemo_cards/icons/skull.webp b/mnemo_cards/icons/skull.webp new file mode 100644 index 0000000..b7b9681 Binary files /dev/null and b/mnemo_cards/icons/skull.webp differ diff --git a/mnemo_cards/icons/small_circle.webp b/mnemo_cards/icons/small_circle.webp new file mode 100644 index 0000000..2fd26c0 Binary files /dev/null and b/mnemo_cards/icons/small_circle.webp differ diff --git a/mnemo_cards/icons/sound_off.webp b/mnemo_cards/icons/sound_off.webp new file mode 100644 index 0000000..4438c46 Binary files /dev/null and b/mnemo_cards/icons/sound_off.webp differ diff --git a/mnemo_cards/icons/sound_on.webp b/mnemo_cards/icons/sound_on.webp new file mode 100644 index 0000000..6bbbeb4 Binary files /dev/null and b/mnemo_cards/icons/sound_on.webp differ diff --git a/mnemo_cards/icons/telegram.webp b/mnemo_cards/icons/telegram.webp new file mode 100644 index 0000000..76f3871 Binary files /dev/null and b/mnemo_cards/icons/telegram.webp differ diff --git a/mnemo_cards/icons/tg.webp b/mnemo_cards/icons/tg.webp new file mode 100644 index 0000000..a44171d Binary files /dev/null and b/mnemo_cards/icons/tg.webp differ diff --git a/mnemo_cards/icons/view.webp b/mnemo_cards/icons/view.webp new file mode 100644 index 0000000..2a79315 Binary files /dev/null and b/mnemo_cards/icons/view.webp differ diff --git a/mnemo_cards/images/cerdo.webp b/mnemo_cards/images/cerdo.webp new file mode 100644 index 0000000..2ef102a Binary files /dev/null and b/mnemo_cards/images/cerdo.webp differ diff --git a/mnemo_cards/images/luna.webp b/mnemo_cards/images/luna.webp new file mode 100644 index 0000000..5caceed Binary files /dev/null and b/mnemo_cards/images/luna.webp differ diff --git a/mnemo_cards/lib/common_device_info.dart b/mnemo_cards/lib/common_device_info.dart new file mode 100644 index 0000000..06dc55e --- /dev/null +++ b/mnemo_cards/lib/common_device_info.dart @@ -0,0 +1,7 @@ +class CommonDeviceInfo { + final bool isPhysicalDevice; + + CommonDeviceInfo({ + required this.isPhysicalDevice, + }); +} diff --git a/mnemo_cards/lib/features/chat/chat_page.dart b/mnemo_cards/lib/features/chat/chat_page.dart new file mode 100644 index 0000000..db09995 --- /dev/null +++ b/mnemo_cards/lib/features/chat/chat_page.dart @@ -0,0 +1,28 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:mnemo_cards/features/games/game_button.dart'; +import 'package:mnemo_cards_common/mnemo_cards_common.dart'; +import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart'; + +import '../../di/locator.dart'; +import '../analytics/analytics.dart'; + +@RoutePage() +class ChatPage extends StatelessWidget { + @override + Widget build(BuildContext context) { + Analytics.chatPageOpened(locator.userManager.userStateHolder.user); + final theme = Theme.of(context).textTheme; + return Scaffold( + body: SafeArea( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Center( + child: Text('Chat Page'), + )), + ), + ); + } +} diff --git a/mnemo_cards/lib/features/games/funny_letters/funny_letters_settings.dart b/mnemo_cards/lib/features/games/funny_letters/funny_letters_settings.dart new file mode 100644 index 0000000..4ee3b16 --- /dev/null +++ b/mnemo_cards/lib/features/games/funny_letters/funny_letters_settings.dart @@ -0,0 +1,84 @@ +// import 'package:flutter/material.dart'; +// // import 'package:funny_letters/funny_letters.dart'; +// +// import '../../../di/locator.dart'; +// import '../../../main.dart'; +// import '../../../widgets/choose_cards.dart'; +// +// class FunnyLettersSettingsImpl implements FunnyLettersSettings { +// final ThemeData _themeData; +// final ValueGetter _contextGetter; +// Map? _backgrounds; +// Map? _wordsWithTranslations; +// +// FunnyLettersSettingsImpl( +// this._themeData, +// this._contextGetter, +// ); +// +// Future init({String? packId}) async { +// final packCacheManager = locator.packCacheManager; +// final packs = packId == null +// ? await packCacheManager.loadAllPacks() +// : [await packCacheManager.loadPackFromCache(packId)]; +// _wordsWithTranslations = Map.fromEntries( +// packs.expand( +// (p) => p.cards +// .where((c) => c.original != null && c.translation != null) +// .map((c) => MapEntry(c.original!, c.translation!)), +// ), +// ); +// +// _backgrounds = Map.fromEntries( +// packs +// .expand( +// (p) => p.cards.map( +// (c) => MapEntry( +// c.original, +// packCacheManager.getPackImageFile(p.id, c.id).path, +// ), +// ), +// ) +// .where((e) => e.key != null), +// ).cast(); +// } +// +// @override +// Map get backgrounds => _backgrounds!; +// +// @override +// bool get musicEnabled => globalSharedPreferences.getBool('sound_on') ?? true; +// +// @override +// bool get soundEnabled => globalSharedPreferences.getBool('sound_on') ?? true; +// +// @override +// Map get wordsWithTranslations => _wordsWithTranslations!; +// +// @override +// FunnyLettersThemeData get themeData => FunnyLettersThemeData( +// brightness: _themeData.brightness, +// ); +// +// @override +// Future> updateWordsWithTranslations() async { +// await showDialog( +// context: _contextGetter(), +// builder: (context) => FutureBuilder( +// future: locator.packCacheManager.loadAllPacks(), +// builder: (context, packs) { +// if (!packs.hasData) { +// return Center( +// child: CircularProgressIndicator(), +// ); +// } +// return ChooseCards(packs.requireData); +// }, +// ), +// ); +// return {}; +// } +// +// @override +// Map toJson() => {}; +// } diff --git a/mnemo_cards/lib/features/games/game_assets_loader.dart b/mnemo_cards/lib/features/games/game_assets_loader.dart new file mode 100644 index 0000000..c66bb88 --- /dev/null +++ b/mnemo_cards/lib/features/games/game_assets_loader.dart @@ -0,0 +1,24 @@ +import 'package:flutter_tts/flutter_tts.dart'; +import 'package:universal_io/io.dart'; + +// import 'package:funny_letters/main.dart'; + +import '../../managers/audio_player.dart'; +import 'games_manager.dart'; + +class AssetLoaderImpl { + final gameId; + final GamesManager _gamesManager; + + AssetLoaderImpl(this.gameId, this._gamesManager); + + @override + File loadFile(String path) { + return _gamesManager.getGameFile(gameId, path); + } + + @override + Future playWord(String word, {String? lang}) async { + AudioPlayer.playAudio(word, lang: lang); + } +} diff --git a/mnemo_cards/lib/features/games/games_api.dart b/mnemo_cards/lib/features/games/games_api.dart new file mode 100644 index 0000000..31b8084 --- /dev/null +++ b/mnemo_cards/lib/features/games/games_api.dart @@ -0,0 +1,29 @@ +import 'dart:typed_data'; + +import 'package:dio/dio.dart'; +import 'package:mnemo_cards_common/mnemo_cards_common.dart'; + +import '../../managers/repository/api.dart'; + +class GamesApi with Api { + final Dio _dio; + + GamesApi(this._dio); + + Future getGameAssets(String gameId) async { + final r = await _dio.get( + '$path/games/$gameId/assets', + options: Options( + responseType: ResponseType.bytes, + ), + ); + return r.data as Uint8List; + } + + Future> getGamesList() async { + final r = await _dio.get( + '$path/games/list', + ); + return r.data!.decodeList(GameDto.fromJson); + } +} diff --git a/mnemo_cards/lib/features/games/games_bloc.dart b/mnemo_cards/lib/features/games/games_bloc.dart new file mode 100644 index 0000000..6963621 --- /dev/null +++ b/mnemo_cards/lib/features/games/games_bloc.dart @@ -0,0 +1,45 @@ +import 'package:bloc/bloc.dart'; +import 'package:mnemo_cards_common/mnemo_cards_common.dart'; + +import 'games_repository.dart'; + +class GamesCubit extends Cubit { + final GamesRepository gamesRepository; + + GamesCubit(this.gamesRepository) : super(GamesLoading()); + + Future loadGames() async { + print('Load games'); + try { + emit(GamesLoading()); + final games = await gamesRepository.updateAvailableGames(); + emit(GamesLoaded(games, null)); + } catch (e) { + emit(GamesError(e.toString())); + } + } + + void setActiveGame(GameDto game) { + if (state is GamesLoaded) { + final loadedState = state as GamesLoaded; + emit(GamesLoaded(loadedState.games, game)); + } + } +} + +abstract interface class GamesState {} + +class GamesLoading implements GamesState {} + +class GamesError implements GamesState { + String error; + + GamesError(this.error); +} + +class GamesLoaded implements GamesState { + final List games; + final GameDto? activeGame; + + GamesLoaded(this.games, this.activeGame); +} diff --git a/mnemo_cards/lib/features/games/games_manager.dart b/mnemo_cards/lib/features/games/games_manager.dart new file mode 100644 index 0000000..73226ae --- /dev/null +++ b/mnemo_cards/lib/features/games/games_manager.dart @@ -0,0 +1,38 @@ +import 'dart:developer'; +import 'package:mnemo_cards/features/games/games_repository.dart'; +import 'package:universal_io/io.dart'; + +import 'package:flutter/foundation.dart'; +import 'package:path_provider/path_provider.dart'; + +import 'games_api.dart'; + +class GamesManager { + late final String? _docsDirectory; + + final GamesRepository _gamesRepository; + + GamesManager(this._gamesRepository); + + Future init() async { + _gamesRepository.init(); + _docsDirectory = + kIsWeb ? null : (await getApplicationDocumentsDirectory()).path; + } + + File getGameFile(String gameId, String path) { + // if (path.contains('')) { + // return File(path.replaceAll('', '')); + // } + final realFile = File(path); + if (realFile.existsSync()) { + return realFile; + } + final dir = _gameDirectory(gameId); + return File('${dir.path}/$path'); + } + + Future loadAssets(String id) => _gamesRepository.loadAssets(id); + + Directory _gameDirectory(String id) => Directory('$_docsDirectory/games/$id'); +} diff --git a/mnemo_cards/lib/features/games/games_repository.dart b/mnemo_cards/lib/features/games/games_repository.dart new file mode 100644 index 0000000..54c055c --- /dev/null +++ b/mnemo_cards/lib/features/games/games_repository.dart @@ -0,0 +1,79 @@ +import 'dart:developer'; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:flutter/foundation.dart'; +import 'package:mnemo_cards/features/games/games_api.dart'; +import 'package:mnemo_cards_common/mnemo_cards_common.dart'; +import 'package:package_info_plus/package_info_plus.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../../managers/repository/dio_provider.dart'; +import '../../utils/archive_decoder.dart'; + +class GamesRepository { + final SharedPreferences sharedPreferences; + final GamesApi _api; + + late final String? _docsDirectory; + + GamesRepository(this.sharedPreferences, this._api); + + Future init() async { + _docsDirectory = + kIsWeb ? null : (await getApplicationDocumentsDirectory()).path; + } + + Future> updateAvailableGames() async { + final games = await _api.getGamesList(); + return games; + } + + Future updateGameCache(String gameId) async { + final cache = _api.getGameAssets(gameId); + } + + Future loadAssets(String gameId) async { + try { + final data = await _api.getGameAssets(gameId); + await saveGameAssets(gameId, data); + } catch (e, s) { + log('Failed to load game $gameId', error: e, stackTrace: s); + } + } + + Future saveGameAssets(String gameId, Uint8List data) async { + log('Saving game $gameId'); + if (kIsWeb) { + log('Not saving game in web $gameId'); + return; + } + final dir = _gameDirectory(gameId); + final assetsDir = Directory('${dir.path}/assets'); + if (assetsDir.existsSync()) { + assetsDir.deleteSync(recursive: true); + } + await assetsDir.create(recursive: true); + final appVersion = + (await PackageInfo.fromPlatform(baseUrl: versionUrl)).version; + final assets = + await ArchiveDecoder().decodeArchive(data, gameId, appVersion); + if (assets == null) { + log('Failed to decode archive'); + return; + } + for (final entry in assets.entries) { + if (entry.value.isEmpty) { + continue; + } + final file = File('${assetsDir.path}/${entry.key}'); + await file.create(recursive: true); + await file.writeAsBytes(entry.value); + log('Saved ${entry.key} -> ${file.path}'); + } + log('Saved ${gameId} to ${assetsDir.path}'); + } + + Directory _gameDirectory(String id) => Directory('$_docsDirectory/games/$id'); +} diff --git a/mnemo_cards/lib/features/packs/web_pack_cache_manager.dart b/mnemo_cards/lib/features/packs/web_pack_cache_manager.dart new file mode 100644 index 0000000..8701151 --- /dev/null +++ b/mnemo_cards/lib/features/packs/web_pack_cache_manager.dart @@ -0,0 +1,206 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:developer'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:mnemo_cards_common/mnemo_cards_common.dart'; + +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:universal_io/io.dart'; + +import '../../di/locator.dart'; +import '../analytics/analytics.dart'; +import 'pack_cache_manager.dart'; + +// persist +class WebPackCacheManager implements PackCacheManager { + late final SharedPreferences _sharedPreferences; + late final String? _cacheDirectory; + + WebPackCacheManager(); + + Future init() async { + _sharedPreferences = await SharedPreferences.getInstance(); + } + + Future isPackCached(String id) async { + try { + await loadPackFromCache(id, withCardImages: false); + } catch (e, s) { + return false; + } + return true; + } + + Future markPackForUpdate(String packId) async { + try { + final cacheId = await _packCacheId(packId); + final jsonString = _sharedPreferences.getString(cacheId); + if (jsonString != null) { + final json = jsonString.decode(); + json['id'] = '0'; + await _sharedPreferences.setString(cacheId, jsonEncode(json)); + } + } catch (e, s) { + log('Cant mark pack for update', error: e, stackTrace: s); + Analytics.crashlyticsError( + e: e, + s: s, + message: 'Cant mark pack for update', + ); + } + } + + Future loadPackFromCache( + String packId, { + bool withCardImages = true, + bool clearCardImages = true, + }) async { + log('Loading from cache ${packId}'); + try { + final packCacheId = _packCacheId(packId); + final json = _sharedPreferences.getString(packCacheId); + if (json == null) { + log('Pack cache not exist'); + return throw Exception('Pack cache not exist'); + } + var pack = json.decode(CardPackDto.fromJson); + if (withCardImages) { + Map images = {}; + log('Cards ${packId}: ${pack.cards.length}'); + if (pack.cards.isNotEmpty) { + // log('Loading cards ${packId}'); + // await Future.wait( + // pack.cards.map((dto) async { + // final file = File('${packDir.path}/images/${dto.id}'); + // if (await file.exists()) { + // try { + // images[dto.id.toString()] = + // MemoryImage(await file.readAsBytes()); + // } catch (e) { + // markPackForUpdate(packId); + // throw Exception('Cant read file $packId $e'); + // } + // } else { + // log('Cant load card ${dto.id} from $packId, skiping'); + // } + // }), + // ); + } + log('Pack loaded ${packId}'); + locator.imagesHolder.setPackImages(images, packId); + } + return pack; + } catch (e, s) { + log('Cant load from cache ${packId} ${e} ${s}'); + deletePack(packId); + rethrow; + } + } + + Future> _updateSavedPacks() async { + if (kIsWeb) { + return []; + } + final packsDir = Directory('${_cacheDirectory}/packs'); + List savedPackIds = []; + if (packsDir.existsSync()) { + for (final packDir in packsDir.listSync()) { + if (File('${packDir.path}/pack.json').existsSync()) { + savedPackIds.add(packDir.path.split('/').last); + } + } + } + _sharedPreferences.setStringList('packs', savedPackIds); + return savedPackIds; + } + + Future> loadAllPacks({bool withCardImages = false}) async { + final ids = await _updateSavedPacks(); + final List packs = []; + for (final id in ids) { + try { + final pack = await loadPackFromCache( + id, + withCardImages: withCardImages, + ); + packs.add(pack); + } catch (e) { + log('Pack $id doesnt exist anymore'); + } + } + return packs; + } + + Future deletePack(String packId) async { + try { + final id = await _packCacheId(packId); + _sharedPreferences.remove(id); + // dir.deleteSync(recursive: true); + } catch (e) { + log('Deletion failed $e'); + } + _updateSavedPacks(); + } + + Future loadPackFile(String packId, String path) async { + // final bytes = + // File('${(await _packDirectory(packId)).path}/$path').readAsBytesSync(); + // return bytes; + return null; + } + + Future clearCache() async { + final saved = await _updateSavedPacks(); + for (final p in saved) { + await deletePack(p); + } + await _sharedPreferences.remove('packs'); + } + + Future savePack( + CardPackDto dto, { + Map>? images, + }) async { + log('Saving pack ${dto.id}'); + final packCacheId = _packCacheId(dto.id); + final json = dto.encode(); + log('Saving dto ${dto.id}'); + _sharedPreferences.setString(packCacheId, jsonEncode(json)); + + // if (images != null) { + // log('Saving card images ${dto.id}'); + // for (final card in dto.cards) { + // final bytes = images[card.id.toString()]; + // if (bytes != null) { + // File('${imageDir.path}/${card.id}') + // ..createSync(recursive: true) + // ..writeAsBytesSync(bytes); + // } + // } + // } + + log('Saved ${dto.id}'); + } + + String _packCacheId(String dtoId) => 'pack_cache_${dtoId}'; + + @override + File getPackFile(String packId, String path) { + // TODO: implement getPackFile + throw UnimplementedError(); + } + + @override + File getPackImageFile(String packId, int id) { + // TODO: implement getPackImageFile + throw UnimplementedError(); + } + + @override + Future loadPackImage(String packId, int id) { + // TODO: implement loadPackImage + throw UnimplementedError(); + } +} diff --git a/mnemo_cards/lib/utils/archive_decoder.dart b/mnemo_cards/lib/utils/archive_decoder.dart new file mode 100644 index 0000000..86233bd --- /dev/null +++ b/mnemo_cards/lib/utils/archive_decoder.dart @@ -0,0 +1,54 @@ +import 'package:archive/archive_io.dart'; +import 'package:flutter/foundation.dart'; +import 'package:mnemo_cards_common/mnemo_cards_common.dart'; + +class ArchiveDecoder { + const ArchiveDecoder(); + + Future>?> decodeArchive( + Uint8List data, + String id, + String appVersion, + ) async { + return await compute( + _decodeArchive, + _Envelope( + data: data, + id: id, + appVersion: appVersion, + ), + ); + } + + Map> _decodeArchive(_Envelope envelope) { + final s = Stopwatch()..start(); + final password = TokenGenerator.generateArchivePassword( + appVersion: envelope.appVersion, + id: envelope.id, + ); + print('generate token ${s.elapsedMilliseconds} mills'); + final imagesArchive = ZipDecoder().decodeBytes( + envelope.data, + password: password, + ); + print('deflate archive ${s.elapsedMilliseconds} mills'); + final images = Map>(); + for (final file in imagesArchive) { + images[file.name] = file.content as List; + } + print('loaded images ${s.elapsedMilliseconds} mills'); + return images; + } +} + +class _Envelope { + final List data; + final String id; + final String appVersion; + + _Envelope({ + required this.data, + required this.id, + required this.appVersion, + }); +} diff --git a/mnemo_cards/lib/widgets/card_pack/cards_list.dart b/mnemo_cards/lib/widgets/card_pack/cards_list.dart new file mode 100644 index 0000000..cff7cc9 --- /dev/null +++ b/mnemo_cards/lib/widgets/card_pack/cards_list.dart @@ -0,0 +1,166 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:mnemo_cards/features/packs/images_holder.dart'; +import 'package:mnemo_cards/widgets/card_pack/cards_view.dart'; +import 'package:mnemo_cards_common/mnemo_cards_common.dart'; +import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart' + as frontend; +import 'package:reorderables/reorderables.dart'; + + +import '../game_card_widget.dart'; + +class CardsList extends StatelessWidget { + final List cards; + final double cardWidth; + final double imageWidth; + final double cardRatio; + final Function(List, GameCardDto) onCardTap; + final Color? color; + final Animation animation; + final Function(int, int) onReorder; + final CardBuilder? cardBuilder; + + const CardsList({ + required this.cards, + required this.cardWidth, + required this.imageWidth, + required this.cardRatio, + required this.onCardTap, + required this.onReorder, + required this.color, + required this.animation, + this.cardBuilder, + }); + + @override + Widget build(BuildContext context) { + return ReorderableWrap( + scrollPhysics: + animation.value > 0 ? const NeverScrollableScrollPhysics() : null, + onReorder: (int from, int to) => onReorder(from, to), + buildDraggableFeedback: frontend.FeedbackBuilder.simple, + padding: EdgeInsets.symmetric( + horizontal: (cardWidth - imageWidth - 1), + vertical: (cardWidth - imageWidth - 1), + ), + runSpacing: (cardWidth - imageWidth - 1) * 2, + spacing: (cardWidth - imageWidth - 1) * 2, + children: cards.map((card) { + if (cardBuilder != null) { + return cardBuilder!( + card, + // color, + // imageWidth, + // cardWidth, + // cardRatio, + // () => onCardTap(cards, card), + ); + } + final memoryImage = card.memoryImage; + if (memoryImage == null) return const SizedBox(); + return _CardItem( + key: ValueKey(card.id), + card: card, + color: color, + image: memoryImage, + imageWidth: imageWidth, + cardWidth: cardWidth, + cardRatio: cardRatio, + onTap: () => onCardTap(cards, card), + ); + }).toList(), + ); + } +} + +class _CardItem extends StatelessWidget { + final GameCardDto card; + final Color? color; + final MemoryImage image; + final double imageWidth; + final double cardWidth; + final double cardRatio; + final VoidCallback onTap; + + const _CardItem({ + required this.card, + required this.color, + required this.image, + required this.imageWidth, + required this.cardWidth, + required this.cardRatio, + required this.onTap, + super.key, + }); + + @override + Widget build(BuildContext context) { + return Container( + height: 2 * imageWidth - cardWidth * cardRatio * 0.5 + 2, + width: 2 * imageWidth - cardWidth + 2, + alignment: Alignment.topCenter, + child: Container( + height: imageWidth * cardRatio, + child: _CardBuilder( + color, + card, + image, + onTap, + ), + ), + ); + } +} + +class _CardBuilder extends StatelessWidget { + final GameCardDto card; + final Color? color; + final MemoryImage image; + final VoidCallback onTap; + + _CardBuilder( + this.color, + this.card, + this.image, + this.onTap, + ); + + @override + Widget build(BuildContext context) { + return GestureDetector( + key: ObjectKey(card), + onTap: onTap, + child: Hero( + key: ValueKey('${card.id}'), + tag: card.id, + child: GameCardDecoration( + alpha: 0.0, + borderColor: color, + child: FrontCard( + card, + image, + alpha: 0, + ), + ), + createRectTween: (a, b) => RectTween(begin: a, end: b), + flightShuttleBuilder: (flightC, a, dir, fromC, toC) { + final animatedCard = dir == HeroFlightDirection.push ? card : card; + // todo resolve card from context + return AnimatedBuilder( + animation: a, + builder: (context, child) => GameCardDecoration( + alpha: a.value, + borderColor: color, + child: FrontCard( + animatedCard, + animatedCard.memoryImage!, + alpha: a.value, + ), + ), + ); + }, + ), + ); + } +} diff --git a/lib/widgets/card_pack/loading_pack.dart b/mnemo_cards/lib/widgets/card_pack/error_loading_pack.dart similarity index 58% rename from lib/widgets/card_pack/loading_pack.dart rename to mnemo_cards/lib/widgets/card_pack/error_loading_pack.dart index fd5bf46..a30e3a9 100644 --- a/lib/widgets/card_pack/loading_pack.dart +++ b/mnemo_cards/lib/widgets/card_pack/error_loading_pack.dart @@ -1,16 +1,19 @@ import 'dart:convert'; +import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:mnemo_cards_common/mnemo_cards_common.dart'; +import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart'; import '../../di/locator.dart'; import '../../utils/string_helper.dart'; import 'card_pack_header.dart'; -class Loading extends StatelessWidget { +class ErrorLoading extends StatelessWidget { final CardPackLoadingDto loadingDto; - Loading(this.loadingDto); + ErrorLoading(this.loadingDto); @override Widget build(BuildContext context) { @@ -29,18 +32,27 @@ class Loading extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ if (memoryImage != null) - Padding( - padding: const EdgeInsets.all(8.0), + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16.0), + ), + clipBehavior: Clip.antiAlias, child: Image( image: memoryImage, width: 100, ), ), - if (loadingDto.message != null) Text(loadingDto.message!), + if (loadingDto.message != null) + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text(loadingDto.message!), + ), + SizedBox(height: 8.0.h), ], ), ), ), + BigBackButton(), ], ); } diff --git a/mnemo_cards/lib/widgets/choose_cards.dart b/mnemo_cards/lib/widgets/choose_cards.dart new file mode 100644 index 0000000..6ecb8ac --- /dev/null +++ b/mnemo_cards/lib/widgets/choose_cards.dart @@ -0,0 +1,145 @@ +import 'dart:developer'; + +import 'package:collection/collection.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:mnemo_cards/utils/string_helper.dart'; +import 'package:mnemo_cards/widgets/card_pack/cards_list.dart'; +import 'package:mnemo_cards_common/mnemo_cards_common.dart'; + +import '../di/locator.dart'; +import 'card_pack/cards_view.dart'; +import 'card_pack/cards_view_controller.dart'; + +class ChooseCards extends StatefulWidget { + final List _cardPacks; + + ChooseCards(this._cardPacks); + + @override + State createState() => ChooseCardsState(); +} + +class ChooseCardsState extends State { + late final List _cardControllers; + + @override + void initState() { + super.initState(); + _cardControllers = widget._cardPacks + .map( + (pack) => CardsViewController(pack, expanded: true), + ) + .toList(); + } + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: () async { + try { + final packs = await locator.packCacheManager.loadAllPacks( + withCardImages: true, + ); + print('Loaded ${packs.length} packs: ${packs}'); + return true; + } catch (e, s) { + log('Cant load packs', error: e, stackTrace: s); + } + }.call(), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return const Center( + child: CircularProgressIndicator(), + ); + } + return Container( + child: ListView( + shrinkWrap: true, + children: [ + ..._cardControllers.mapIndexed((index, controller) { + final pack = controller.cardPack; + final color = controller.cardPack.color?.asColor; + return Container( + child: Column( + children: [ + Container( + height: 1, + color: color, + ), + DecoratedBox( + decoration: BoxDecoration( + color: color?.withOpacity(0.1), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only( + left: 12.0, + top: 8.0, + ).w, + child: _Title(controller.cardPack), + ), + CardsView( + controller, + color: color, + onCardTap: (cards, card) {}, + ), + ], + ), + ), + Container( + height: 1, + color: color, + ), + ], + ), + ); + }), + ], + ), + ); + }); + } +} + +class _Title extends StatelessWidget { + final CardPackDto pack; + + const _Title(this.pack); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + FittedBox( + alignment: Alignment.centerLeft, + fit: BoxFit.scaleDown, + child: Text( + pack.title, + style: TextStyle( + fontWeight: FontWeight.w700, + fontSize: 24.sp, + height: 0.9, + ), + ), + ), + if (pack.subtitle.isNotEmpty) + FittedBox( + alignment: Alignment.centerLeft, + fit: BoxFit.scaleDown, + child: Text( + pack.subtitle, + style: TextStyle( + fontWeight: FontWeight.w300, + fontSize: 16.sp, + ), + ), + ), + ], + ); + } +} diff --git a/mnemo_cards/web_server.py b/mnemo_cards/web_server.py new file mode 100644 index 0000000..bfa0173 --- /dev/null +++ b/mnemo_cards/web_server.py @@ -0,0 +1,88 @@ +# Directory to serve your website files +WEBSITE_DIRECTORY = "/Users/dmitry/StudioProjects/mnemo_cards/mnemo_cards/build/web" + + +import http.server +import socketserver +import os +import urllib.request + +class ProxyHTTPRequestHandler(http.server.CGIHTTPRequestHandler): + def end_headers(self): + # Add CORS headers + self.send_header("Access-Control-Allow-Origin", "*") # Allow all origins + self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS") # Allowed methods + self.send_header("Access-Control-Allow-Headers", "Content-Type, Authorization") # Allowed headers + super().end_headers() + + def do_OPTIONS(self): + # Handle preflight requests + self.send_response(204) # No Content + self.end_headers() + + def do_GET(self): + # Check if the request is for a local file + local_file_path = os.path.join(WEBSITE_DIRECTORY, self.path.lstrip("/")) + if 'version.json' in local_file_path: + local_file_path = local_file_path.split('?')[0] + if self.path == '/': + local_file_path = os.path.join(WEBSITE_DIRECTORY, 'index.html') + if os.path.isfile(local_file_path): + self.serve_local_file(local_file_path) + else: + # Otherwise, act as a proxy + self.handle_proxy_request() + + def do_POST(self): + content_length = int(self.headers.get('Content-Length', 0)) + post_data = self.rfile.read(content_length) # Read the POST body + + # Extract target URL (Remove the leading '/') + target_url = 'http://localhost:8080/' + self.path[1:] + + try: + # Forward the POST request + request = urllib.request.Request(target_url, data=post_data, method="POST") + for header in self.headers: + request.add_header(header, self.headers[header]) # Copy headers + + with urllib.request.urlopen(request) as response: + content = response.read() + self.send_response(response.getcode()) + self.send_header("Content-Type", response.headers.get("Content-Type", "text/html")) + self.end_headers() + self.wfile.write(content) + except Exception as e: + self.send_error(500, f"Error forwarding POST request: {e}") + + def serve_local_file(self, local_file_path): + self.send_response(200) + self.send_header("Content-Type", self.guess_type(local_file_path)) + self.end_headers() + with open(local_file_path, "rb") as file: + self.wfile.write(file.read()) + + def handle_proxy_request(self): + try: + # Remove the leading '/' to get the actual URL + target_url = 'http://localhost:8080/' + self.path[1:] + with urllib.request.urlopen(target_url) as response: + content = response.read() + self.send_response(response.getcode()) + self.send_header("Content-Type", response.headers.get("Content-Type", "text/html")) + self.end_headers() + self.wfile.write(content) + except Exception as e: + self.send_error(500, f"Error fetching URL: {e}") + +if __name__ == "__main__": + PORT = 8000 + + # Change the working directory to the website directory + os.chdir(WEBSITE_DIRECTORY) + + # Start the server + with socketserver.TCPServer(("", PORT), ProxyHTTPRequestHandler) as httpd: + print(f"Serving on port {PORT}") + print(f"Serving website from: {os.path.abspath(WEBSITE_DIRECTORY)}") + httpd.serve_forever() \ No newline at end of file diff --git a/mnemo_cards/zanahoria.png b/mnemo_cards/zanahoria.png new file mode 100644 index 0000000..b2cd9b6 Binary files /dev/null and b/mnemo_cards/zanahoria.png differ diff --git a/pubspec.lock b/pubspec.lock deleted file mode 100644 index 563f3ed..0000000 --- a/pubspec.lock +++ /dev/null @@ -1,1520 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - _fe_analyzer_shared: - dependency: transitive - description: - name: _fe_analyzer_shared - sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" - url: "https://pub.dev" - source: hosted - version: "67.0.0" - _flutterfire_internals: - dependency: transitive - description: - name: _flutterfire_internals - sha256: b1595874fbc8f7a50da90f5d8f327bb0bfd6a95dc906c390efe991540c3b54aa - url: "https://pub.dev" - source: hosted - version: "1.3.40" - analyzer: - dependency: transitive - description: - name: analyzer - sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" - url: "https://pub.dev" - source: hosted - version: "6.4.1" - ansicolor: - dependency: transitive - description: - name: ansicolor - sha256: "8bf17a8ff6ea17499e40a2d2542c2f481cd7615760c6d34065cb22bfd22e6880" - url: "https://pub.dev" - source: hosted - version: "2.0.2" - app_links: - dependency: "direct main" - description: - name: app_links - sha256: "4acba851087b25136e8f6e32a53bd4536eb3bec69947ddb66e7b9a5792ceb0c7" - url: "https://pub.dev" - source: hosted - version: "6.2.0" - app_links_linux: - dependency: transitive - description: - name: app_links_linux - sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81 - url: "https://pub.dev" - source: hosted - version: "1.0.3" - app_links_platform_interface: - dependency: transitive - description: - name: app_links_platform_interface - sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f" - url: "https://pub.dev" - source: hosted - version: "2.0.2" - app_links_web: - dependency: transitive - description: - name: app_links_web - sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555 - url: "https://pub.dev" - source: hosted - version: "1.0.4" - archive: - dependency: "direct main" - description: - name: archive - sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d - url: "https://pub.dev" - source: hosted - version: "3.6.1" - args: - dependency: transitive - description: - name: args - sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" - url: "https://pub.dev" - source: hosted - version: "2.5.0" - async: - dependency: transitive - description: - name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" - url: "https://pub.dev" - source: hosted - version: "2.11.0" - auto_route: - dependency: "direct main" - description: - name: auto_route - sha256: bb673104dbdc22667d01ec668df3d2a358b6e3da481428eeb1151933cfc1a7d6 - url: "https://pub.dev" - source: hosted - version: "9.2.0" - auto_route_generator: - dependency: "direct dev" - description: - name: auto_route_generator - sha256: c9086eb07271e51b44071ad5cff34e889f3156710b964a308c2ab590769e79e6 - url: "https://pub.dev" - source: hosted - version: "9.0.0" - auto_size_text: - dependency: "direct main" - description: - name: auto_size_text - sha256: "3f5261cd3fb5f2a9ab4e2fc3fba84fd9fcaac8821f20a1d4e71f557521b22599" - url: "https://pub.dev" - source: hosted - version: "3.0.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - build: - dependency: transitive - description: - name: build - sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" - url: "https://pub.dev" - source: hosted - version: "2.4.1" - build_config: - dependency: transitive - description: - name: build_config - sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 - url: "https://pub.dev" - source: hosted - version: "1.1.1" - build_daemon: - dependency: transitive - description: - name: build_daemon - sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9" - url: "https://pub.dev" - source: hosted - version: "4.0.2" - build_resolvers: - dependency: transitive - description: - name: build_resolvers - sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" - url: "https://pub.dev" - source: hosted - version: "2.4.2" - build_runner: - dependency: "direct dev" - description: - name: build_runner - sha256: "644dc98a0f179b872f612d3eb627924b578897c629788e858157fa5e704ca0c7" - url: "https://pub.dev" - source: hosted - version: "2.4.11" - build_runner_core: - dependency: transitive - description: - name: build_runner_core - sha256: e3c79f69a64bdfcd8a776a3c28db4eb6e3fb5356d013ae5eb2e52007706d5dbe - url: "https://pub.dev" - source: hosted - version: "7.3.1" - built_collection: - dependency: transitive - description: - name: built_collection - sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" - url: "https://pub.dev" - source: hosted - version: "5.1.1" - built_value: - dependency: transitive - description: - name: built_value - sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb - url: "https://pub.dev" - source: hosted - version: "8.9.2" - characters: - dependency: transitive - description: - name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" - url: "https://pub.dev" - source: hosted - version: "1.3.0" - checked_yaml: - dependency: transitive - description: - name: checked_yaml - sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff - url: "https://pub.dev" - source: hosted - version: "2.0.3" - cli_util: - dependency: transitive - description: - name: cli_util - sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 - url: "https://pub.dev" - source: hosted - version: "0.4.1" - clock: - dependency: transitive - description: - name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf - url: "https://pub.dev" - source: hosted - version: "1.1.1" - cloud_firestore: - dependency: "direct main" - description: - name: cloud_firestore - sha256: a31eec60eadaa859f0677bf661d9f86ed15961c716512f64884e59edcb341472 - url: "https://pub.dev" - source: hosted - version: "5.2.1" - cloud_firestore_platform_interface: - dependency: transitive - description: - name: cloud_firestore_platform_interface - sha256: "3224e6158441c8897325e74f9971140cde2c85ee75a26704407a91b969b50829" - url: "https://pub.dev" - source: hosted - version: "6.3.1" - cloud_firestore_web: - dependency: transitive - description: - name: cloud_firestore_web - sha256: c1312945cb7dd55921bcc10445f6c9a494bc04104b7d0821c3ed577b970ab088 - url: "https://pub.dev" - source: hosted - version: "4.1.1" - code_builder: - dependency: transitive - description: - name: code_builder - sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 - url: "https://pub.dev" - source: hosted - version: "4.10.0" - collection: - dependency: "direct main" - description: - name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a - url: "https://pub.dev" - source: hosted - version: "1.18.0" - convert: - dependency: transitive - description: - name: convert - sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" - url: "https://pub.dev" - source: hosted - version: "3.1.1" - copy_with_extension: - dependency: "direct main" - description: - name: copy_with_extension - sha256: fbcf890b0c34aedf0894f91a11a579994b61b4e04080204656b582708b5b1125 - url: "https://pub.dev" - source: hosted - version: "5.0.4" - copy_with_extension_gen: - dependency: "direct main" - description: - name: copy_with_extension_gen - sha256: "51cd11094096d40824c8da629ca7f16f3b7cea5fc44132b679617483d43346b0" - url: "https://pub.dev" - source: hosted - version: "5.0.4" - cross_file: - dependency: transitive - description: - name: cross_file - sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" - url: "https://pub.dev" - source: hosted - version: "0.3.4+2" - crypto: - dependency: transitive - description: - name: crypto - sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab - url: "https://pub.dev" - source: hosted - version: "3.0.3" - csslib: - dependency: transitive - description: - name: csslib - sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" - url: "https://pub.dev" - source: hosted - version: "1.0.0" - cupertino_icons: - dependency: "direct main" - description: - name: cupertino_icons - sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 - url: "https://pub.dev" - source: hosted - version: "1.0.8" - dart_style: - dependency: transitive - description: - name: dart_style - sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" - url: "https://pub.dev" - source: hosted - version: "2.3.6" - device_info_plus: - dependency: "direct main" - description: - name: device_info_plus - sha256: "93429694c9253d2871b3af80cf11b3cbb5c65660d402ed7bf69854ce4a089f82" - url: "https://pub.dev" - source: hosted - version: "10.1.1" - device_info_plus_platform_interface: - dependency: transitive - description: - name: device_info_plus_platform_interface - sha256: "282d3cf731045a2feb66abfe61bbc40870ae50a3ed10a4d3d217556c35c8c2ba" - url: "https://pub.dev" - source: hosted - version: "7.0.1" - dio: - dependency: "direct main" - description: - name: dio - sha256: e17f6b3097b8c51b72c74c9f071a605c47bcc8893839bd66732457a5ebe73714 - url: "https://pub.dev" - source: hosted - version: "5.5.0+1" - dio_web_adapter: - dependency: transitive - description: - name: dio_web_adapter - sha256: "36c5b2d79eb17cdae41e974b7a8284fec631651d2a6f39a8a2ff22327e90aeac" - url: "https://pub.dev" - source: hosted - version: "1.0.1" - dot_navigation_bar: - dependency: "direct main" - description: - name: dot_navigation_bar - sha256: "753e1d91644e39beddd0a4ed7e366f37a95e38cafb601c3b7496120ae0532f63" - url: "https://pub.dev" - source: hosted - version: "1.0.2" - equatable: - dependency: transitive - description: - name: equatable - sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 - url: "https://pub.dev" - source: hosted - version: "2.0.5" - fake_async: - dependency: transitive - description: - name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" - url: "https://pub.dev" - source: hosted - version: "1.3.1" - ffi: - dependency: transitive - description: - name: ffi - sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - file: - dependency: transitive - description: - name: file - sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" - url: "https://pub.dev" - source: hosted - version: "7.0.0" - file_picker: - dependency: "direct main" - description: - name: file_picker - sha256: "825aec673606875c33cd8d3c4083f1a3c3999015a84178b317b7ef396b7384f3" - url: "https://pub.dev" - source: hosted - version: "8.0.7" - firebase_analytics: - dependency: "direct main" - description: - name: firebase_analytics - sha256: "064e5b57b0693305946b7caa6a80ed80a918f46804c247b6cd7ed9cd327df48f" - url: "https://pub.dev" - source: hosted - version: "11.2.1" - firebase_analytics_platform_interface: - dependency: transitive - description: - name: firebase_analytics_platform_interface - sha256: d094547c9022c404b5ca39b7209607fc80e75e39d38875f050508fa4346b3e74 - url: "https://pub.dev" - source: hosted - version: "4.2.1" - firebase_analytics_web: - dependency: transitive - description: - name: firebase_analytics_web - sha256: "06dc023b0144c0df630a56b6262cc9e7d6069fe78148853d97614dbefb6ea923" - url: "https://pub.dev" - source: hosted - version: "0.5.9+1" - firebase_core: - dependency: transitive - description: - name: firebase_core - sha256: "3187f4f8e49968573fd7403011dca67ba95aae419bc0d8131500fae160d94f92" - url: "https://pub.dev" - source: hosted - version: "3.3.0" - firebase_core_platform_interface: - dependency: transitive - description: - name: firebase_core_platform_interface - sha256: "3c3a1e92d6f4916c32deea79c4a7587aa0e9dbbe5889c7a16afcf005a485ee02" - url: "https://pub.dev" - source: hosted - version: "5.2.0" - firebase_core_web: - dependency: transitive - description: - name: firebase_core_web - sha256: e8d1e22de72cb21cdcfc5eed7acddab3e99cd83f3b317f54f7a96c32f25fd11e - url: "https://pub.dev" - source: hosted - version: "2.17.4" - firebase_crashlytics: - dependency: "direct main" - description: - name: firebase_crashlytics - sha256: "30260e1b8ad1464b41ca4531b44ce63d752daaf2f12c92ca6cdcd82b270abecc" - url: "https://pub.dev" - source: hosted - version: "4.0.4" - firebase_crashlytics_platform_interface: - dependency: transitive - description: - name: firebase_crashlytics_platform_interface - sha256: a75e1826d92ea4e86e4a753c7b5d64b844a362676fa653185f1581c859186d18 - url: "https://pub.dev" - source: hosted - version: "3.6.40" - fixnum: - dependency: transitive - description: - name: fixnum - sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - fl_chart: - dependency: "direct main" - description: - name: fl_chart - sha256: d0f0d49112f2f4b192481c16d05b6418bd7820e021e265a3c22db98acf7ed7fb - url: "https://pub.dev" - source: hosted - version: "0.68.0" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_colorpicker: - dependency: "direct main" - description: - name: flutter_colorpicker - sha256: "969de5f6f9e2a570ac660fb7b501551451ea2a1ab9e2097e89475f60e07816ea" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - flutter_launcher_icons: - dependency: "direct dev" - description: - name: flutter_launcher_icons - sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea" - url: "https://pub.dev" - source: hosted - version: "0.13.1" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" - url: "https://pub.dev" - source: hosted - version: "4.0.0" - flutter_native_splash: - dependency: "direct dev" - description: - name: flutter_native_splash - sha256: aa06fec78de2190f3db4319dd60fdc8d12b2626e93ef9828633928c2dcaea840 - url: "https://pub.dev" - source: hosted - version: "2.4.1" - flutter_plugin_android_lifecycle: - dependency: transitive - description: - name: flutter_plugin_android_lifecycle - sha256: "9d98bd47ef9d34e803d438f17fd32b116d31009f534a6fa5ce3a1167f189a6de" - url: "https://pub.dev" - source: hosted - version: "2.0.21" - flutter_screenutil: - dependency: "direct main" - description: - name: flutter_screenutil - sha256: "8239210dd68bee6b0577aa4a090890342d04a136ce1c81f98ee513fc0ce891de" - url: "https://pub.dev" - source: hosted - version: "5.9.3" - flutter_secure_storage: - dependency: "direct main" - description: - name: flutter_secure_storage - sha256: "165164745e6afb5c0e3e3fcc72a012fb9e58496fb26ffb92cf22e16a821e85d0" - url: "https://pub.dev" - source: hosted - version: "9.2.2" - flutter_secure_storage_linux: - dependency: transitive - description: - name: flutter_secure_storage_linux - sha256: "4d91bfc23047422cbcd73ac684bc169859ee766482517c22172c86596bf1464b" - url: "https://pub.dev" - source: hosted - version: "1.2.1" - flutter_secure_storage_macos: - dependency: transitive - description: - name: flutter_secure_storage_macos - sha256: "1693ab11121a5f925bbea0be725abfcfbbcf36c1e29e571f84a0c0f436147a81" - url: "https://pub.dev" - source: hosted - version: "3.1.2" - flutter_secure_storage_platform_interface: - dependency: transitive - description: - name: flutter_secure_storage_platform_interface - sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 - url: "https://pub.dev" - source: hosted - version: "1.1.2" - flutter_secure_storage_web: - dependency: transitive - description: - name: flutter_secure_storage_web - sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - flutter_secure_storage_windows: - dependency: transitive - description: - name: flutter_secure_storage_windows - sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 - url: "https://pub.dev" - source: hosted - version: "3.1.2" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - flutter_tts: - dependency: "direct main" - description: - name: flutter_tts - sha256: aed2a00c48c43af043ed81145fd8503ddd793dafa7088ab137dbef81a703e53d - url: "https://pub.dev" - source: hosted - version: "4.0.2" - flutter_web_plugins: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - flutter_widget_from_html_core: - dependency: "direct main" - description: - name: flutter_widget_from_html_core - sha256: df7c7c9e5ea144f7ab0adfbad733b4d4f7d408ab733c94e6e9fdcb327af92aa1 - url: "https://pub.dev" - source: hosted - version: "0.15.1" - freezed: - dependency: "direct main" - description: - name: freezed - sha256: a434911f643466d78462625df76fd9eb13e57348ff43fe1f77bbe909522c67a1 - url: "https://pub.dev" - source: hosted - version: "2.5.2" - freezed_annotation: - dependency: "direct main" - description: - name: freezed_annotation - sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 - url: "https://pub.dev" - source: hosted - version: "2.4.4" - frontend_server_client: - dependency: transitive - description: - name: frontend_server_client - sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 - url: "https://pub.dev" - source: hosted - version: "4.0.0" - get_it: - dependency: "direct main" - description: - name: get_it - sha256: d85128a5dae4ea777324730dc65edd9c9f43155c109d5cc0a69cab74139fbac1 - url: "https://pub.dev" - source: hosted - version: "7.7.0" - glob: - dependency: transitive - description: - name: glob - sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - google_identity_services_web: - dependency: transitive - description: - name: google_identity_services_web - sha256: "5be191523702ba8d7a01ca97c17fca096822ccf246b0a9f11923a6ded06199b6" - url: "https://pub.dev" - source: hosted - version: "0.3.1+4" - google_sign_in: - dependency: "direct main" - description: - name: google_sign_in - sha256: "0b8787cb9c1a68ad398e8010e8c8766bfa33556d2ab97c439fb4137756d7308f" - url: "https://pub.dev" - source: hosted - version: "6.2.1" - google_sign_in_android: - dependency: transitive - description: - name: google_sign_in_android - sha256: "5a47ebec9af97daf0822e800e4f101c3340b5ebc3f6898cf860c1a71b53cf077" - url: "https://pub.dev" - source: hosted - version: "6.1.28" - google_sign_in_ios: - dependency: transitive - description: - name: google_sign_in_ios - sha256: a058c9880be456f21e2e8571c1126eaacd570bdc5b6c6d9d15aea4bdf22ca9fe - url: "https://pub.dev" - source: hosted - version: "5.7.6" - google_sign_in_platform_interface: - dependency: transitive - description: - name: google_sign_in_platform_interface - sha256: "1f6e5787d7a120cc0359ddf315c92309069171306242e181c09472d1b00a2971" - url: "https://pub.dev" - source: hosted - version: "2.4.5" - google_sign_in_web: - dependency: transitive - description: - name: google_sign_in_web - sha256: "042805a21127a85b0dc46bba98a37926f17d2439720e8a459d27045d8ef68055" - url: "https://pub.dev" - source: hosted - version: "0.12.4+2" - graphs: - dependency: transitive - description: - name: graphs - sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" - url: "https://pub.dev" - source: hosted - version: "2.3.2" - gtk: - dependency: transitive - description: - name: gtk - sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c - url: "https://pub.dev" - source: hosted - version: "2.1.0" - html: - dependency: transitive - description: - name: html - sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" - url: "https://pub.dev" - source: hosted - version: "0.15.4" - http: - dependency: transitive - description: - name: http - sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 - url: "https://pub.dev" - source: hosted - version: "1.2.2" - http_certificate_pinning: - dependency: transitive - description: - name: http_certificate_pinning - sha256: "12b4848113d50c570af93e94bed64004caba14dde87719310d1e4556686c0afa" - url: "https://pub.dev" - source: hosted - version: "2.1.3" - http_multi_server: - dependency: transitive - description: - name: http_multi_server - sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" - url: "https://pub.dev" - source: hosted - version: "3.2.1" - http_parser: - dependency: transitive - description: - name: http_parser - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" - url: "https://pub.dev" - source: hosted - version: "4.0.2" - image: - dependency: transitive - description: - name: image - sha256: "2237616a36c0d69aef7549ab439b833fb7f9fb9fc861af2cc9ac3eedddd69ca8" - url: "https://pub.dev" - source: hosted - version: "4.2.0" - in_app_purchase: - dependency: "direct main" - description: - name: in_app_purchase - sha256: "960f26a08d9351fb8f89f08901f8a829d41b04d45a694b8f776121d9e41dcad6" - url: "https://pub.dev" - source: hosted - version: "3.2.0" - in_app_purchase_android: - dependency: "direct main" - description: - name: in_app_purchase_android - sha256: "4c3f46f736a31902ec5392a75f41ea1ad292f27db00ed51153f3912d076d0efa" - url: "https://pub.dev" - source: hosted - version: "0.3.6+5" - in_app_purchase_platform_interface: - dependency: transitive - description: - name: in_app_purchase_platform_interface - sha256: "1d353d38251da5b9fea6635c0ebfc6bb17a2d28d0e86ea5e083bf64244f1fb4c" - url: "https://pub.dev" - source: hosted - version: "1.4.0" - in_app_purchase_storekit: - dependency: transitive - description: - name: in_app_purchase_storekit - sha256: "28a0577d9f4a5bdfb7aad96c347193e1225cc335c8003fbb98b875a67dab34cc" - url: "https://pub.dev" - source: hosted - version: "0.3.17+3" - injectable: - dependency: transitive - description: - name: injectable - sha256: "69874ba3ec10e3a0de3f519a184442878291d928f3299d718813f24642585198" - url: "https://pub.dev" - source: hosted - version: "2.4.4" - injectable_generator: - dependency: "direct dev" - description: - name: injectable_generator - sha256: af403d76c7b18b4217335e0075e950cd0579fd7f8d7bd47ee7c85ada31680ba1 - url: "https://pub.dev" - source: hosted - version: "2.6.2" - io: - dependency: transitive - description: - name: io - sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" - url: "https://pub.dev" - source: hosted - version: "1.0.4" - jailbreak_root_detection: - dependency: "direct main" - description: - name: jailbreak_root_detection - sha256: ee98f5e69b43c758ee1779204b0926a09f0371eebe1462b876fb6a8bd130cbe5 - url: "https://pub.dev" - source: hosted - version: "1.1.5" - js: - dependency: transitive - description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.dev" - source: hosted - version: "0.6.7" - json_annotation: - dependency: "direct main" - description: - name: json_annotation - sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" - url: "https://pub.dev" - source: hosted - version: "4.9.0" - json_serializable: - dependency: "direct main" - description: - name: json_serializable - sha256: ea1432d167339ea9b5bb153f0571d0039607a873d6e04e0117af043f14a1fd4b - url: "https://pub.dev" - source: hosted - version: "6.8.0" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" - url: "https://pub.dev" - source: hosted - version: "10.0.4" - leak_tracker_flutter_testing: - dependency: transitive - description: - name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" - url: "https://pub.dev" - source: hosted - version: "3.0.3" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" - url: "https://pub.dev" - source: hosted - version: "3.0.1" - lints: - dependency: transitive - description: - name: lints - sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" - url: "https://pub.dev" - source: hosted - version: "4.0.0" - logging: - dependency: transitive - description: - name: logging - sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - matcher: - dependency: transitive - description: - name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb - url: "https://pub.dev" - source: hosted - version: "0.12.16+1" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" - url: "https://pub.dev" - source: hosted - version: "0.8.0" - meta: - dependency: transitive - description: - name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" - url: "https://pub.dev" - source: hosted - version: "1.12.0" - mime: - dependency: transitive - description: - name: mime - sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" - url: "https://pub.dev" - source: hosted - version: "1.0.5" - mnemo_cards_common: - dependency: "direct main" - description: - path: "../mnemo_cards_common" - relative: true - source: path - version: "0.0.1" - mnemo_cards_frontend_common: - dependency: "direct main" - description: - path: "../mnemo_cards_frontend_common" - relative: true - source: path - version: "0.0.1" - nested: - dependency: transitive - description: - name: nested - sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" - url: "https://pub.dev" - source: hosted - version: "1.0.0" - no_screenshot: - dependency: "direct main" - description: - name: no_screenshot - sha256: "93d4079541d1413261d5552ee09e23d28c183c171719079575d69809233884d0" - url: "https://pub.dev" - source: hosted - version: "0.2.0" - package_config: - dependency: transitive - description: - name: package_config - sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - package_info_plus: - dependency: "direct main" - description: - name: package_info_plus - sha256: "4de6c36df77ffbcef0a5aefe04669d33f2d18397fea228277b852a2d4e58e860" - url: "https://pub.dev" - source: hosted - version: "8.0.1" - package_info_plus_platform_interface: - dependency: transitive - description: - name: package_info_plus_platform_interface - sha256: ac1f4a4847f1ade8e6a87d1f39f5d7c67490738642e2542f559ec38c37489a66 - url: "https://pub.dev" - source: hosted - version: "3.0.1" - path: - dependency: transitive - description: - name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" - url: "https://pub.dev" - source: hosted - version: "1.9.0" - path_provider: - dependency: "direct main" - description: - name: path_provider - sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378 - url: "https://pub.dev" - source: hosted - version: "2.1.4" - path_provider_android: - dependency: transitive - description: - name: path_provider_android - sha256: "490539678396d4c3c0b06efdaab75ae60675c3e0c66f72bc04c2e2c1e0e2abeb" - url: "https://pub.dev" - source: hosted - version: "2.2.9" - path_provider_foundation: - dependency: transitive - description: - name: path_provider_foundation - sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 - url: "https://pub.dev" - source: hosted - version: "2.4.0" - path_provider_linux: - dependency: transitive - description: - name: path_provider_linux - sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 - url: "https://pub.dev" - source: hosted - version: "2.2.1" - path_provider_platform_interface: - dependency: transitive - description: - name: path_provider_platform_interface - sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - path_provider_windows: - dependency: transitive - description: - name: path_provider_windows - sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 - url: "https://pub.dev" - source: hosted - version: "2.3.0" - petitparser: - dependency: transitive - description: - name: petitparser - sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 - url: "https://pub.dev" - source: hosted - version: "6.0.2" - platform: - dependency: transitive - description: - name: platform - sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" - url: "https://pub.dev" - source: hosted - version: "3.1.5" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" - url: "https://pub.dev" - source: hosted - version: "2.1.8" - pool: - dependency: transitive - description: - name: pool - sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" - url: "https://pub.dev" - source: hosted - version: "1.5.1" - provider: - dependency: transitive - description: - name: provider - sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c - url: "https://pub.dev" - source: hosted - version: "6.1.2" - pub_semver: - dependency: transitive - description: - name: pub_semver - sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - pubspec_parse: - dependency: transitive - description: - name: pubspec_parse - sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8 - url: "https://pub.dev" - source: hosted - version: "1.3.0" - recase: - dependency: transitive - description: - name: recase - sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213 - url: "https://pub.dev" - source: hosted - version: "4.1.0" - reorderables: - dependency: "direct main" - description: - name: reorderables - sha256: "004a886e4878df1ee27321831c838bc1c976311f4ca6a74ce7d561e506540a77" - url: "https://pub.dev" - source: hosted - version: "0.6.0" - retrofit: - dependency: transitive - description: - name: retrofit - sha256: "13a2865c0d97da580ea4e3c64d412d81f365fd5b26be2a18fca9582e021da37a" - url: "https://pub.dev" - source: hosted - version: "4.1.0" - rxdart: - dependency: "direct main" - description: - name: rxdart - sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" - url: "https://pub.dev" - source: hosted - version: "0.28.0" - shared_preferences: - dependency: "direct main" - description: - name: shared_preferences - sha256: c272f9cabca5a81adc9b0894381e9c1def363e980f960fa903c604c471b22f68 - url: "https://pub.dev" - source: hosted - version: "2.3.1" - shared_preferences_android: - dependency: transitive - description: - name: shared_preferences_android - sha256: "041be4d9d2dc6079cf342bc8b761b03787e3b71192d658220a56cac9c04a0294" - url: "https://pub.dev" - source: hosted - version: "2.3.0" - shared_preferences_foundation: - dependency: transitive - description: - name: shared_preferences_foundation - sha256: "671e7a931f55a08aa45be2a13fe7247f2a41237897df434b30d2012388191833" - url: "https://pub.dev" - source: hosted - version: "2.5.0" - shared_preferences_linux: - dependency: transitive - description: - name: shared_preferences_linux - sha256: "2ba0510d3017f91655b7543e9ee46d48619de2a2af38e5c790423f7007c7ccc1" - url: "https://pub.dev" - source: hosted - version: "2.4.0" - shared_preferences_platform_interface: - dependency: transitive - description: - name: shared_preferences_platform_interface - sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" - url: "https://pub.dev" - source: hosted - version: "2.4.1" - shared_preferences_web: - dependency: transitive - description: - name: shared_preferences_web - sha256: "59dc807b94d29d52ddbb1b3c0d3b9d0a67fc535a64e62a5542c8db0513fcb6c2" - url: "https://pub.dev" - source: hosted - version: "2.4.1" - shared_preferences_windows: - dependency: transitive - description: - name: shared_preferences_windows - sha256: "398084b47b7f92110683cac45c6dc4aae853db47e470e5ddcd52cab7f7196ab2" - url: "https://pub.dev" - source: hosted - version: "2.4.0" - shelf: - dependency: transitive - description: - name: shelf - sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 - url: "https://pub.dev" - source: hosted - version: "1.4.1" - shelf_web_socket: - dependency: transitive - description: - name: shelf_web_socket - sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" - url: "https://pub.dev" - source: hosted - version: "2.0.0" - shimmer: - dependency: "direct main" - description: - name: shimmer - sha256: "5f88c883a22e9f9f299e5ba0e4f7e6054857224976a5d9f839d4ebdc94a14ac9" - url: "https://pub.dev" - source: hosted - version: "3.0.0" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_gen: - dependency: transitive - description: - name: source_gen - sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" - url: "https://pub.dev" - source: hosted - version: "1.5.0" - source_helper: - dependency: transitive - description: - name: source_helper - sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd" - url: "https://pub.dev" - source: hosted - version: "1.3.4" - source_span: - dependency: transitive - description: - name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" - url: "https://pub.dev" - source: hosted - version: "1.10.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" - url: "https://pub.dev" - source: hosted - version: "1.11.1" - story: - dependency: "direct main" - description: - name: story - sha256: "0cff3c02d5ad1d9c1cf79481b8fe4a4f2f859e56b351644e96b8b209e6a110a5" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 - url: "https://pub.dev" - source: hosted - version: "2.1.2" - stream_transform: - dependency: transitive - description: - name: stream_transform - sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - test_api: - dependency: transitive - description: - name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" - url: "https://pub.dev" - source: hosted - version: "0.7.0" - timing: - dependency: transitive - description: - name: timing - sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" - url: "https://pub.dev" - source: hosted - version: "1.0.1" - typed_data: - dependency: transitive - description: - name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c - url: "https://pub.dev" - source: hosted - version: "1.3.2" - universal_io: - dependency: transitive - description: - name: universal_io - sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad" - url: "https://pub.dev" - source: hosted - version: "2.2.2" - url_launcher: - dependency: "direct main" - description: - name: url_launcher - sha256: "21b704ce5fa560ea9f3b525b43601c678728ba46725bab9b01187b4831377ed3" - url: "https://pub.dev" - source: hosted - version: "6.3.0" - url_launcher_android: - dependency: transitive - description: - name: url_launcher_android - sha256: "94d8ad05f44c6d4e2ffe5567ab4d741b82d62e3c8e288cc1fcea45965edf47c9" - url: "https://pub.dev" - source: hosted - version: "6.3.8" - url_launcher_ios: - dependency: transitive - description: - name: url_launcher_ios - sha256: e43b677296fadce447e987a2f519dcf5f6d1e527dc35d01ffab4fff5b8a7063e - url: "https://pub.dev" - source: hosted - version: "6.3.1" - url_launcher_linux: - dependency: transitive - description: - name: url_launcher_linux - sha256: e2b9622b4007f97f504cd64c0128309dfb978ae66adbe944125ed9e1750f06af - url: "https://pub.dev" - source: hosted - version: "3.2.0" - url_launcher_macos: - dependency: transitive - description: - name: url_launcher_macos - sha256: "9a1a42d5d2d95400c795b2914c36fdcb525870c752569438e4ebb09a2b5d90de" - url: "https://pub.dev" - source: hosted - version: "3.2.0" - url_launcher_platform_interface: - dependency: transitive - description: - name: url_launcher_platform_interface - sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" - url: "https://pub.dev" - source: hosted - version: "2.3.2" - url_launcher_web: - dependency: transitive - description: - name: url_launcher_web - sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e" - url: "https://pub.dev" - source: hosted - version: "2.3.3" - url_launcher_windows: - dependency: transitive - description: - name: url_launcher_windows - sha256: "49c10f879746271804767cb45551ec5592cdab00ee105c06dddde1a98f73b185" - url: "https://pub.dev" - source: hosted - version: "3.1.2" - uuid: - dependency: transitive - description: - name: uuid - sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" - url: "https://pub.dev" - source: hosted - version: "3.0.7" - vector_math: - dependency: transitive - description: - name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" - url: "https://pub.dev" - source: hosted - version: "14.2.1" - watcher: - dependency: transitive - description: - name: watcher - sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - web: - dependency: transitive - description: - name: web - sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" - url: "https://pub.dev" - source: hosted - version: "0.5.1" - web_socket: - dependency: transitive - description: - name: web_socket - sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" - url: "https://pub.dev" - source: hosted - version: "0.1.6" - web_socket_channel: - dependency: transitive - description: - name: web_socket_channel - sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f" - url: "https://pub.dev" - source: hosted - version: "3.0.1" - webview_flutter: - dependency: "direct main" - description: - name: webview_flutter - sha256: "6869c8786d179f929144b4a1f86e09ac0eddfe475984951ea6c634774c16b522" - url: "https://pub.dev" - source: hosted - version: "4.8.0" - webview_flutter_android: - dependency: transitive - description: - name: webview_flutter_android - sha256: c66651fba15f9d7ddd31daec42da8d6bce46c85610a7127e3ebcb39a4395c3c9 - url: "https://pub.dev" - source: hosted - version: "3.16.6" - webview_flutter_platform_interface: - dependency: transitive - description: - name: webview_flutter_platform_interface - sha256: d937581d6e558908d7ae3dc1989c4f87b786891ab47bb9df7de548a151779d8d - url: "https://pub.dev" - source: hosted - version: "2.10.0" - webview_flutter_wkwebview: - dependency: transitive - description: - name: webview_flutter_wkwebview - sha256: "9c62cc46fa4f2d41e10ab81014c1de470a6c6f26051a2de32111b2ee55287feb" - url: "https://pub.dev" - source: hosted - version: "3.14.0" - win32: - dependency: transitive - description: - name: win32 - sha256: "015002c060f1ae9f41a818f2d5640389cc05283e368be19dc8d77cecb43c40c9" - url: "https://pub.dev" - source: hosted - version: "5.5.3" - win32_registry: - dependency: transitive - description: - name: win32_registry - sha256: "723b7f851e5724c55409bb3d5a32b203b3afe8587eaf5dafb93a5fed8ecda0d6" - url: "https://pub.dev" - source: hosted - version: "1.1.4" - xdg_directories: - dependency: transitive - description: - name: xdg_directories - sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d - url: "https://pub.dev" - source: hosted - version: "1.0.4" - xml: - dependency: transitive - description: - name: xml - sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 - url: "https://pub.dev" - source: hosted - version: "6.5.0" - yaml: - dependency: transitive - description: - name: yaml - sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" - url: "https://pub.dev" - source: hosted - version: "3.1.2" - yandex_mobileads: - dependency: "direct main" - description: - name: yandex_mobileads - sha256: "836d886d1eab1e42dee819415e0d5320bb3003632b879a470a2190ec0b940e36" - url: "https://pub.dev" - source: hosted - version: "7.1.0" - yookassa_client: - dependency: "direct main" - description: - name: yookassa_client - sha256: "667d04d0e2d8c7e5180d26a3966587cc99d38fb887d318e36562a202d1adb6e2" - url: "https://pub.dev" - source: hosted - version: "1.0.2" -sdks: - dart: ">=3.4.0 <4.0.0" - flutter: ">=3.22.0" diff --git a/pubspec.yaml b/pubspec.yaml deleted file mode 100644 index 73b8c20..0000000 --- a/pubspec.yaml +++ /dev/null @@ -1,171 +0,0 @@ -name: mnemo_cards -description: Mnemo -publish_to: 'none' - -version: 1.0.4+18 - -environment: - sdk: '>=3.1.3 <4.0.0' - -dependencies: - flutter: - sdk: flutter - - mnemo_cards_common: - path: ../mnemo_cards_common - - mnemo_cards_frontend_common: - path: ../mnemo_cards_frontend_common - - - - get_it: - shared_preferences: ^2.2.3 - flutter_secure_storage: ^9.2.2 - rxdart: -# firebase_core: ^3.3.0 - # firebase_crashlytics: - # firebase_storage: - cloud_firestore: ^5.2.1 - freezed: - json_serializable: - json_annotation: ^4.7.0 - auto_size_text: ^3.0.0 - path_provider: -# google_fonts: - auto_route: ^9.2.0 - flutter_tts: ^4.0.2 - dio: ^5.3.3 - copy_with_extension_gen: ^5.0.4 - archive: ^3.4.6 - shimmer: ^3.0.0 - google_sign_in: - device_info_plus: ^10.1.1 - package_info_plus: ^8.0.0 - yandex_mobileads: ^7.1.0 - firebase_crashlytics: ^4.0.4 - firebase_analytics: ^11.2.1 - in_app_purchase: ^3.2.0 - in_app_purchase_android: - dot_navigation_bar: ^1.0.2 - reorderables: ^0.6.0 - story: ^1.1.0 - flutter_screenutil: ^5.9.0 - fl_chart: ^0.68.0 - yookassa_client: ^1.0.2 - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 - webview_flutter: ^4.7.0 - flutter_colorpicker: ^1.1.0 - jailbreak_root_detection: ^1.1.1 - no_screenshot: ^0.2.0 - flutter_widget_from_html_core: ^0.15.1 - - file_picker: - url_launcher: ^6.2.6 - app_links: ^6.2.0 - -# RUSTORE -# flutter_rustore_billing: ^6.0.1 - - collection: any - freezed_annotation: any - copy_with_extension: any -dev_dependencies: - flutter_test: - sdk: flutter - auto_route_generator: - build_runner: - injectable_generator: - flutter_lints: ^4.0.0 - - flutter_launcher_icons: ^0.13.1 - flutter_native_splash: ^2.4.0 - -# The following section is specific to Flutter packages. -flutter: - uses-material-design: true - - # To add assets to your application, add an assets section, like this: - assets: - - images/cerdo.jpg - - assets/ - - icons/ - - fonts: - - family: Nunito - fonts: - - asset: fonts/Nunito-VariableFont_wght.ttf - -flutter_launcher_icons: - android: "launcher_icon" - ios: true - image_path: "images/icon.png" - min_sdk_android: 21 # android min sdk min:16, default 21 - -flutter_native_splash: - # Only one parameter can be used, color and background_image cannot both be set. - color: "#ffffff" - #background_image: "assets/background.png" - - # Optional parameters are listed below. To enable a parameter, uncomment the line by removing - # the leading # character. - - # The image parameter allows you to specify an image used in the splash screen. It must be a - # png file and should be sized for 4x pixel density. - image: images/cerdo_big.jpg - - # The branding property allows you to specify an image used as branding in the splash screen. - # It must be a png file. It is supported for Android, iOS and the Web. For Android 12, - # see the Android 12 section below. - #branding: assets/dart.png - - # To position the branding image at the bottom of the screen you can use bottom, bottomRight, - # and bottomLeft. The default values is bottom if not specified or specified something else. - #branding_mode: bottom - - # The color_dark, background_image_dark, image_dark, branding_dark are parameters that set the background - # and image when the device is in dark mode. If they are not specified, the app will use the - # parameters from above. If the image_dark parameter is specified, color_dark or - # background_image_dark must be specified. color_dark and background_image_dark cannot both be - # set. - #color_dark: "#042a49" - #background_image_dark: "assets/dark-background.png" - #image_dark: assets/splash-invert.png - #branding_dark: assets/dart_dark.png - - # From Android 12 onwards, the splash screen is handled differently than in previous versions. - # Please visit https://developer.android.com/guide/topics/ui/splash-screen - # Following are specific parameters for Android 12+. - android_12: - # The image parameter sets the splash screen icon image. If this parameter is not specified, - # the app's launcher icon will be used instead. - # Please note that the splash screen will be clipped to a circle on the center of the screen. - # App icon with an icon background: This should be 960×960 pixels, and fit within a circle - # 640 pixels in diameter. - # App icon without an icon background: This should be 1152×1152 pixels, and fit within a circle - # 768 pixels in diameter. - image: images/cerdo_big.jpg - - # Splash screen background color. - color: "#ffffff" - - # App icon background color. - #icon_background_color: "#111111" - - # The branding property allows you to specify an image used as branding in the splash screen. - #branding: assets/dart.png - - # The image_dark, color_dark, icon_background_color_dark, and branding_dark set values that - # apply when the device is in dark mode. If they are not specified, the app will use the - # parameters from above. - #image_dark: assets/android12splash-invert.png - #color_dark: "#042a49" - #icon_background_color_dark: "#eeeeee" - - # The android, ios and web parameters can be used to disable generating a splash screen on a given - # platform. - #android: false - #ios: false - #web: false diff --git a/sha.sh b/sha.sh deleted file mode 100644 index 91846cd..0000000 --- a/sha.sh +++ /dev/null @@ -1,2 +0,0 @@ -cd android -./gradlew signingReport \ No newline at end of file diff --git a/some.json b/some.json deleted file mode 100644 index a2adec2..0000000 --- a/some.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "admin": true, - "id": 3, - "name": null, - "purchases": [ - "27", - "28" - ], - "subscription": false -} \ No newline at end of file diff --git a/test.json b/test.json deleted file mode 100644 index 54fbffa..0000000 --- a/test.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "cards": [ - 1, - 2, - -1, - 3, - 4, - 5, - 6 - ], - "color": "0xff8CCBFF", - "cover": "cards/pata.jpg", - "id": 91906190, - "name": "Original", - "size": 7, - "version": "10" -} \ No newline at end of file diff --git a/v b/v deleted file mode 100644 index e69de29..0000000