Frontend first draft

This commit is contained in:
Arseni
2025-11-13 15:06:15 +03:00
parent e47f343afb
commit ddb54ddfdc
504 changed files with 25498 additions and 1 deletions

45
frontend/pweb/.gitignore vendored Normal file
View File

@@ -0,0 +1,45 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release

30
frontend/pweb/.metadata Normal file
View File

@@ -0,0 +1,30 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "adc901062556672b4138e18a4dc62a4be8f4b3c2"
channel: "stable"
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
- platform: web
create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'

16
frontend/pweb/README.md Normal file
View File

@@ -0,0 +1,16 @@
# web
A new Flutter project.
## 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:
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
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.

View File

@@ -0,0 +1,28 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at https://dart.dev/lints.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

14
frontend/pweb/android/.gitignore vendored Normal file
View File

@@ -0,0 +1,14 @@
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
.cxx/
# Remember to never publicly share your keystore.
# See https://flutter.dev/to/reference-keystore
key.properties
**/*.keystore
**/*.jks

View File

@@ -0,0 +1,44 @@
plugins {
id("com.android.application")
id("kotlin-android")
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id("dev.flutter.flutter-gradle-plugin")
}
android {
namespace = "com.example.web"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString()
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.example.web"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.getByName("debug")
}
}
}
flutter {
source = "../.."
}

View File

@@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

View File

@@ -0,0 +1,45 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="web"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</intent>
</queries>
</manifest>

View File

@@ -0,0 +1,5 @@
package com.example.web
import io.flutter.embedding.android.FlutterActivity
class MainActivity : FlutterActivity()

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

View File

@@ -0,0 +1,21 @@
allprojects {
repositories {
google()
mavenCentral()
}
}
val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get()
rootProject.layout.buildDirectory.value(newBuildDir)
subprojects {
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
project.layout.buildDirectory.value(newSubprojectBuildDir)
}
subprojects {
project.evaluationDependsOn(":app")
}
tasks.register<Delete>("clean") {
delete(rootProject.layout.buildDirectory)
}

View File

@@ -0,0 +1,3 @@
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true

View File

@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip

View File

@@ -0,0 +1,25 @@
pluginManagement {
val flutterSdkPath = run {
val properties = java.util.Properties()
file("local.properties").inputStream().use { properties.load(it) }
val flutterSdkPath = properties.getProperty("flutter.sdk")
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
flutterSdkPath
}
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.7.3" apply false
id("org.jetbrains.kotlin.android") version "2.1.0" apply false
}
include(":app")

25
frontend/pweb/entrypoint.sh Executable file
View File

@@ -0,0 +1,25 @@
#!/bin/sh
replace_env_var() {
local var_name=$1
local placeholder="%%$var_name%%"
local value=$(eval echo \$$var_name)
# inject value to the index.html
sed -i "s|$placeholder|$value|g" /usr/share/pweb/index.html
}
echo "Starting Container"
replace_env_var "WS_PROTOCOL"
replace_env_var "WS_ENDPOINT"
replace_env_var "API_PROTOCOL"
replace_env_var "SERVICE_HOST"
replace_env_var "API_ENDPOINT"
replace_env_var "AMPLITUDE_SECRET"
replace_env_var "DEFAULT_LOCALE"
replace_env_var "DEFAULT_CURRENCY"
echo "Passing by launch command"
# Execute the passed command (e.g., starting Nginx)
# exec "$@"
exec nginx -g 'daemon off;'

34
frontend/pweb/ios/.gitignore vendored Normal file
View File

@@ -0,0 +1,34 @@
**/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

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>12.0</string>
</dict>
</plist>

View File

@@ -0,0 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"

View File

@@ -0,0 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"

43
frontend/pweb/ios/Podfile Normal file
View File

@@ -0,0 +1,43 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '12.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!
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

View File

@@ -0,0 +1,61 @@
PODS:
- Flutter (1.0.0)
- flutter_timezone (0.0.1):
- Flutter
- image_picker_ios (0.0.1):
- Flutter
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- share_plus (0.0.1):
- Flutter
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
- sqflite_darwin (0.0.4):
- Flutter
- FlutterMacOS
- url_launcher_ios (0.0.1):
- Flutter
DEPENDENCIES:
- Flutter (from `Flutter`)
- flutter_timezone (from `.symlinks/plugins/flutter_timezone/ios`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
EXTERNAL SOURCES:
Flutter:
:path: Flutter
flutter_timezone:
:path: ".symlinks/plugins/flutter_timezone/ios"
image_picker_ios:
:path: ".symlinks/plugins/image_picker_ios/ios"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
share_plus:
:path: ".symlinks/plugins/share_plus/ios"
shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
sqflite_darwin:
:path: ".symlinks/plugins/sqflite_darwin/darwin"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"
SPEC CHECKSUMS:
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_timezone: 7c838e17ffd4645d261e87037e5bebf6d38fe544
image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
PODFILE CHECKSUM: 4305caec6b40dde0ae97be1573c53de1882a07e5
COCOAPODS: 1.16.2

View File

@@ -0,0 +1,731 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
2C56D73CE91635539A9A15DA /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B53E22FD6A63D40B1C3D0B8 /* Pods_RunnerTests.framework */; };
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 */; };
7FDB1F7A73965C7C67C9A58A /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3191231068A580A1B7EB0FC1 /* 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 */; };
/* 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 */
11E3F4B5E7F65D8227C50E6E /* 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 = "<group>"; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
3191231068A580A1B7EB0FC1 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
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 = "<group>"; };
61B869663D462553198E2AC1 /* 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 = "<group>"; };
6B53E22FD6A63D40B1C3D0B8 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
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 = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
995403D706740B49D3A8D16E /* 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 = "<group>"; };
A6678C3B3551DF40750DA02B /* 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 = "<group>"; };
A6E3941EC81B9DAA2584BE2B /* 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 = "<group>"; };
B5C149D70208E226E8B02D31 /* 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 = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
8E6A3AB46FFEEA916FAA437C /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
2C56D73CE91635539A9A15DA /* Pods_RunnerTests.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
7FDB1F7A73965C7C67C9A58A /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
331C8082294A63A400263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
331C807B294A618700263BE5 /* RunnerTests.swift */,
);
path = RunnerTests;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */,
F8666F8B24C7533EE74CB803 /* Pods */,
C315FB501BE68ED01CE7B90E /* Frameworks */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
331C8081294A63A400263BE5 /* RunnerTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
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 = "<group>";
};
C315FB501BE68ED01CE7B90E /* Frameworks */ = {
isa = PBXGroup;
children = (
3191231068A580A1B7EB0FC1 /* Pods_Runner.framework */,
6B53E22FD6A63D40B1C3D0B8 /* Pods_RunnerTests.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
F8666F8B24C7533EE74CB803 /* Pods */ = {
isa = PBXGroup;
children = (
A6E3941EC81B9DAA2584BE2B /* Pods-Runner.debug.xcconfig */,
995403D706740B49D3A8D16E /* Pods-Runner.release.xcconfig */,
11E3F4B5E7F65D8227C50E6E /* Pods-Runner.profile.xcconfig */,
A6678C3B3551DF40750DA02B /* Pods-RunnerTests.debug.xcconfig */,
61B869663D462553198E2AC1 /* Pods-RunnerTests.release.xcconfig */,
B5C149D70208E226E8B02D31 /* Pods-RunnerTests.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
331C8080294A63A400263BE5 /* RunnerTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
FB52EAF123C25402F515699A /* [CP] Check Pods Manifest.lock */,
331C807D294A63A400263BE5 /* Sources */,
331C807F294A63A400263BE5 /* Resources */,
8E6A3AB46FFEEA916FAA437C /* 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 = (
1D3F480F2DA3F801E5B09957 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
CA85DA6319399F521D13FFCD /* [CP] Embed Pods Frameworks */,
);
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 */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
1D3F480F2DA3F801E5B09957 /* [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";
};
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";
};
CA85DA6319399F521D13FFCD /* [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;
};
FB52EAF123C25402F515699A /* [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 = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
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;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
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 = 12.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 = 88GA9VDY6Q;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.web;
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 = A6678C3B3551DF40750DA02B /* 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.example.web.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 = 61B869663D462553198E2AC1 /* 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.example.web.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 = B5C149D70208E226E8B02D31 /* 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.example.web.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;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
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;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
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 = 12.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;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
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;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
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 = 12.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 = 88GA9VDY6Q;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.web;
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 = 88GA9VDY6Q;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.web;
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 */;
}

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View File

@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "331C8080294A63A400263BE5"
BuildableName = "RunnerTests.xctest"
BlueprintName = "RunnerTests"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
enableGPUValidationMode = "1"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View File

@@ -0,0 +1,13 @@
import Flutter
import UIKit
@main
@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)
}
}

View File

@@ -0,0 +1 @@
{"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":"57x57","idiom":"iphone","filename":"Icon-App-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@2x.png","scale":"2x"},{"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":"50x50","idiom":"ipad","filename":"Icon-App-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@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"}}

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

View File

@@ -0,0 +1,5 @@
# 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.

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Web</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>web</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1 @@
#import "GeneratedPluginRegistrant.h"

View File

@@ -0,0 +1,12 @@
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.
}
}

5
frontend/pweb/l10n.yaml Normal file
View File

@@ -0,0 +1,5 @@
arb-dir: lib/l10n
output-dir: lib/generated/i18n
template-arb-file: en.arb
output-localization-file: app_localizations.dart
untranslated-messages-file: untranslated.txt

View File

@@ -0,0 +1,34 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:pshared/config/constants.dart';
import 'package:pshared/provider/locale.dart';
import 'package:pweb/app/router/router.dart';
import 'package:pshared/generated/i18n/ps_localizations.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
final _router = createRouter();
class PayApp extends StatelessWidget {
const PayApp({super.key});
@override
Widget build(BuildContext context) => MaterialApp.router(
title: 'Profee Pay',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Constants.themeColor),
useMaterial3: true,
),
routerConfig: _router,
localizationsDelegates: [
...PSLocalizations.localizationsDelegates,
...AppLocalizations.localizationsDelegates,
],
supportedLocales: AppLocalizations.supportedLocales,
locale: context.watch<LocaleProvider>().locale,
);
}

View File

@@ -0,0 +1,70 @@
import 'package:flutter/material.dart';
import 'package:intl/find_locale.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:pshared/provider/locale.dart';
String _localeVarStorageName() {
return 'mcrm_last_locale';
}
Locale _selectDefaultLocale(List<Locale> appLocales, Locale defaultLocale) {
return appLocales.contains(defaultLocale)
? defaultLocale
: appLocales.isEmpty
? throw ArgumentError('empty application locales list', 'appLocales')
: appLocales.first;
}
class LocaleManager {
late SharedPreferences _prefs;
final List<Locale> appLocales;
final Locale _defaultLocale;
final LocaleProvider localeProvider;
LocaleManager(this.localeProvider, this.appLocales, Locale defaultLocale)
: _defaultLocale = _selectDefaultLocale(appLocales, defaultLocale) {
SharedPreferences.getInstance().then((prefs) {
_prefs = prefs;
_initializeLocaleProvider();
});
}
Future<void> _initializeLocaleProvider() async {
final initialLocale = await _getInitialLocale();
localeProvider.setLocale(initialLocale);
localeProvider.addListener(_onLocaleChanged);
}
Future<Locale> _getInitialLocale() async {
final locale = await _pickLocale();
return appLocales.contains(locale) ? locale : _defaultLocale;
}
Future<Locale> _pickLocale() async {
String? savedLocaleCode = _prefs.getString(_localeVarStorageName());
if (savedLocaleCode != null) {
return Locale(savedLocaleCode);
}
String systemLocaleString = await findSystemLocale();
final List<String> localeParts = systemLocaleString.split('_');
final Locale systemLocale = Locale(localeParts[0]);
final res = appLocales.contains(systemLocale);
return res ? systemLocale : _defaultLocale;
}
Future<bool> saveLocale(Locale locale) async {
return _prefs.setString(_localeVarStorageName(), locale.toString());
}
Future<bool> _onLocaleChanged() async {
return saveLocale(localeProvider.locale);
}
}

View File

@@ -0,0 +1,15 @@
enum PageParams {
token,
projectRef,
roleRef,
taskRef,
invitationRef,
}
String routerPageParam(PageParams param) {
return ':${param.name}';
}
String routerAddParam(PageParams param) {
return '/${routerPageParam(param)}';
}

View File

@@ -0,0 +1,60 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
enum Pages {
root,
sfactor,
login,
methods,
verify,
signup,
settings,
dashboard,
profile,
recipients,
users,
roles,
permissions,
invitations,
}
String routerPath(String page) {
return '/$page';
}
String routerPage(Pages page) {
return page == Pages.root ? '/' : routerPath(page.name);
}
String _pagePath(Pages page, {String? objectRef}) => _pagesPath([page], objectRef: objectRef);
String _pagesPath(List<Pages> pages, {String? objectRef}) {
final path = pages.map(routerPage).join();
return objectRef != null ? '$path/$objectRef' : path;
}
void navigateAndReplace(BuildContext context, Pages page, {String? objectRef, Object? extra}) {
context.go(_pagePath(page, objectRef: objectRef), extra: extra);
}
void navigate(BuildContext context, Pages page, {String? objectRef, Object? extra}) {
navigatePages(context, [page], objectRef: objectRef, extra: extra);
}
void navigatePages(BuildContext context, List<Pages> pages, {String? objectRef, Object? extra}) {
context.push(_pagesPath(pages, objectRef: objectRef), extra: extra);
}
void navigateNamed(BuildContext context, Pages page, {String? objectRef, Object? extra}) {
context.pushNamed(page.name, extra: extra);
}
void navigateNamedAndReplace(BuildContext context, Pages page, {String? objectRef, Object? extra}) {
context.replaceNamed(page.name, extra: extra);
}
void navigateNext(BuildContext context, Pages page, {Object? extra}) {
WidgetsBinding.instance.addPostFrameCallback((_) => navigate(context, page, extra: extra));
}

View File

@@ -0,0 +1,57 @@
import 'package:go_router/go_router.dart';
import 'package:pweb/app/router/pages.dart';
import 'package:pweb/pages/2fa/page.dart';
import 'package:pweb/pages/signup/page.dart';
import 'package:pweb/widgets/sidebar/page.dart';
import 'package:pweb/pages/login/page.dart';
import 'package:pweb/pages/errors/not_found.dart';
GoRouter createRouter() => GoRouter(
debugLogDiagnostics: true,
routes: <RouteBase>[
GoRoute(
name: Pages.root.name,
path: routerPage(Pages.root),
builder: (_, __) => const LoginPage(),
routes: [
GoRoute(
name: Pages.login.name,
path: routerPage(Pages.login),
builder: (_, __) => const LoginPage(),
),
GoRoute(
name: Pages.dashboard.name,
path: routerPage(Pages.dashboard),
builder: (_, __) => const PageSelector(),
),
GoRoute(
name: Pages.sfactor.name,
path: routerPage(Pages.sfactor),
builder: (context, state) {
// Определяем откуда пришел пользователь
final isFromSignup = state.uri.queryParameters['from'] == 'signup';
return TwoFactorCodePage(
onVerificationSuccess: () {
if (isFromSignup) {
// После регистрации -> на страницу логина
context.goNamed(Pages.login.name);
} else {
// После логина -> на дашборд
context.goNamed(Pages.dashboard.name);
}
},
);
},
),
GoRoute(
name: Pages.signup.name,
path: routerPage(Pages.signup),
builder: (_, __) => const SignUpPage(),
),
],
),
],
errorBuilder: (_, __) => const NotFoundPage(),
);

View File

@@ -0,0 +1,27 @@
import 'package:timeago/timeago.dart' as timeago;
import 'package:pweb/generated/i18n/app_localizations.dart'; // Ensure this file exports supportedLocales
// Mapping of language codes to timeago message classes.
final Map<String, timeago.LookupMessages> _timeagoLocales = {
'en': timeago.EnMessages(),
'ru': timeago.RuMessages(),
'uk': timeago.UkMessages(),
// Add more mappings as needed.
};
/// Initializes timeago using the supported locales from AppLocalisations.
/// Optionally, [defaultLocale] can be set (defaults to 'en').
void initializeTimeagoLocales({String defaultLocale = 'en'}) {
// Assume AppLocalisations.supportedLocales is a static List<Locale>
final supportedLocales = AppLocalizations.supportedLocales;
for (final locale in supportedLocales) {
final languageCode = locale.languageCode;
if (_timeagoLocales.containsKey(languageCode)) {
timeago.setLocaleMessages(languageCode, _timeagoLocales[languageCode]!);
}
}
// Set the default locale.
timeago.setDefaultLocale(defaultLocale);
}

View File

@@ -0,0 +1,10 @@
class Constants {
static const minPasswordCharacters = 8;
}
class AppConfig {
static const String appName = String.fromEnvironment(
'APP_NAME',
defaultValue: 'SendiCo',
);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,779 @@
// ignore: unused_import
import 'package:intl/intl.dart' as intl;
import 'app_localizations.dart';
// ignore_for_file: type=lint
/// The translations for English (`en`).
class AppLocalizationsEn extends AppLocalizations {
AppLocalizationsEn([String locale = 'en']) : super(locale);
@override
String get login => 'Login';
@override
String get logout => 'Logout';
@override
String get profile => 'Profile';
@override
String get signup => 'Sign up';
@override
String get username => 'Email';
@override
String get usernameHint => 'email@example.com';
@override
String get usernameErrorInvalid => 'Provide a valid email address';
@override
String usernameUnknownTLD(Object domain) {
return 'Domain .$domain is not known, please, check it';
}
@override
String get password => 'Password';
@override
String get confirmPassword => 'Confirm password';
@override
String get passwordValidationRuleDigit => 'has digit';
@override
String get passwordValidationRuleUpperCase => 'has uppercase letter';
@override
String get passwordValidationRuleLowerCase => 'has lowercase letter';
@override
String get passwordValidationRuleSpecialCharacter =>
'has special character letter';
@override
String passwordValidationRuleMinCharacters(Object charNum) {
return 'is $charNum characters long at least';
}
@override
String get passwordsDoNotMatch => 'Passwords do not match';
@override
String passwordValidationError(Object matchesCriteria) {
return 'Check that your password $matchesCriteria';
}
@override
String notificationError(Object error) {
return 'Error occurred: $error';
}
@override
String loginUserNotFound(Object account) {
return 'Account $account has not been registered in the system';
}
@override
String get loginPasswordIncorrect =>
'Authorization failed, please check your password';
@override
String internalErrorOccurred(Object error) {
return 'An internal server error occurred: $error, we already know about it and working hard to fix it';
}
@override
String get noErrorInformation =>
'Some error occurred, but we have not error information. We are already investigating the issue';
@override
String get yourName => 'Your name';
@override
String get nameHint => 'John Doe';
@override
String get errorPageNotFoundTitle => 'Page Not Found';
@override
String get errorPageNotFoundMessage => 'Oops! We couldn\'t find that page.';
@override
String get errorPageNotFoundHint =>
'The page you\'re looking for doesn\'t exist or has been moved. Please check the URL or return to the home page.';
@override
String get errorUnknown => 'Unknown error occurred';
@override
String get unknown => 'unknown';
@override
String get goToLogin => 'Go to Login';
@override
String get goBack => 'Go Back';
@override
String get goToMainPage => 'Go to Main Page';
@override
String get goToSignUp => 'Go to Sign Up';
@override
String signupError(Object error) {
return 'Failed to signup: $error';
}
@override
String signupSuccess(Object email) {
return 'Email confirmation message has been sent to $email. Please, open it and click link to activate your account.';
}
@override
String connectivityError(Object serverAddress) {
return 'Cannot reach the server at $serverAddress. Check your network and try again.';
}
@override
String get errorAccountExists => 'Account already exists';
@override
String get errorAccountNotVerified =>
'Your account hasn\'t been verified yet. Please check your email to complete the verification';
@override
String get errorLoginUnauthorized =>
'Login or password is incorrect. Please try again';
@override
String get errorInternalError =>
'An internal error occurred. We\'re aware of the issue and working to resolve it. Please try again later';
@override
String get errorVerificationTokenNotFound =>
'Account for verification not found. Sign up again';
@override
String get created => 'Created';
@override
String get edited => 'Edited';
@override
String get errorDataConflict =>
'We cant process your data because it has conflicting or contradictory information.';
@override
String get errorAccessDenied =>
'You do not have permission to access this resource. If you need access, please contact an administrator.';
@override
String get errorBrokenPayload =>
'The data you sent is invalid or incomplete. Please check your submission and try again.';
@override
String get errorInvalidArgument =>
'One or more arguments are invalid. Verify your input and try again.';
@override
String get errorBrokenReference =>
'The resource you\'re trying to access could not be referenced. It may have been moved or deleted.';
@override
String get errorInvalidQueryParameter =>
'One or more query parameters are missing or incorrect. Check them and try again.';
@override
String get errorNotImplemented =>
'This feature is not yet available. Please try again later or contact support.';
@override
String get errorLicenseRequired =>
'A valid license is required to perform this action. Please contact your administrator.';
@override
String get errorNotFound =>
'We couldn\'t find the resource you requested. It may have been removed or is temporarily unavailable.';
@override
String get errorNameMissing => 'Please provide a name before continuing.';
@override
String get errorEmailMissing =>
'Please provide an email address before continuing.';
@override
String get errorPasswordMissing =>
'Please provide a password before continuing.';
@override
String get errorEmailNotRegistered =>
'We could not find an account associated with that email address.';
@override
String get errorDuplicateEmail =>
'This email address is already in use. Try another one or reset your password.';
@override
String get showDetailsAction => 'Show Details';
@override
String get errorLogin => 'Error logging in';
@override
String get errorCreatingInvitation => 'Failed to create invitaiton';
@override
String get footerCompanyName => 'Sibilla Solutions LTD';
@override
String get footerAddress =>
'27, Pindarou Street, Alpha Business Centre, Block B 7th Floor, 1060 Nicosia, Cyprus';
@override
String get footerSupport => 'Support';
@override
String get footerEmail => 'Email TBD';
@override
String get footerPhoneLabel => 'Phone';
@override
String get footerPhone => '+357 22 000 253';
@override
String get footerTermsOfService => 'Terms of Service';
@override
String get footerPrivacyPolicy => 'Privacy Policy';
@override
String get footerCookiePolicy => 'Cookie Policy';
@override
String get navigationLogout => 'Logout';
@override
String get dashboard => 'Dashboard';
@override
String get navigationUsersSettings => 'Users';
@override
String get navigationRolesSettings => 'Roles';
@override
String get navigationPermissionsSettings => 'Permissions';
@override
String get usersManagement => 'User Management';
@override
String get navigationOrganizationSettings => 'Organization settings';
@override
String get navigationAccountSettings => 'Profile settings';
@override
String get twoFactorPrompt => 'Enter the 6-digit code we sent to your device';
@override
String get twoFactorResend => 'Didnt receive a code? Resend';
@override
String get twoFactorTitle => 'Two-Factor Authentication';
@override
String get twoFactorError => 'Invalid code. Please try again.';
@override
String get payoutNavDashboard => 'Dashboard';
@override
String get payoutNavSendPayout => 'Send payout';
@override
String get payoutNavRecipients => 'Recipients';
@override
String get payoutNavReports => 'Reports';
@override
String get payoutNavSettings => 'Settings';
@override
String get payoutNavLogout => 'Logout';
@override
String get payoutNavMethods => 'Payouts';
@override
String get expand => 'Expand';
@override
String get collapse => 'Collapse';
@override
String get pageTitleRecipients => 'Recipient address book';
@override
String get actionAddNew => 'Add new';
@override
String get colDataOwner => 'Data owner';
@override
String get colAvatar => 'Avatar';
@override
String get colName => 'Name';
@override
String get colEmail => 'Email';
@override
String get colStatus => 'Status';
@override
String get statusReady => 'Ready';
@override
String get statusRegistered => 'Registered';
@override
String get statusNotRegistered => 'Not registered';
@override
String get typeInternal => 'Managed by me';
@override
String get typeExternal => 'Selfmanaged';
@override
String get searchHint => 'Search recipients';
@override
String get colActions => 'Actions';
@override
String get menuEdit => 'Edit';
@override
String get menuSendPayout => 'Send payout';
@override
String get tooltipRowActions => 'More actions';
@override
String get accountSettings => 'Account Settings';
@override
String get accountNameUpdateError => 'Failed to update account name';
@override
String get settingsSuccessfullyUpdated => 'Settings successfully updated';
@override
String get language => 'Language';
@override
String get failedToUpdateLanguage => 'Failed to update language';
@override
String get settingsImageUpdateError => 'Couldn\'t update the image';
@override
String get settingsImageTitle => 'Image';
@override
String get settingsImageHint => 'Tap to change the image';
@override
String get accountName => 'Name';
@override
String get accountNameHint => 'Specify your name';
@override
String get avatar => 'Profile photo';
@override
String get avatarHint => 'Tap to update';
@override
String get avatarUpdateError => 'Failed to update profile photo';
@override
String get settings => 'Settings';
@override
String get notSet => 'not set';
@override
String get search => 'Search...';
@override
String get ok => 'Ok';
@override
String get cancel => 'Cancel';
@override
String get confirm => 'Confirm';
@override
String get back => 'Back';
@override
String get operationfryTitle => 'Operation history';
@override
String get filters => 'Filters';
@override
String get period => 'Period';
@override
String get selectPeriod => 'Select period';
@override
String get apply => 'Apply';
@override
String status(String status) {
return '$status';
}
@override
String get operationStatusSuccessful => 'Successful';
@override
String get operationStatusPending => 'Pending';
@override
String get operationStatusUnsuccessful => 'Unsuccessful';
@override
String get statusColumn => 'Status';
@override
String get fileNameColumn => 'File name';
@override
String get amountColumn => 'Amount';
@override
String get toAmountColumn => 'To amount';
@override
String get payIdColumn => 'Pay ID';
@override
String get cardNumberColumn => 'Card number';
@override
String get nameColumn => 'Name';
@override
String get dateColumn => 'Date';
@override
String get commentColumn => 'Comment';
@override
String get paymentConfigTitle => 'Where to receive money';
@override
String get paymentConfigSubtitle =>
'Add multiple methods and choose your primary one.';
@override
String get addPaymentMethod => 'Add payment method';
@override
String get makeMain => 'Make primary';
@override
String get advanced => 'Advanced';
@override
String get fallbackExplanation =>
'If the primary method is unavailable, we will try the next enabled one in the list.';
@override
String get delete => 'Delete';
@override
String get deletePaymentConfirmation =>
'Are you sure you want to delete this payment method?';
@override
String get edit => 'Edit';
@override
String get moreActions => 'More actions';
@override
String get noPayouts => 'No Payouts';
@override
String get enterBankName => 'Enter bank name';
@override
String get paymentType => 'Payment Method Type';
@override
String get selectPaymentType => 'Please select a payment method type';
@override
String get paymentTypeCard => 'Credit Card';
@override
String get paymentTypeBankAccount => 'Russian Bank Account';
@override
String get paymentTypeIban => 'IBAN';
@override
String get paymentTypeWallet => 'Wallet';
@override
String get cardNumber => 'Card Number';
@override
String get enterCardNumber => 'Enter the card number';
@override
String get cardholderName => 'Cardholder Name';
@override
String get iban => 'IBAN';
@override
String get enterIban => 'Enter IBAN';
@override
String get bic => 'BIC';
@override
String get bankName => 'Bank Name';
@override
String get accountHolder => 'Account Holder';
@override
String get enterAccountHolder => 'Enter account holder';
@override
String get enterBic => 'Enter BIC';
@override
String get walletId => 'Wallet ID';
@override
String get enterWalletId => 'Enter wallet ID';
@override
String get recipients => 'Recipients';
@override
String get recipientName => 'Recipient Name';
@override
String get enterRecipientName => 'Enter recipient name';
@override
String get inn => 'INN';
@override
String get enterInn => 'Enter INN';
@override
String get kpp => 'KPP';
@override
String get enterKpp => 'Enter KPP';
@override
String get accountNumber => 'Account Number';
@override
String get enterAccountNumber => 'Enter account number';
@override
String get correspondentAccount => 'Correspondent Account';
@override
String get enterCorrespondentAccount => 'Enter correspondent account';
@override
String get bik => 'BIK';
@override
String get enterBik => 'Enter BIK';
@override
String get add => 'Add';
@override
String get expiryDate => 'Expiry (MM/YY)';
@override
String get firstName => 'First Name';
@override
String get enterFirstName => 'Enter First Name';
@override
String get lastName => 'Last Name';
@override
String get enterLastName => 'Enter Last Name';
@override
String get sendSingle => 'Send single transaction';
@override
String get sendMultiple => 'Send multiple transactions';
@override
String get addFunds => 'Add Funds';
@override
String get close => 'Close';
@override
String get multiplePayout => 'Multiple Payout';
@override
String get howItWorks => 'How it works?';
@override
String get exampleTitle => 'File Format & Sample';
@override
String get downloadSampleCSV => 'Download sample.csv';
@override
String get tokenColumn => 'Token (required)';
@override
String get currency => 'Currency';
@override
String get amount => 'Amount';
@override
String get comment => 'Comment';
@override
String get uploadCSV => 'Upload your CSV';
@override
String get upload => 'Upload';
@override
String get hintUpload => 'Supported format: .CSV · Max size 1 MB';
@override
String get uploadHistory => 'Upload History';
@override
String get payout => 'Payout';
@override
String get sendTo => 'Send Payout To';
@override
String get send => 'Send Payout';
@override
String get recipientPaysFee => 'Recipient pays the fee';
@override
String sentAmount(String amount) {
return 'Sent amount: \$$amount';
}
@override
String fee(String fee) {
return 'Fee: \$$fee';
}
@override
String recipientWillReceive(String amount) {
return 'Recipient will receive: \$$amount';
}
@override
String total(String total) {
return 'Total: \$$total';
}
@override
String get hideDetails => 'Hide Details';
@override
String get showDetails => 'Show Details';
@override
String get whereGetMoney => 'Source of funds for debit';
@override
String get details => 'Details';
@override
String get addRecipient => 'Add Recipient';
@override
String get editRecipient => 'Edit Recipient';
@override
String get saveRecipient => 'Save Recipient';
@override
String get choosePaymentMethod => 'Payment Methods (choose at least 1)';
@override
String get recipientFormRule =>
'Recipient must have at least one payment method';
@override
String get allStatus => 'All';
@override
String get readyStatus => 'Ready';
@override
String get registeredStatus => 'Registered';
@override
String get notRegisteredStatus => 'Not registered';
@override
String get noRecipientSelected => 'No recipient selected';
@override
String get companyName => 'Name of your company';
@override
String get companynameRequired => 'Company name required';
@override
String get errorSignUp => 'Error occured while signing up, try again later';
@override
String get companyDescription => 'Company Description';
@override
String get companyDescriptionHint =>
'Describe any of the fields of the Company\'s business';
@override
String get optional => 'optional';
}

View File

@@ -0,0 +1,782 @@
// ignore: unused_import
import 'package:intl/intl.dart' as intl;
import 'app_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Russian (`ru`).
class AppLocalizationsRu extends AppLocalizations {
AppLocalizationsRu([String locale = 'ru']) : super(locale);
@override
String get login => 'Войти';
@override
String get logout => 'Выйти';
@override
String get profile => 'Профиль';
@override
String get signup => 'Регистрация';
@override
String get username => 'Email';
@override
String get usernameHint => 'email@example.com';
@override
String get usernameErrorInvalid =>
'Укажите действительный адрес электронной почты';
@override
String usernameUnknownTLD(Object domain) {
return 'Домен .$domain неизвестен, пожалуйста, проверьте его';
}
@override
String get password => 'Пароль';
@override
String get confirmPassword => 'Подтвердите пароль';
@override
String get passwordValidationRuleDigit => 'содержит цифру';
@override
String get passwordValidationRuleUpperCase => 'содержит заглавную букву';
@override
String get passwordValidationRuleLowerCase => 'содержит строчную букву';
@override
String get passwordValidationRuleSpecialCharacter =>
'содержит специальный символ';
@override
String passwordValidationRuleMinCharacters(Object charNum) {
return 'длина не менее $charNum символов';
}
@override
String get passwordsDoNotMatch => 'Пароли не совпадают';
@override
String passwordValidationError(Object matchesCriteria) {
return 'Убедитесь, что ваш пароль $matchesCriteria';
}
@override
String notificationError(Object error) {
return 'Произошла ошибка: $error';
}
@override
String loginUserNotFound(Object account) {
return 'Аккаунт $account не зарегистрирован в системе';
}
@override
String get loginPasswordIncorrect =>
'Ошибка авторизации, пожалуйста, проверьте пароль';
@override
String internalErrorOccurred(Object error) {
return 'Произошла внутренняя ошибка сервера: $error, мы уже знаем о ней и усердно работаем над исправлением';
}
@override
String get noErrorInformation =>
'Произошла ошибка, но у нас нет информации о ней. Мы уже расследуем этот вопрос';
@override
String get yourName => 'Ваше имя';
@override
String get nameHint => 'Иван Иванов';
@override
String get errorPageNotFoundTitle => 'Страница не найдена';
@override
String get errorPageNotFoundMessage =>
'Упс! Мы не смогли найти эту страницу.';
@override
String get errorPageNotFoundHint =>
'Запрашиваемая страница не существует или была перемещена. Пожалуйста, проверьте URL или вернитесь на главную страницу.';
@override
String get errorUnknown => 'Произошла неизвестная ошибка';
@override
String get unknown => 'неизвестно';
@override
String get goToLogin => 'Перейти к входу';
@override
String get goBack => 'Назад';
@override
String get goToMainPage => 'На главную';
@override
String get goToSignUp => 'Перейти к регистрации';
@override
String signupError(Object error) {
return 'Не удалось зарегистрироваться: $error';
}
@override
String signupSuccess(Object email) {
return 'Письмо с подтверждением email отправлено на $email. Пожалуйста, откройте его и перейдите по ссылке для активации вашего аккаунта.';
}
@override
String connectivityError(Object serverAddress) {
return 'Не удается связаться с сервером $serverAddress. Проверьте ваше интернет-соединение и попробуйте снова.';
}
@override
String get errorAccountExists => 'Account already exists';
@override
String get errorAccountNotVerified =>
'Ваш аккаунт еще не подтвержден. Пожалуйста, проверьте вашу электронную почту для завершения верификации';
@override
String get errorLoginUnauthorized =>
'Неверный логин или пароль. Пожалуйста, попробуйте снова';
@override
String get errorInternalError =>
'Произошла внутренняя ошибка. Мы в курсе проблемы и работаем над ее решением. Пожалуйста, попробуйте позже';
@override
String get errorVerificationTokenNotFound =>
'Аккаунт для верификации не найден. Зарегистрируйтесь снова';
@override
String get created => 'Создано';
@override
String get edited => 'Изменено';
@override
String get errorDataConflict =>
'Мы не можем обработать ваши данные, так как они содержат конфликтующую или противоречивую информацию.';
@override
String get errorAccessDenied =>
'У вас нет разрешения на доступ к этому ресурсу. Если вам нужен доступ, пожалуйста, обратитесь к администратору.';
@override
String get errorBrokenPayload =>
'Отправленные данные недействительны или неполны. Пожалуйста, проверьте введенные данные и попробуйте снова.';
@override
String get errorInvalidArgument =>
'Один или несколько аргументов недействительны. Проверьте введенные данные и попробуйте снова.';
@override
String get errorBrokenReference =>
'Ресурс, к которому вы пытаетесь получить доступ, не может быть найден. Возможно, он был перемещен или удален.';
@override
String get errorInvalidQueryParameter =>
'Один или несколько параметров запроса отсутствуют или указаны неверно. Проверьте их и попробуйте снова.';
@override
String get errorNotImplemented =>
'Эта функция еще недоступна. Пожалуйста, попробуйте позже или обратитесь в службу поддержки.';
@override
String get errorLicenseRequired =>
'Для выполнения этого действия требуется действующая лицензия. Пожалуйста, обратитесь к вашему администратору.';
@override
String get errorNotFound =>
'Мы не смогли найти запрошенный ресурс. Возможно, он был удален или временно недоступен.';
@override
String get errorNameMissing => 'Пожалуйста, укажите имя для продолжения.';
@override
String get errorEmailMissing =>
'Пожалуйста, укажите адрес электронной почты для продолжения.';
@override
String get errorPasswordMissing =>
'Пожалуйста, укажите пароль для продолжения.';
@override
String get errorEmailNotRegistered =>
'Мы не нашли аккаунт, связанный с этим адресом электронной почты.';
@override
String get errorDuplicateEmail =>
'Этот адрес электронной почты уже используется. Попробуйте другой или восстановите пароль.';
@override
String get showDetailsAction => 'Показать детали';
@override
String get errorLogin => 'Ошибка входа';
@override
String get errorCreatingInvitation => 'Не удалось создать приглашение';
@override
String get footerCompanyName => 'Sibilla Solutions LTD';
@override
String get footerAddress =>
'27, Pindarou Street, Alpha Business Centre, Block B 7th Floor, 1060 Nicosia, Cyprus';
@override
String get footerSupport => 'Поддержка';
@override
String get footerEmail => 'Email TBD';
@override
String get footerPhoneLabel => 'Телефон';
@override
String get footerPhone => '+357 22 000 253';
@override
String get footerTermsOfService => 'Условия обслуживания';
@override
String get footerPrivacyPolicy => 'Политика конфиденциальности';
@override
String get footerCookiePolicy => 'Политика использования файлов cookie';
@override
String get navigationLogout => 'Выйти';
@override
String get dashboard => 'Дашборд';
@override
String get navigationUsersSettings => 'Пользователи';
@override
String get navigationRolesSettings => 'Роли';
@override
String get navigationPermissionsSettings => 'Разрешения';
@override
String get usersManagement => 'Управление пользователями';
@override
String get navigationOrganizationSettings => 'Настройки организации';
@override
String get navigationAccountSettings => 'Настройки профиля';
@override
String get twoFactorPrompt =>
'Введите 6-значный код, отправленный на ваше устройство';
@override
String get twoFactorResend => 'Не получили код? Отправить снова';
@override
String get twoFactorTitle => 'Двухфакторная аутентификация';
@override
String get twoFactorError => 'Неверный код. Пожалуйста, попробуйте снова.';
@override
String get payoutNavDashboard => 'Дашборд';
@override
String get payoutNavSendPayout => 'Отправить выплату';
@override
String get payoutNavRecipients => 'Получатели';
@override
String get payoutNavReports => 'Отчеты';
@override
String get payoutNavSettings => 'Настройки';
@override
String get payoutNavLogout => 'Выйти';
@override
String get payoutNavMethods => 'Выплаты';
@override
String get expand => 'Развернуть';
@override
String get collapse => 'Свернуть';
@override
String get pageTitleRecipients => 'Адресная книга получателей';
@override
String get actionAddNew => 'Добавить';
@override
String get colDataOwner => 'Владелец данных';
@override
String get colAvatar => 'Аватар';
@override
String get colName => 'Имя';
@override
String get colEmail => 'Email';
@override
String get colStatus => 'Статус';
@override
String get statusReady => 'Готов';
@override
String get statusRegistered => 'Зарегистрирован';
@override
String get statusNotRegistered => 'Не зарегистрирован';
@override
String get typeInternal => 'Управляется мной';
@override
String get typeExternal => 'Самоуправляемый';
@override
String get searchHint => 'Поиск получателей';
@override
String get colActions => 'Действия';
@override
String get menuEdit => 'Редактировать';
@override
String get menuSendPayout => 'Отправить выплату';
@override
String get tooltipRowActions => 'Другие действия';
@override
String get accountSettings => 'Настройки аккаунта';
@override
String get accountNameUpdateError => 'Не удалось обновить имя аккаунта';
@override
String get settingsSuccessfullyUpdated => 'Настройки успешно обновлены';
@override
String get language => 'Язык';
@override
String get failedToUpdateLanguage => 'Не удалось обновить язык';
@override
String get settingsImageUpdateError => 'Не удалось обновить изображение';
@override
String get settingsImageTitle => 'Изображение';
@override
String get settingsImageHint => 'Нажмите, чтобы изменить изображение';
@override
String get accountName => 'Имя';
@override
String get accountNameHint => 'Укажите ваше имя';
@override
String get avatar => 'Фото профиля';
@override
String get avatarHint => 'Нажмите для обновления';
@override
String get avatarUpdateError => 'Не удалось обновить фото профиля';
@override
String get settings => 'Настройки';
@override
String get notSet => 'не задано';
@override
String get search => 'Поиск...';
@override
String get ok => 'Ок';
@override
String get cancel => 'Отмена';
@override
String get confirm => 'Подтвердить';
@override
String get back => 'Назад';
@override
String get operationfryTitle => 'История операций';
@override
String get filters => 'Фильтры';
@override
String get period => 'Период';
@override
String get selectPeriod => 'Выберите период';
@override
String get apply => 'Применить';
@override
String status(String status) {
return '$status';
}
@override
String get operationStatusSuccessful => 'Успешно';
@override
String get operationStatusPending => 'В ожидании';
@override
String get operationStatusUnsuccessful => 'Неуспешно';
@override
String get statusColumn => 'Статус';
@override
String get fileNameColumn => 'Имя файла';
@override
String get amountColumn => 'Сумма';
@override
String get toAmountColumn => 'На сумму';
@override
String get payIdColumn => 'Pay ID';
@override
String get cardNumberColumn => 'Номер карты';
@override
String get nameColumn => 'Имя';
@override
String get dateColumn => 'Дата';
@override
String get commentColumn => 'Комментарий';
@override
String get paymentConfigTitle => 'Куда получать деньги';
@override
String get paymentConfigSubtitle =>
'Добавьте несколько методов и выберите основной.';
@override
String get addPaymentMethod => 'Добавить способ оплаты';
@override
String get makeMain => 'Сделать основным';
@override
String get advanced => 'Дополнительно';
@override
String get fallbackExplanation =>
'Если основной метод недоступен, мы попробуем следующий включенный метод в списке.';
@override
String get delete => 'Удалить';
@override
String get deletePaymentConfirmation =>
'Вы уверены, что хотите удалить этот способ оплаты?';
@override
String get edit => 'Редактировать';
@override
String get moreActions => 'Еще действия';
@override
String get noPayouts => 'Нет выплат';
@override
String get enterBankName => 'Введите название банка';
@override
String get paymentType => 'Тип способа оплаты';
@override
String get selectPaymentType => 'Пожалуйста, выберите тип способа оплаты';
@override
String get paymentTypeCard => 'Кредитная карта';
@override
String get paymentTypeBankAccount => 'Российский банковский счет';
@override
String get paymentTypeIban => 'IBAN';
@override
String get paymentTypeWallet => 'Кошелек';
@override
String get cardNumber => 'Номер карты';
@override
String get enterCardNumber => 'Введите номер карты';
@override
String get cardholderName => 'Имя держателя карты';
@override
String get iban => 'IBAN';
@override
String get enterIban => 'Введите IBAN';
@override
String get bic => 'BIC';
@override
String get bankName => 'Название банка';
@override
String get accountHolder => 'Владелец счета';
@override
String get enterAccountHolder => 'Введите владельца счета';
@override
String get enterBic => 'Введите BIC';
@override
String get walletId => 'ID кошелька';
@override
String get enterWalletId => 'Введите ID кошелька';
@override
String get recipients => 'Получатели';
@override
String get recipientName => 'Имя получателя';
@override
String get enterRecipientName => 'Введите имя получателя';
@override
String get inn => 'ИНН';
@override
String get enterInn => 'Введите ИНН';
@override
String get kpp => 'КПП';
@override
String get enterKpp => 'Введите КПП';
@override
String get accountNumber => 'Номер счета';
@override
String get enterAccountNumber => 'Введите номер счета';
@override
String get correspondentAccount => 'Корреспондентский счет';
@override
String get enterCorrespondentAccount => 'Введите корреспондентский счет';
@override
String get bik => 'БИК';
@override
String get enterBik => 'Введите БИК';
@override
String get add => 'Добавить';
@override
String get expiryDate => 'Срок действия (ММ/ГГ)';
@override
String get firstName => 'Имя';
@override
String get enterFirstName => 'Введите имя';
@override
String get lastName => 'Фамилия';
@override
String get enterLastName => 'Введите фамилию';
@override
String get sendSingle => 'Отправить одну транзакцию';
@override
String get sendMultiple => 'Отправить несколько транзакций';
@override
String get addFunds => 'Пополнить счет';
@override
String get close => 'Закрыть';
@override
String get multiplePayout => 'Множественная выплата';
@override
String get howItWorks => 'Как это работает?';
@override
String get exampleTitle => 'Формат файла и образец';
@override
String get downloadSampleCSV => 'Скачать sample.csv';
@override
String get tokenColumn => 'Токен (обязательно)';
@override
String get currency => 'Валюта';
@override
String get amount => 'Сумма';
@override
String get comment => 'Комментарий';
@override
String get uploadCSV => 'Загрузите ваш CSV';
@override
String get upload => 'Загрузить';
@override
String get hintUpload => 'Поддерживаемый формат: .CSV · Макс. размер 1 МБ';
@override
String get uploadHistory => 'История загрузок';
@override
String get payout => 'Выплата';
@override
String get sendTo => 'Отправить выплату';
@override
String get send => 'Отправить выплату';
@override
String get recipientPaysFee => 'Получатель оплачивает комиссию';
@override
String sentAmount(String amount) {
return 'Отправленная сумма: \$$amount';
}
@override
String fee(String fee) {
return 'Комиссия: \$$fee';
}
@override
String recipientWillReceive(String amount) {
return 'Получатель получит: \$$amount';
}
@override
String total(String total) {
return 'Итого: \$$total';
}
@override
String get hideDetails => 'Скрыть детали';
@override
String get showDetails => 'Показать детали';
@override
String get whereGetMoney => 'Источник средств для списания';
@override
String get details => 'Детали';
@override
String get addRecipient => 'Добавить получателя';
@override
String get editRecipient => 'Редактировать получателя';
@override
String get saveRecipient => 'Сохранить получателя';
@override
String get choosePaymentMethod => 'Способы оплаты (выберите хотя бы 1)';
@override
String get recipientFormRule =>
'Получатель должен иметь хотя бы один способ оплаты';
@override
String get allStatus => 'Все';
@override
String get readyStatus => 'Готов';
@override
String get registeredStatus => 'Зарегистрирован';
@override
String get notRegisteredStatus => 'Не зарегистрирован';
@override
String get noRecipientSelected => 'Получатель не выбран';
@override
String get companyName => 'Name of your company';
@override
String get companynameRequired => 'Company name required';
@override
String get errorSignUp => 'Error occured while signing up, try again later';
@override
String get companyDescription => 'Company Description';
@override
String get companyDescriptionHint =>
'Describe any of the fields of the Company\'s business';
@override
String get optional => 'optional';
}

View File

@@ -0,0 +1,435 @@
{
"@@locale": "en",
"login": "Login",
"logout": "Logout",
"profile": "Profile",
"signup": "Sign up",
"username": "Email",
"usernameHint": "email@example.com",
"usernameErrorInvalid": "Provide a valid email address",
"usernameUnknownTLD": "Domain .{domain} is not known, please, check it",
"password": "Password",
"confirmPassword": "Confirm password",
"passwordValidationRuleDigit": "has digit",
"passwordValidationRuleUpperCase": "has uppercase letter",
"passwordValidationRuleLowerCase": "has lowercase letter",
"passwordValidationRuleSpecialCharacter": "has special character letter",
"passwordValidationRuleMinCharacters": "is {charNum} characters long at least",
"passwordsDoNotMatch": "Passwords do not match",
"passwordValidationError": "Check that your password {matchesCriteria}",
"notificationError": "Error occurred: {error}",
"loginUserNotFound": "Account {account} has not been registered in the system",
"loginPasswordIncorrect": "Authorization failed, please check your password",
"internalErrorOccurred": "An internal server error occurred: {error}, we already know about it and working hard to fix it",
"noErrorInformation": "Some error occurred, but we have not error information. We are already investigating the issue",
"yourName": "Your name",
"nameHint": "John Doe",
"errorPageNotFoundTitle": "Page Not Found",
"errorPageNotFoundMessage": "Oops! We couldn't find that page.",
"errorPageNotFoundHint": "The page you're looking for doesn't exist or has been moved. Please check the URL or return to the home page.",
"errorUnknown": "Unknown error occurred",
"unknown": "unknown",
"goToLogin": "Go to Login",
"goBack": "Go Back",
"goToMainPage": "Go to Main Page",
"goToSignUp": "Go to Sign Up",
"signupError": "Failed to signup: {error}",
"signupSuccess": "Email confirmation message has been sent to {email}. Please, open it and click link to activate your account.",
"connectivityError": "Cannot reach the server at {serverAddress}. Check your network and try again.",
"errorAccountExists": "Account already exists",
"errorAccountNotVerified": "Your account hasn't been verified yet. Please check your email to complete the verification",
"errorLoginUnauthorized": "Login or password is incorrect. Please try again",
"errorInternalError": "An internal error occurred. We're aware of the issue and working to resolve it. Please try again later",
"errorVerificationTokenNotFound": "Account for verification not found. Sign up again",
"created": "Created",
"edited": "Edited",
"errorDataConflict": "We cant process your data because it has conflicting or contradictory information.",
"errorAccessDenied": "You do not have permission to access this resource. If you need access, please contact an administrator.",
"errorBrokenPayload": "The data you sent is invalid or incomplete. Please check your submission and try again.",
"errorInvalidArgument": "One or more arguments are invalid. Verify your input and try again.",
"errorBrokenReference": "The resource you're trying to access could not be referenced. It may have been moved or deleted.",
"errorInvalidQueryParameter": "One or more query parameters are missing or incorrect. Check them and try again.",
"errorNotImplemented": "This feature is not yet available. Please try again later or contact support.",
"errorLicenseRequired": "A valid license is required to perform this action. Please contact your administrator.",
"errorNotFound": "We couldn't find the resource you requested. It may have been removed or is temporarily unavailable.",
"errorNameMissing": "Please provide a name before continuing.",
"errorEmailMissing": "Please provide an email address before continuing.",
"errorPasswordMissing": "Please provide a password before continuing.",
"errorEmailNotRegistered": "We could not find an account associated with that email address.",
"errorDuplicateEmail": "This email address is already in use. Try another one or reset your password.",
"showDetailsAction": "Show Details",
"errorLogin": "Error logging in",
"errorCreatingInvitation": "Failed to create invitaiton",
"@errorCreatingInvitation": {
"description": "Error message displayed when invitation creation fails"
},
"footerCompanyName": "Sibilla Solutions LTD",
"footerAddress": "27, Pindarou Street, Alpha Business Centre, Block B 7th Floor, 1060 Nicosia, Cyprus",
"footerSupport": "Support",
"footerEmail": "Email TBD",
"footerPhoneLabel": "Phone",
"footerPhone": "+357 22 000 253",
"footerTermsOfService": "Terms of Service",
"footerPrivacyPolicy": "Privacy Policy",
"footerCookiePolicy": "Cookie Policy",
"navigationLogout": "Logout",
"dashboard": "Dashboard",
"navigationUsersSettings": "Users",
"navigationRolesSettings": "Roles",
"navigationPermissionsSettings": "Permissions",
"usersManagement": "User Management",
"navigationOrganizationSettings": "Organization settings",
"navigationAccountSettings": "Profile settings",
"twoFactorPrompt": "Enter the 6-digit code we sent to your device",
"twoFactorResend": "Didnt receive a code? Resend",
"twoFactorTitle": "Two-Factor Authentication",
"twoFactorError": "Invalid code. Please try again.",
"payoutNavDashboard": "Dashboard",
"payoutNavSendPayout": "Send payout",
"payoutNavRecipients": "Recipients",
"payoutNavReports": "Reports",
"payoutNavSettings": "Settings",
"payoutNavLogout": "Logout",
"payoutNavMethods": "Payouts",
"expand": "Expand",
"collapse": "Collapse",
"pageTitleRecipients": "Recipient address book",
"@pageTitleRecipients": {
"description": "Title of the recipient address book page",
"type": "text"
},
"actionAddNew": "Add new",
"@actionAddNew": {
"description": "Tooltip and button label to add a new recipient"
},
"colDataOwner": "Data owner",
"@colDataOwner": {
"description": "Column header for who manages the payout data"
},
"colAvatar": "Avatar",
"@colAvatar": {
"description": "Column header for recipient avatar"
},
"colName": "Name",
"@colName": {
"description": "Column header for recipient name"
},
"colEmail": "Email",
"@colEmail": {
"description": "Column header for recipient email address"
},
"colStatus": "Status",
"@colStatus": {
"description": "Column header for payout readiness status"
},
"statusReady": "Ready",
"@statusReady": {
"description": "Status indicating payouts can be sent immediately"
},
"statusRegistered": "Registered",
"@statusRegistered": {
"description": "Status indicating recipient is registered but not yet fully ready"
},
"statusNotRegistered": "Not registered",
"@statusNotRegistered": {
"description": "Status indicating recipient has not completed registration"
},
"typeInternal": "Managed by me",
"@typeInternal": {
"description": "Label for recipients whose payout data is managed internally by the user/company"
},
"typeExternal": "Selfmanaged",
"@typeExternal": {
"description": "Label for recipients who manage their own payout data"
},
"searchHint": "Search recipients",
"colActions": "Actions",
"menuEdit": "Edit",
"menuSendPayout": "Send payout",
"tooltipRowActions": "More actions",
"accountSettings": "Account Settings",
"accountNameUpdateError": "Failed to update account name",
"settingsSuccessfullyUpdated": "Settings successfully updated",
"language": "Language",
"failedToUpdateLanguage": "Failed to update language",
"settingsImageUpdateError": "Couldn't update the image",
"settingsImageTitle": "Image",
"settingsImageHint": "Tap to change the image",
"accountName": "Name",
"accountNameHint": "Specify your name",
"avatar": "Profile photo",
"avatarHint": "Tap to update",
"avatarUpdateError": "Failed to update profile photo",
"settings": "Settings",
"notSet": "not set",
"search": "Search...",
"ok": "Ok",
"cancel": "Cancel",
"confirm": "Confirm",
"back": "Back",
"operationfryTitle": "Operation history",
"@operationfryTitle": {
"description": "Title of the operation history page"
},
"filters": "Filters",
"@filters": {
"description": "Label for the filters expansion panel"
},
"period": "Period",
"@period": {
"description": "Label for the daterange filter"
},
"selectPeriod": "Select period",
"@selectPeriod": {
"description": "Placeholder when no period is selected"
},
"apply": "Apply",
"@apply": {
"description": "Button text to apply the filters"
},
"status": "{status}",
"@status": {
"description": "Template for a single status filter chip",
"placeholders": {
"status": {
"type": "String",
"example": "Successful"
}
}
},
"operationStatusSuccessful": "Successful",
"@operationStatusSuccessful": {
"description": "Status indicating the operation succeeded"
},
"operationStatusPending": "Pending",
"@operationStatusPending": {
"description": "Status indicating the operation is pending"
},
"operationStatusUnsuccessful": "Unsuccessful",
"@operationStatusUnsuccessful": {
"description": "Status indicating the operation failed"
},
"statusColumn": "Status",
"@statusColumn": {
"description": "Table column header for status"
},
"fileNameColumn": "File name",
"@fileNameColumn": {
"description": "Table column header for file name"
},
"amountColumn": "Amount",
"@amountColumn": {
"description": "Table column header for the original amount"
},
"toAmountColumn": "To amount",
"@toAmountColumn": {
"description": "Table column header for the converted amount"
},
"payIdColumn": "Pay ID",
"@payIdColumn": {
"description": "Table column header for the payment ID"
},
"cardNumberColumn": "Card number",
"@cardNumberColumn": {
"description": "Table column header for the masked card number"
},
"nameColumn": "Name",
"@nameColumn": {
"description": "Table column header for recipient name"
},
"dateColumn": "Date",
"@dateColumn": {
"description": "Table column header for the date/time"
},
"commentColumn": "Comment",
"@commentColumn": {
"description": "Table column header for any comment"
},
"paymentConfigTitle": "Where to receive money",
"paymentConfigSubtitle": "Add multiple methods and choose your primary one.",
"addPaymentMethod": "Add payment method",
"makeMain": "Make primary",
"advanced": "Advanced",
"fallbackExplanation": "If the primary method is unavailable, we will try the next enabled one in the list.",
"delete": "Delete",
"@delete": {
"description": "Button label to delete a payment method"
},
"deletePaymentConfirmation": "Are you sure you want to delete this payment method?",
"@deletePaymentConfirmation": {
"description": "Confirmation dialog message shown before a payment method is removed"
},
"edit": "Edit",
"@edit": {
"description": "Button label to edit a payment method"
},
"moreActions": "More actions",
"@moreActions": {
"description": "Tooltip for an overflow menu button that reveals extra actions for a payment method"
},
"noPayouts": "No Payouts",
"enterBankName": "Enter bank name",
"paymentType": "Payment Method Type",
"selectPaymentType": "Please select a payment method type",
"paymentTypeCard": "Credit Card",
"paymentTypeBankAccount": "Russian Bank Account",
"paymentTypeIban": "IBAN",
"paymentTypeWallet": "Wallet",
"cardNumber": "Card Number",
"enterCardNumber": "Enter the card number",
"cardholderName": "Cardholder Name",
"iban": "IBAN",
"enterIban": "Enter IBAN",
"bic": "BIC",
"bankName": "Bank Name",
"accountHolder": "Account Holder",
"enterAccountHolder": "Enter account holder",
"enterBic": "Enter BIC",
"walletId": "Wallet ID",
"enterWalletId": "Enter wallet ID",
"recipients": "Recipients",
"recipientName": "Recipient Name",
"enterRecipientName": "Enter recipient name",
"inn": "INN",
"enterInn": "Enter INN",
"kpp": "KPP",
"enterKpp": "Enter KPP",
"accountNumber": "Account Number",
"enterAccountNumber": "Enter account number",
"correspondentAccount": "Correspondent Account",
"enterCorrespondentAccount": "Enter correspondent account",
"bik": "BIK",
"enterBik": "Enter BIK",
"add": "Add",
"expiryDate": "Expiry (MM/YY)",
"firstName": "First Name",
"enterFirstName": "Enter First Name",
"lastName": "Last Name",
"enterLastName": "Enter Last Name",
"sendSingle": "Send single transaction",
"sendMultiple": "Send multiple transactions",
"addFunds": "Add Funds",
"close": "Close",
"multiplePayout": "Multiple Payout",
"howItWorks": "How it works?",
"exampleTitle": "File Format & Sample",
"downloadSampleCSV": "Download sample.csv",
"tokenColumn": "Token (required)",
"currency": "Currency",
"amount": "Amount",
"comment": "Comment",
"uploadCSV": "Upload your CSV",
"upload": "Upload",
"hintUpload": "Supported format: .CSV · Max size 1 MB",
"uploadHistory": "Upload History",
"payout": "Payout",
"sendTo": "Send Payout To",
"send": "Send Payout",
"recipientPaysFee": "Recipient pays the fee",
"sentAmount": "Sent amount: ${amount}",
"@sentAmount": {
"description": "Label showing the amount sent",
"placeholders": {
"amount": {
"type": "String"
}
}
},
"fee": "Fee: ${fee}",
"@fee": {
"description": "Label showing the transaction fee",
"placeholders": {
"fee": {
"type": "String"
}
}
},
"recipientWillReceive": "Recipient will receive: ${amount}",
"@recipientWillReceive": {
"description": "Label showing how much the recipient will receive",
"placeholders": {
"amount": {
"type": "String"
}
}
},
"total": "Total: ${total}",
"@total": {
"description": "Label showing the total amount of the transaction",
"placeholders": {
"total": {
"type": "String"
}
}
},
"hideDetails": "Hide Details",
"showDetails": "Show Details",
"whereGetMoney": "Source of funds for debit",
"details": "Details",
"addRecipient": "Add Recipient",
"editRecipient": "Edit Recipient",
"saveRecipient": "Save Recipient",
"choosePaymentMethod": "Payment Methods (choose at least 1)",
"recipientFormRule": "Recipient must have at least one payment method",
"allStatus": "All",
"readyStatus": "Ready",
"registeredStatus": "Registered",
"notRegisteredStatus": "Not registered",
"noRecipientSelected": "No recipient selected",
"companyName": "Name of your company",
"companynameRequired": "Company name required",
"errorSignUp": "Error occured while signing up, try again later",
"companyDescription": "Company Description",
"companyDescriptionHint": "Describe any of the fields of the Company's business",
"optional": "optional"
}

View File

@@ -0,0 +1,426 @@
{
"@@locale": "ru",
"login": "Войти",
"logout": "Выйти",
"profile": "Профиль",
"signup": "Регистрация",
"username": "Email",
"usernameHint": "email@example.com",
"usernameErrorInvalid": "Укажите действительный адрес электронной почты",
"usernameUnknownTLD": "Домен .{domain} неизвестен, пожалуйста, проверьте его",
"password": "Пароль",
"confirmPassword": "Подтвердите пароль",
"passwordValidationRuleDigit": "содержит цифру",
"passwordValidationRuleUpperCase": "содержит заглавную букву",
"passwordValidationRuleLowerCase": "содержит строчную букву",
"passwordValidationRuleSpecialCharacter": "содержит специальный символ",
"passwordValidationRuleMinCharacters": "длина не менее {charNum} символов",
"passwordsDoNotMatch": "Пароли не совпадают",
"passwordValidationError": "Убедитесь, что ваш пароль {matchesCriteria}",
"notificationError": "Произошла ошибка: {error}",
"loginUserNotFound": "Аккаунт {account} не зарегистрирован в системе",
"loginPasswordIncorrect": "Ошибка авторизации, пожалуйста, проверьте пароль",
"internalErrorOccurred": "Произошла внутренняя ошибка сервера: {error}, мы уже знаем о ней и усердно работаем над исправлением",
"noErrorInformation": "Произошла ошибка, но у нас нет информации о ней. Мы уже расследуем этот вопрос",
"yourName": "Ваше имя",
"nameHint": "Иван Иванов",
"errorPageNotFoundTitle": "Страница не найдена",
"errorPageNotFoundMessage": "Упс! Мы не смогли найти эту страницу.",
"errorPageNotFoundHint": "Запрашиваемая страница не существует или была перемещена. Пожалуйста, проверьте URL или вернитесь на главную страницу.",
"errorUnknown": "Произошла неизвестная ошибка",
"unknown": "неизвестно",
"goToLogin": "Перейти к входу",
"goBack": "Назад",
"goToMainPage": "На главную",
"goToSignUp": "Перейти к регистрации",
"signupError": "Не удалось зарегистрироваться: {error}",
"signupSuccess": "Письмо с подтверждением email отправлено на {email}. Пожалуйста, откройте его и перейдите по ссылке для активации вашего аккаунта.",
"connectivityError": "Не удается связаться с сервером {serverAddress}. Проверьте ваше интернет-соединение и попробуйте снова.",
"errorAccountNotVerified": "Ваш аккаунт еще не подтвержден. Пожалуйста, проверьте вашу электронную почту для завершения верификации",
"errorLoginUnauthorized": "Неверный логин или пароль. Пожалуйста, попробуйте снова",
"errorInternalError": "Произошла внутренняя ошибка. Мы в курсе проблемы и работаем над ее решением. Пожалуйста, попробуйте позже",
"errorVerificationTokenNotFound": "Аккаунт для верификации не найден. Зарегистрируйтесь снова",
"created": "Создано",
"edited": "Изменено",
"errorDataConflict": "Мы не можем обработать ваши данные, так как они содержат конфликтующую или противоречивую информацию.",
"errorAccessDenied": "У вас нет разрешения на доступ к этому ресурсу. Если вам нужен доступ, пожалуйста, обратитесь к администратору.",
"errorBrokenPayload": "Отправленные данные недействительны или неполны. Пожалуйста, проверьте введенные данные и попробуйте снова.",
"errorInvalidArgument": "Один или несколько аргументов недействительны. Проверьте введенные данные и попробуйте снова.",
"errorBrokenReference": "Ресурс, к которому вы пытаетесь получить доступ, не может быть найден. Возможно, он был перемещен или удален.",
"errorInvalidQueryParameter": "Один или несколько параметров запроса отсутствуют или указаны неверно. Проверьте их и попробуйте снова.",
"errorNotImplemented": "Эта функция еще недоступна. Пожалуйста, попробуйте позже или обратитесь в службу поддержки.",
"errorLicenseRequired": "Для выполнения этого действия требуется действующая лицензия. Пожалуйста, обратитесь к вашему администратору.",
"errorNotFound": "Мы не смогли найти запрошенный ресурс. Возможно, он был удален или временно недоступен.",
"errorNameMissing": "Пожалуйста, укажите имя для продолжения.",
"errorEmailMissing": "Пожалуйста, укажите адрес электронной почты для продолжения.",
"errorPasswordMissing": "Пожалуйста, укажите пароль для продолжения.",
"errorEmailNotRegistered": "Мы не нашли аккаунт, связанный с этим адресом электронной почты.",
"errorDuplicateEmail": "Этот адрес электронной почты уже используется. Попробуйте другой или восстановите пароль.",
"showDetailsAction": "Показать детали",
"errorLogin": "Ошибка входа",
"errorCreatingInvitation": "Не удалось создать приглашение",
"@errorCreatingInvitation": {
"description": "Сообщение об ошибке, отображаемое при неудачном создании приглашения"
},
"footerCompanyName": "Sibilla Solutions LTD",
"footerAddress": "27, Pindarou Street, Alpha Business Centre, Block B 7th Floor, 1060 Nicosia, Cyprus",
"footerSupport": "Поддержка",
"footerEmail": "Email TBD",
"footerPhoneLabel": "Телефон",
"footerPhone": "+357 22 000 253",
"footerTermsOfService": "Условия обслуживания",
"footerPrivacyPolicy": "Политика конфиденциальности",
"footerCookiePolicy": "Политика использования файлов cookie",
"navigationLogout": "Выйти",
"dashboard": "Дашборд",
"navigationUsersSettings": "Пользователи",
"navigationRolesSettings": "Роли",
"navigationPermissionsSettings": "Разрешения",
"usersManagement": "Управление пользователями",
"navigationOrganizationSettings": "Настройки организации",
"navigationAccountSettings": "Настройки профиля",
"twoFactorPrompt": "Введите 6-значный код, отправленный на ваше устройство",
"twoFactorResend": "Не получили код? Отправить снова",
"twoFactorTitle": "Двухфакторная аутентификация",
"twoFactorError": "Неверный код. Пожалуйста, попробуйте снова.",
"payoutNavDashboard": "Дашборд",
"payoutNavSendPayout": "Отправить выплату",
"payoutNavRecipients": "Получатели",
"payoutNavReports": "Отчеты",
"payoutNavSettings": "Настройки",
"payoutNavLogout": "Выйти",
"payoutNavMethods": "Выплаты",
"expand": "Развернуть",
"collapse": "Свернуть",
"pageTitleRecipients": "Адресная книга получателей",
"@pageTitleRecipients": {
"description": "Заголовок страницы адресной книги получателей",
"type": "text"
},
"actionAddNew": "Добавить",
"@actionAddNew": {
"description": "Подсказка и метка кнопки для добавления нового получателя"
},
"colDataOwner": "Владелец данных",
"@colDataOwner": {
"description": "Заголовок столбца для указания, кто управляет данными о выплатах"
},
"colAvatar": "Аватар",
"@colAvatar": {
"description": "Заголовок столбца для аватара получателя"
},
"colName": "Имя",
"@colName": {
"description": "Заголовок столбца для имени получателя"
},
"colEmail": "Email",
"@colEmail": {
"description": "Заголовок столбца для адреса электронной почты получателя"
},
"colStatus": "Статус",
"@colStatus": {
"description": "Заголовок столбца для статуса готовности к выплате"
},
"statusReady": "Готов",
"@statusReady": {
"description": "Статус, указывающий, что выплаты можно отправлять немедленно"
},
"statusRegistered": "Зарегистрирован",
"@statusRegistered": {
"description": "Статус, указывающий, что получатель зарегистрирован, но еще не полностью готов"
},
"statusNotRegistered": "Не зарегистрирован",
"@statusNotRegistered": {
"description": "Статус, указывающий, что получатель не завершил регистрацию"
},
"typeInternal": "Управляется мной",
"@typeInternal": {
"description": "Метка для получателей, чьи данные о выплатах управляются внутренне пользователем/компанией"
},
"typeExternal": "Самоуправляемый",
"@typeExternal": {
"description": "Метка для получателей, которые управляют своими данными о выплатах самостоятельно"
},
"searchHint": "Поиск получателей",
"colActions": "Действия",
"menuEdit": "Редактировать",
"menuSendPayout": "Отправить выплату",
"tooltipRowActions": "Другие действия",
"accountSettings": "Настройки аккаунта",
"accountNameUpdateError": "Не удалось обновить имя аккаунта",
"settingsSuccessfullyUpdated": "Настройки успешно обновлены",
"language": "Язык",
"failedToUpdateLanguage": "Не удалось обновить язык",
"settingsImageUpdateError": "Не удалось обновить изображение",
"settingsImageTitle": "Изображение",
"settingsImageHint": "Нажмите, чтобы изменить изображение",
"accountName": "Имя",
"accountNameHint": "Укажите ваше имя",
"avatar": "Фото профиля",
"avatarHint": "Нажмите для обновления",
"avatarUpdateError": "Не удалось обновить фото профиля",
"settings": "Настройки",
"notSet": "не задано",
"search": "Поиск...",
"ok": "Ок",
"cancel": "Отмена",
"confirm": "Подтвердить",
"back": "Назад",
"operationfryTitle": "История операций",
"@operationfryTitle": {
"description": "Заголовок страницы истории операций"
},
"filters": "Фильтры",
"@filters": {
"description": "Метка для панели расширения фильтров"
},
"period": "Период",
"@period": {
"description": "Метка для фильтра по диапазону дат"
},
"selectPeriod": "Выберите период",
"@selectPeriod": {
"description": "Заполнитель, когда период не выбран"
},
"apply": "Применить",
"@apply": {
"description": "Текст кнопки для применения фильтров"
},
"status": "{status}",
"@status": {
"description": "Шаблон для одного чипа фильтра статуса",
"placeholders": {
"status": {
"type": "String",
"example": "Успешно"
}
}
},
"operationStatusSuccessful": "Успешно",
"@operationStatusSuccessful": {
"description": "Статус, указывающий на успешное выполнение операции"
},
"operationStatusPending": "В ожидании",
"@operationStatusPending": {
"description": "Статус, указывающий, что операция ожидает выполнения"
},
"operationStatusUnsuccessful": "Неуспешно",
"@operationStatusUnsuccessful": {
"description": "Статус, указывающий на сбой операции"
},
"statusColumn": "Статус",
"@statusColumn": {
"description": "Заголовок столбца таблицы для статуса"
},
"fileNameColumn": "Имя файла",
"@fileNameColumn": {
"description": "Заголовок столбца таблицы для имени файла"
},
"amountColumn": "Сумма",
"@amountColumn": {
"description": "Заголовок столбца таблицы для исходной суммы"
},
"toAmountColumn": "На сумму",
"@toAmountColumn": {
"description": "Заголовок столбца таблицы для конвертированной суммы"
},
"payIdColumn": "Pay ID",
"@payIdColumn": {
"description": "Заголовок столбца таблицы для идентификатора платежа"
},
"cardNumberColumn": "Номер карты",
"@cardNumberColumn": {
"description": "Заголовок столбца таблицы для замаскированного номера карты"
},
"nameColumn": "Имя",
"@nameColumn": {
"description": "Заголовок столбца таблицы для имени получателя"
},
"dateColumn": "Дата",
"@dateColumn": {
"description": "Заголовок столбца таблицы для даты/времени"
},
"commentColumn": "Комментарий",
"@commentColumn": {
"description": "Заголовок столбца таблицы для комментария"
},
"paymentConfigTitle": "Куда получать деньги",
"paymentConfigSubtitle": "Добавьте несколько методов и выберите основной.",
"addPaymentMethod": "Добавить способ оплаты",
"makeMain": "Сделать основным",
"advanced": "Дополнительно",
"fallbackExplanation": "Если основной метод недоступен, мы попробуем следующий включенный метод в списке.",
"delete": "Удалить",
"@delete": {
"description": "Метка кнопки для удаления способа оплаты"
},
"deletePaymentConfirmation": "Вы уверены, что хотите удалить этот способ оплаты?",
"@deletePaymentConfirmation": {
"description": "Сообщение диалога подтверждения, показываемое перед удалением способа оплаты"
},
"edit": "Редактировать",
"@edit": {
"description": "Метка кнопки для редактирования способа оплаты"
},
"moreActions": "Еще действия",
"@moreActions": {
"description": "Подсказка для кнопки меню с многоточием, открывающей дополнительные действия для способа оплаты"
},
"noPayouts": "Нет выплат",
"enterBankName": "Введите название банка",
"paymentType": "Тип способа оплаты",
"selectPaymentType": "Пожалуйста, выберите тип способа оплаты",
"paymentTypeCard": "Кредитная карта",
"paymentTypeBankAccount": "Российский банковский счет",
"paymentTypeIban": "IBAN",
"paymentTypeWallet": "Кошелек",
"cardNumber": "Номер карты",
"enterCardNumber": "Введите номер карты",
"cardholderName": "Имя держателя карты",
"iban": "IBAN",
"enterIban": "Введите IBAN",
"bic": "BIC",
"bankName": "Название банка",
"accountHolder": "Владелец счета",
"enterAccountHolder": "Введите владельца счета",
"enterBic": "Введите BIC",
"walletId": "ID кошелька",
"enterWalletId": "Введите ID кошелька",
"recipients": "Получатели",
"recipientName": "Имя получателя",
"enterRecipientName": "Введите имя получателя",
"inn": "ИНН",
"enterInn": "Введите ИНН",
"kpp": "КПП",
"enterKpp": "Введите КПП",
"accountNumber": "Номер счета",
"enterAccountNumber": "Введите номер счета",
"correspondentAccount": "Корреспондентский счет",
"enterCorrespondentAccount": "Введите корреспондентский счет",
"bik": "БИК",
"enterBik": "Введите БИК",
"add": "Добавить",
"expiryDate": "Срок действия (ММ/ГГ)",
"firstName": "Имя",
"enterFirstName": "Введите имя",
"lastName": "Фамилия",
"enterLastName": "Введите фамилию",
"sendSingle": "Отправить одну транзакцию",
"sendMultiple": "Отправить несколько транзакций",
"addFunds": "Пополнить счет",
"close": "Закрыть",
"multiplePayout": "Множественная выплата",
"howItWorks": "Как это работает?",
"exampleTitle": "Формат файла и образец",
"downloadSampleCSV": "Скачать sample.csv",
"tokenColumn": "Токен (обязательно)",
"currency": "Валюта",
"amount": "Сумма",
"comment": "Комментарий",
"uploadCSV": "Загрузите ваш CSV",
"upload": "Загрузить",
"hintUpload": "Поддерживаемый формат: .CSV · Макс. размер 1 МБ",
"uploadHistory": "История загрузок",
"payout": "Выплата",
"sendTo": "Отправить выплату",
"send": "Отправить выплату",
"recipientPaysFee": "Получатель оплачивает комиссию",
"sentAmount": "Отправленная сумма: ${amount}",
"@sentAmount": {
"description": "Метка, показывающая отправленную сумму",
"placeholders": {
"amount": {
"type": "String"
}
}
},
"fee": "Комиссия: ${fee}",
"@fee": {
"description": "Метка, показывающая комиссию за транзакцию",
"placeholders": {
"fee": {
"type": "String"
}
}
},
"recipientWillReceive": "Получатель получит: ${amount}",
"@recipientWillReceive": {
"description": "Метка, показывающая, сколько получит получатель",
"placeholders": {
"amount": {
"type": "String"
}
}
},
"total": "Итого: ${total}",
"@total": {
"description": "Метка, показывающая общую сумму транзакции",
"placeholders": {
"total": {
"type": "String"
}
}
},
"hideDetails": "Скрыть детали",
"showDetails": "Показать детали",
"whereGetMoney": "Источник средств для списания",
"details": "Детали",
"addRecipient": "Добавить получателя",
"editRecipient": "Редактировать получателя",
"saveRecipient": "Сохранить получателя",
"choosePaymentMethod": "Способы оплаты (выберите хотя бы 1)",
"recipientFormRule": "Получатель должен иметь хотя бы один способ оплаты",
"allStatus": "Все",
"readyStatus": "Готов",
"registeredStatus": "Зарегистрирован",
"notRegisteredStatus": "Не зарегистрирован",
"noRecipientSelected": "Получатель не выбран"
}

104
frontend/pweb/lib/main.dart Normal file
View File

@@ -0,0 +1,104 @@
import 'package:flutter/material.dart';
// ignore: depend_on_referenced_packages
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
import 'package:provider/provider.dart';
import 'package:logging/logging.dart';
import 'package:pshared/config/constants.dart';
import 'package:pshared/provider/account.dart';
import 'package:pshared/provider/locale.dart';
import 'package:pshared/provider/organizations.dart';
import 'package:pshared/provider/pfe/provider.dart';
import 'package:pweb/app/app.dart';
import 'package:pweb/app/timeago.dart';
import 'package:pweb/providers/balance.dart';
import 'package:pweb/providers/carousel.dart';
import 'package:pweb/providers/mock_payment.dart';
import 'package:pweb/providers/page_selector.dart';
import 'package:pweb/providers/payment_methods.dart';
import 'package:pweb/providers/recipient.dart';
import 'package:pweb/providers/two_factor.dart';
import 'package:pweb/providers/upload_history.dart';
import 'package:pweb/providers/wallets.dart';
import 'package:pweb/services/amplitude.dart';
import 'package:pweb/services/auth.dart';
import 'package:pweb/services/balance.dart';
import 'package:pweb/services/payments/payment_methods.dart';
import 'package:pweb/services/payments/upload_history.dart';
import 'package:pweb/services/recipient/recipient.dart';
import 'package:pweb/services/wallets.dart';
void _setupLogging() {
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((record) {
// ignore: avoid_print
print('${record.level.name}: ${record.time}: ${record.loggerName}: ${record.message}');
});
}
void main() async {
await Constants.initialize();
await AmplitudeService.initialize();
_setupLogging();
setUrlStrategy(PathUrlStrategy());
initializeTimeagoLocales();
runApp(
MultiProvider(
providers: [
Provider<AuthenticationService>(
create: (_) => AuthenticationService(),
),
ChangeNotifierProxyProvider<AuthenticationService, TwoFactorProvider>(
create: (context) => TwoFactorProvider(
context.read<AuthenticationService>(),
),
update: (context, authService, previous) => TwoFactorProvider(authService),
),
ChangeNotifierProvider(create: (_) => LocaleProvider(null)),
ChangeNotifierProvider(create: (_) => AccountProvider()),
ChangeNotifierProvider(create: (_) => OrganizationsProvider()),
ChangeNotifierProvider(create: (_) => PfeProvider()),
ChangeNotifierProvider(create: (_) => CarouselIndexProvider()),
ChangeNotifierProvider(
create: (_) => UploadHistoryProvider(service: MockUploadHistoryService())..load(),
),
ChangeNotifierProvider(
create: (_) => PaymentMethodsProvider(service: MockPaymentMethodsService())..loadMethods(),
),
ChangeNotifierProvider(
create: (_) => WalletsProvider(MockWalletsService())..loadData(),
),
ChangeNotifierProvider(
create: (_) => MockPaymentProvider(),
),
ChangeNotifierProvider(
create: (_) => RecipientProvider(RecipientService())..loadRecipients(),
),
ChangeNotifierProvider(
create: (context) {
final recipient = context.read<RecipientProvider?>();
final wallets = context.read<WalletsProvider?>();
return PageSelectorProvider(
recipientProvider: recipient,
walletsProvider: wallets,
);
},
),
ChangeNotifierProvider(
create: (_) => BalanceProvider(MockBalanceService())..loadData(),
),
],
child: const PayApp(),
),
);
}

View File

@@ -0,0 +1 @@
enum Currency {usd, eur, rub, usdt, usdc}

View File

@@ -0,0 +1,38 @@
import 'package:pweb/models/currency.dart';
class Wallet {
final String id;
final String walletUserID; // ID or number that we show the user
final String name;
final double balance;
final Currency currency;
final bool isHidden;
Wallet({
required this.id,
required this.walletUserID,
required this.name,
required this.balance,
required this.currency,
this.isHidden = true,
});
Wallet copyWith({
String? id,
String? name,
double? balance,
Currency? currency,
String? walletUserID,
bool? isHidden,
}) {
return Wallet(
id: id ?? this.id,
name: name ?? this.name,
balance: balance ?? this.balance,
currency: currency ?? this.currency,
walletUserID: walletUserID ?? this.walletUserID,
isHidden: isHidden ?? this.isHidden,
);
}
}

View File

@@ -0,0 +1,16 @@
import 'package:flutter/material.dart';
class ErrorMessage extends StatelessWidget {
final String error;
const ErrorMessage({super.key, required this.error});
@override
Widget build(BuildContext context) => Text(
error,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.error,
),
);
}

View File

@@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
import 'package:pin_code_fields/pin_code_fields.dart';
class TwoFactorCodeInput extends StatelessWidget {
final void Function(String) onCompleted;
const TwoFactorCodeInput({super.key, required this.onCompleted});
@override
Widget build(BuildContext context) => Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 300),
child: PinCodeTextField(
length: 6,
appContext: context,
autoFocus: true,
keyboardType: TextInputType.number,
animationType: AnimationType.fade,
pinTheme: PinTheme(
shape: PinCodeFieldShape.box,
borderRadius: BorderRadius.circular(4),
fieldHeight: 48,
fieldWidth: 40,
inactiveColor: Theme.of(context).dividerColor,
activeColor: Theme.of(context).colorScheme.primary,
selectedColor: Theme.of(context).colorScheme.primary,
),
onCompleted: onCompleted,
onChanged: (_) {},
),
),
);
}

View File

@@ -0,0 +1,61 @@
import 'package:flutter/material.dart';
import 'package:pweb/pages/2fa/error_message.dart';
import 'package:pweb/pages/2fa/input.dart';
import 'package:pweb/pages/2fa/prompt.dart';
import 'package:pweb/pages/2fa/resend.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
import 'package:provider/provider.dart';
import 'package:pweb/providers/two_factor.dart';
class TwoFactorCodePage extends StatelessWidget {
final VoidCallback onVerificationSuccess;
const TwoFactorCodePage({
super.key,
required this.onVerificationSuccess,
});
@override
Widget build(BuildContext context) {
return Consumer<TwoFactorProvider>(
builder: (context, provider, child) {
if (provider.verificationSuccess) {
WidgetsBinding.instance.addPostFrameCallback((_) {
onVerificationSuccess();
});
}
return Scaffold(
appBar: AppBar(title: const Text('')),
body: Padding(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const TwoFactorPromptText(),
const SizedBox(height: 32),
TwoFactorCodeInput(
onCompleted: (code) => provider.submitCode(code),
),
const SizedBox(height: 24),
if (provider.isSubmitting)
const Center(child: CircularProgressIndicator())
else
const ResendCodeButton(),
if (provider.hasError) ...[
const SizedBox(height: 12),
ErrorMessage(error: AppLocalizations.of(context)!.twoFactorError),
],
],
),
),
);
},
);
}
}

View File

@@ -0,0 +1,15 @@
import 'package:flutter/material.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
class TwoFactorPromptText extends StatelessWidget {
const TwoFactorPromptText({super.key});
@override
Widget build(BuildContext context) => Text(
AppLocalizations.of(context)!.twoFactorPrompt,
style: Theme.of(context).textTheme.bodyLarge,
textAlign: TextAlign.center,
);
}

View File

@@ -0,0 +1,31 @@
import 'package:flutter/material.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
class ResendCodeButton extends StatelessWidget {
const ResendCodeButton({super.key});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final localizations = AppLocalizations.of(context)!;
return TextButton(
onPressed: () {
// TODO: Add resend logic
},
style: TextButton.styleFrom(
padding: EdgeInsets.zero,
minimumSize: const Size(0, 0),
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
alignment: Alignment.centerLeft,
foregroundColor: theme.colorScheme.primary,
textStyle: theme.textTheme.bodyMedium?.copyWith(
decoration: TextDecoration.underline,
),
),
child: Text(localizations.twoFactorResend),
);
}
}

View File

@@ -0,0 +1,90 @@
import 'package:flutter/material.dart';
import 'package:pshared/models/payment/type.dart';
import 'package:pweb/pages/payment_methods/form.dart';
import 'package:pweb/pages/payment_methods/icon.dart';
class AdressBookPaymentMethodTile extends StatefulWidget {
final PaymentType type;
final String title;
final Map<PaymentType, Object?> methods;
final ValueChanged<Object?> onChanged;
final double spacingM;
final double spacingS;
final double sizeM;
final TextStyle? titleTextStyle;
const AdressBookPaymentMethodTile({
super.key,
required this.type,
required this.title,
required this.methods,
required this.onChanged,
this.spacingM = 12,
this.spacingS = 8,
this.sizeM = 20,
this.titleTextStyle,
});
@override
State<AdressBookPaymentMethodTile> createState() => _AdressBookPaymentMethodTileState();
}
class _AdressBookPaymentMethodTileState extends State<AdressBookPaymentMethodTile> {
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final isAdded = widget.methods.containsKey(widget.type);
return ExpansionTile(
title: Row(
children: [
Icon(
iconForPaymentType(widget.type),
size: widget.sizeM,
color: isAdded
? theme.colorScheme.primary
: theme.colorScheme.onSurface,
),
SizedBox(width: widget.spacingS),
Text(
widget.title,
style: widget.titleTextStyle ??
theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
color: isAdded ? theme.colorScheme.primary : null,
),
),
],
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (isAdded)
IconButton(
icon: Icon(Icons.delete, color: theme.colorScheme.error),
onPressed: () {
widget.onChanged(null);
},
),
Icon(
isAdded ? Icons.check_circle : Icons.add_circle_outline,
color: isAdded ? theme.colorScheme.primary : null,
),
],
),
children: [
PaymentMethodForm(
key: ValueKey(widget.type),
selectedType: widget.type,
initialData: widget.methods[widget.type],
onChanged: widget.onChanged,
),
SizedBox(height: widget.spacingM),
],
);
}
}

View File

@@ -0,0 +1,109 @@
import 'package:flutter/material.dart';
import 'package:pshared/models/payment/methods/card.dart';
import 'package:pshared/models/payment/methods/iban.dart';
import 'package:pshared/models/payment/methods/russian_bank.dart';
import 'package:pshared/models/payment/methods/wallet.dart';
import 'package:pshared/models/payment/type.dart';
import 'package:pshared/models/recipient/recipient.dart';
import 'package:pshared/models/recipient/status.dart';
import 'package:pshared/models/recipient/type.dart';
import 'package:pweb/pages/address_book/form/view.dart';
import 'package:pweb/services/amplitude.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
class AdressBookRecipientForm extends StatefulWidget {
final Recipient? recipient;
final ValueChanged<Recipient?>? onSaved;
const AdressBookRecipientForm({super.key, this.recipient, this.onSaved});
@override
State<AdressBookRecipientForm> createState() => _AdressBookRecipientFormState();
}
class _AdressBookRecipientFormState extends State<AdressBookRecipientForm> {
final _formKey = GlobalKey<FormState>();
late TextEditingController _nameCtrl;
late TextEditingController _emailCtrl;
RecipientType _type = RecipientType.internal;
RecipientStatus _status = RecipientStatus.ready;
final Map<PaymentType, Object?> _methods = {};
@override
void initState() {
super.initState();
final r = widget.recipient;
_nameCtrl = TextEditingController(text: r?.name ?? "");
_emailCtrl = TextEditingController(text: r?.email ?? "");
_type = r?.type ?? RecipientType.internal;
_status = r?.status ?? RecipientStatus.ready;
if (r?.card != null) _methods[PaymentType.card] = r!.card;
if (r?.iban != null) _methods[PaymentType.iban] = r!.iban;
if (r?.wallet != null) _methods[PaymentType.wallet] = r!.wallet;
if (r?.bank != null) _methods[PaymentType.bankAccount] = r!.bank;
}
//TODO Change when registration is ready
void _save() {
if (!_formKey.currentState!.validate() || _methods.isEmpty) {
AmplitudeService.recipientAddCompleted(
_type,
_status,
_methods.keys.toSet(),
);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context)!.recipientFormRule),
),
);
return;
}
final recipient = Recipient(
name: _nameCtrl.text,
email: _emailCtrl.text,
type: _type,
status: _status,
avatarUrl: null,
card: _methods[PaymentType.card] as CardPaymentMethod?,
iban: _methods[PaymentType.iban] as IbanPaymentMethod?,
wallet: _methods[PaymentType.wallet] as WalletPaymentMethod?,
bank: _methods[PaymentType.bankAccount] as RussianBankAccountPaymentMethod?,
);
widget.onSaved?.call(recipient);
}
@override
Widget build(BuildContext context) {
return FormView(
formKey: _formKey,
nameCtrl: _nameCtrl,
emailCtrl: _emailCtrl,
type: _type,
status: _status,
methods: _methods,
onTypeChanged: (t) => setState(() => _type = t),
onStatusChanged: (s) => setState(() => _status = s),
onMethodsChanged: (type, data) {
setState(() {
if (data != null) {
_methods[type] = data;
} else {
_methods.remove(type);
}
});
},
onSave: _save,
isEditing: widget.recipient != null,
onBack: () {
widget.onSaved?.call(null);
},
);
}
}

View File

@@ -0,0 +1,124 @@
import 'package:flutter/material.dart';
import 'package:pshared/models/payment/type.dart';
import 'package:pshared/models/recipient/status.dart';
import 'package:pshared/models/recipient/type.dart';
import 'package:pweb/utils/payment/label.dart';
import 'package:pweb/pages/address_book/form/method_tile.dart';
import 'package:pweb/pages/address_book/form/widgets/button.dart';
import 'package:pweb/pages/address_book/form/widgets/email_field.dart';
import 'package:pweb/pages/address_book/form/widgets/header.dart';
import 'package:pweb/pages/address_book/form/widgets/name_field.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
class FormView extends StatelessWidget {
final GlobalKey<FormState> formKey;
final TextEditingController nameCtrl;
final TextEditingController emailCtrl;
final RecipientType type;
final RecipientStatus status;
final Map<PaymentType, Object?> methods;
final ValueChanged<RecipientType> onTypeChanged;
final ValueChanged<RecipientStatus> onStatusChanged;
final void Function(PaymentType, Object?) onMethodsChanged;
final VoidCallback onSave;
final bool isEditing;
final VoidCallback onBack;
final double maxWidth;
final double elevation;
final double borderRadius;
final EdgeInsetsGeometry padding;
final double spacingHeader;
final double spacingFields;
final double spacingDivider;
final double spacingSave;
final double spacingBottom;
final TextStyle? titleTextStyle;
const FormView({
super.key,
required this.formKey,
required this.nameCtrl,
required this.emailCtrl,
required this.type,
required this.status,
required this.methods,
required this.onTypeChanged,
required this.onStatusChanged,
required this.onMethodsChanged,
required this.onSave,
required this.isEditing,
required this.onBack,
this.maxWidth = 500,
this.elevation = 4,
this.borderRadius = 16,
this.padding = const EdgeInsets.all(20),
this.spacingHeader = 20,
this.spacingFields = 12,
this.spacingDivider = 40,
this.spacingSave = 30,
this.spacingBottom = 16,
this.titleTextStyle,
});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Align(
alignment: Alignment.topCenter,
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: maxWidth),
child: Material(
elevation: elevation,
borderRadius: BorderRadius.circular(borderRadius),
color: theme.colorScheme.onSecondary,
child: Padding(
padding: padding,
child: Form(
key: formKey,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
HeaderWidget(
isEditing: isEditing,
onBack: onBack,
),
SizedBox(height: spacingHeader),
NameField(controller: nameCtrl),
SizedBox(height: spacingFields),
EmailField(controller: emailCtrl),
Divider(height: spacingDivider),
Text(
AppLocalizations.of(context)!.choosePaymentMethod,
style: titleTextStyle ??
theme.textTheme.titleMedium
?.copyWith(fontWeight: FontWeight.bold),
),
SizedBox(height: spacingFields),
...PaymentType.values.map(
(p) => AdressBookPaymentMethodTile(
type: p,
title: getPaymentTypeLabel(context, p),
methods: methods,
onChanged: (data) => onMethodsChanged(p, data),
),
),
SizedBox(height: spacingSave),
SaveButton(onSave: onSave),
SizedBox(height: spacingBottom),
],
),
),
),
),
),
),
);
}
}

View File

@@ -0,0 +1,56 @@
import 'package:flutter/material.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
class SaveButton extends StatelessWidget {
final VoidCallback onSave;
final double width;
final double height;
final double borderRadius;
final String? text;
final TextStyle? textStyle;
const SaveButton({
super.key,
required this.onSave,
this.width = 200,
this.height = 45,
this.borderRadius = 12,
this.text,
this.textStyle,
});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Center(
child: SizedBox(
width: width,
height: height,
child: InkWell(
borderRadius: BorderRadius.circular(borderRadius),
onTap: onSave,
child: Container(
decoration: BoxDecoration(
color: theme.colorScheme.primary,
borderRadius: BorderRadius.circular(borderRadius),
),
child: Center(
child: Text(
text ?? AppLocalizations.of(context)!.saveRecipient,
style: textStyle ??
theme.textTheme.labelLarge?.copyWith(
color: theme.colorScheme.onPrimary,
fontWeight: FontWeight.w600,
),
),
),
),
),
),
);
}
}

View File

@@ -0,0 +1,62 @@
import 'package:flutter/material.dart';
class ChoiceChips<T> extends StatelessWidget {
final String label;
final List<T> values;
final T selected;
final ValueChanged<T> onChanged;
final double spacing;
final double runSpacing;
final double labelSpacing;
const ChoiceChips({
super.key,
required this.label,
required this.values,
required this.selected,
required this.onChanged,
this.spacing = 8,
this.runSpacing = 8,
this.labelSpacing = 8,
});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: theme.textTheme.titleMedium!.copyWith(fontWeight: FontWeight.bold),
),
SizedBox(height: labelSpacing),
Wrap(
spacing: spacing,
runSpacing: runSpacing,
children: values.map((v) {
final isSelected = v == selected;
return ChoiceChip(
selectedColor: theme.colorScheme.primary,
backgroundColor: theme.colorScheme.onSecondary,
showCheckmark: false,
label: Text(
v.toString().split('.').last,
style: TextStyle(
color: isSelected
? theme.colorScheme.onSecondary
: theme.colorScheme.inverseSurface,
),
),
selected: isSelected,
onSelected: (_) => onChanged(v),
);
}).toList(),
),
],
);
}
}

View File

@@ -0,0 +1,36 @@
import 'package:flutter/material.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
class EmailField extends StatelessWidget {
final TextEditingController controller;
final double borderRadius;
final EdgeInsetsGeometry contentPadding;
const EmailField({
super.key,
required this.controller,
this.borderRadius = 12,
this.contentPadding = const EdgeInsets.symmetric(horizontal: 12, vertical: 14),
});
@override
Widget build(BuildContext context) {
final loc = AppLocalizations.of(context)!;
return TextFormField(
controller: controller,
decoration: InputDecoration(
labelText: loc.username,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(borderRadius),
),
contentPadding: contentPadding,
),
validator: (v) =>
v == null || v.isEmpty ? loc.usernameErrorInvalid : null,
);
}
}

View File

@@ -0,0 +1,44 @@
import 'package:flutter/material.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
class HeaderWidget extends StatelessWidget {
final bool isEditing;
final VoidCallback? onBack;
final double spacing;
final TextStyle? textStyle;
const HeaderWidget({
super.key,
required this.isEditing,
this.onBack,
this.spacing = 8,
this.textStyle,
});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final l10n = AppLocalizations.of(context)!;
return Row(
children: [
IconButton(
icon: const Icon(Icons.arrow_back),
color: theme.colorScheme.primary,
onPressed: onBack,
),
SizedBox(width: spacing),
Text(
isEditing ? l10n.editRecipient : l10n.addRecipient,
style: textStyle ??
theme.textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.bold,
),
),
],
);
}
}

View File

@@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
class NameField extends StatelessWidget {
final TextEditingController controller;
final double borderRadius;
final EdgeInsetsGeometry contentPadding;
const NameField({
super.key,
required this.controller,
this.borderRadius = 12,
this.contentPadding = const EdgeInsets.symmetric(horizontal: 12, vertical: 14),
});
@override
Widget build(BuildContext context) {
final loc = AppLocalizations.of(context)!;
return TextFormField(
controller: controller,
decoration: InputDecoration(
labelText: loc.recipientName,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(borderRadius),
),
contentPadding: contentPadding,
),
validator: (v) => v == null || v.isEmpty ? loc.enterRecipientName : null,
);
}
}

Some files were not shown because too many files have changed in this diff Show More