├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── client_checkout.yml │ ├── client_deploy_preview_channel.yml │ ├── client_deploy_production.yml │ ├── server_checkout.yml │ └── server_deploy_production.yml ├── .gitignore ├── Makefile ├── README.md ├── client ├── .firebaserc ├── .gitignore ├── .metadata ├── .run │ ├── App [dev].run.xml │ ├── App [prod].run.xml │ └── App [stg].run.xml ├── LICENSE ├── Makefile ├── README.md ├── analysis_options.yaml ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ ├── google-services.json │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── dev │ │ │ ├── AndroidManifest.xml │ │ │ └── google-services.json │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── dev │ │ │ │ │ └── plugfox │ │ │ │ │ └── dartjobs │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable-hdpi │ │ │ │ └── splash.png │ │ │ │ ├── drawable-mdpi │ │ │ │ └── splash.png │ │ │ │ ├── drawable-v21 │ │ │ │ ├── background.png │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable-xhdpi │ │ │ │ └── splash.png │ │ │ │ ├── drawable-xxhdpi │ │ │ │ └── splash.png │ │ │ │ ├── drawable-xxxhdpi │ │ │ │ └── splash.png │ │ │ │ ├── drawable │ │ │ │ ├── background.png │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── values-night │ │ │ │ └── styles.xml │ │ │ │ ├── values-v31 │ │ │ │ └── styles.xml │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ ├── prod │ │ │ ├── AndroidManifest.xml │ │ │ └── google-services.json │ │ │ ├── profile │ │ │ └── AndroidManifest.xml │ │ │ └── stage │ │ │ ├── AndroidManifest.xml │ │ │ └── google-services.json │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── assets │ ├── icon │ │ ├── 0.5x │ │ │ └── icon@0.5x.png │ │ ├── 0.75x │ │ │ └── icon@0.75x.png │ │ ├── 1.5x │ │ │ └── icon@1.5x.png │ │ ├── 1x │ │ │ └── icon.png │ │ ├── 2x │ │ │ └── icon@2x.png │ │ ├── 3x │ │ │ └── icon@3x.png │ │ ├── 4x │ │ │ └── icon@4x.png │ │ └── SVG │ │ │ └── icon.svg │ ├── image │ │ └── dart_logo │ │ │ ├── SVG │ │ │ └── dart_logo.svg │ │ │ └── dart_logo_toolbar.png │ └── splash │ │ └── image.png ├── build.yaml ├── firebase.json ├── firestore.indexes.json ├── firestore.rules ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Podfile │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ ├── BrandingImage.imageset │ │ │ └── Contents.json │ │ ├── LaunchBackground.imageset │ │ │ ├── Contents.json │ │ │ └── background.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── GoogleService-Info.plist │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h ├── l10n.yaml ├── lib │ ├── main.dart │ ├── runner_io.dart │ ├── runner_stub.dart │ ├── runner_web.dart │ └── src │ │ ├── app.dart │ │ ├── common │ │ ├── bloc │ │ │ ├── app_bloc_observer.dart │ │ │ ├── bloc_set_state_mixin.dart │ │ │ └── platform │ │ │ │ ├── app_observer_io.dart │ │ │ │ └── app_observer_web.dart │ │ ├── constant │ │ │ ├── environment.dart │ │ │ ├── firebase_options.dart │ │ │ ├── layout_constraints.dart │ │ │ └── storage_namespace.dart │ │ ├── localization │ │ │ ├── intl_en.arb │ │ │ ├── intl_ru.arb │ │ │ └── localizations.dart │ │ ├── model │ │ │ ├── app_metadata.dart │ │ │ └── exceptions.dart │ │ ├── router │ │ │ ├── analytics_navigator_observer.dart │ │ │ ├── configuration.dart │ │ │ ├── information_parser.dart │ │ │ ├── information_provider.dart │ │ │ ├── navigator_observer.dart │ │ │ ├── no_animation_transition_delegate.dart │ │ │ ├── pages.dart │ │ │ ├── pages_builder.dart │ │ │ ├── router.dart │ │ │ ├── router_delegate.dart │ │ │ └── router_util.dart │ │ ├── utils │ │ │ ├── authentication_observer.dart │ │ │ ├── error_util.dart │ │ │ ├── firebase_initializer.dart │ │ │ ├── iterable_to_stream_coverter.dart │ │ │ ├── list_unique.dart │ │ │ ├── locale_util.dart │ │ │ ├── performance_link.dart │ │ │ ├── platform │ │ │ │ ├── authentication_observer_io.dart │ │ │ │ ├── authentication_observer_web.dart │ │ │ │ ├── error_util_io.dart │ │ │ │ └── error_util_web.dart │ │ │ ├── screen_util.dart │ │ │ └── sentry_wrapper.dart │ │ └── widget │ │ │ ├── adaptive_scaffold.dart │ │ │ ├── app_material_context.dart │ │ │ ├── custom_scroll_view_smooth.dart │ │ │ ├── dart_logo_icon.dart │ │ │ ├── drawer_scope.dart │ │ │ ├── error_snackbar.dart │ │ │ ├── snackbars_for_global_scope.dart │ │ │ ├── statuses_overlay.dart │ │ │ └── successful_snackbar.dart │ │ └── feature │ │ ├── authentication │ │ ├── bloc │ │ │ └── authentication_bloc.dart │ │ ├── data │ │ │ └── authentication_repository.dart │ │ ├── model │ │ │ └── user_entity.dart │ │ └── widget │ │ │ ├── authentication_scope.dart │ │ │ ├── profile_screen.dart │ │ │ ├── profile_widget.dart │ │ │ └── user_avatar.dart │ │ ├── browser_capabilities │ │ ├── models │ │ │ ├── browser_capabilities_data.dart │ │ │ ├── browser_capabilities_data_io.dart │ │ │ └── browser_capabilities_data_web.dart │ │ └── widgets │ │ │ └── browser_capabilities.dart │ │ ├── bug_report │ │ ├── bloc │ │ │ └── bug_report_bloc.dart │ │ ├── logic │ │ │ └── bug_report_repository.dart │ │ ├── model │ │ │ └── bug_report_entity.dart │ │ └── widget │ │ │ ├── bug_report_description_input.dart │ │ │ └── bug_report_page.dart │ │ ├── cloud_messaging │ │ ├── bloc │ │ │ └── cloud_messaging_bloc.dart │ │ ├── data │ │ │ └── cloud_messaging_service.dart │ │ ├── model │ │ │ └── notification_status.dart │ │ └── widget │ │ │ └── cloud_messaging_scope.dart │ │ ├── feed │ │ ├── bloc │ │ │ └── feed_bloc.dart │ │ └── widget │ │ │ ├── feed_bar.dart │ │ │ ├── feed_employment_filter.dart │ │ │ ├── feed_filter_bottom_sheet.dart │ │ │ ├── feed_level_filter.dart │ │ │ ├── feed_list.dart │ │ │ ├── feed_relocation_filter.dart │ │ │ ├── feed_remote_filter.dart │ │ │ ├── feed_scope.dart │ │ │ ├── feed_screen.dart │ │ │ ├── feed_search_bar.dart │ │ │ └── feed_tile.dart │ │ ├── initialization │ │ ├── bloc │ │ │ └── initialization_bloc.dart │ │ ├── data │ │ │ ├── app_migrator.dart │ │ │ ├── graphql_client_creator.dart │ │ │ └── initialization_helper.dart │ │ ├── model │ │ │ └── initialization_progress.dart │ │ └── widget │ │ │ └── repository_scope.dart │ │ ├── job │ │ ├── bloc │ │ │ └── job_bloc.dart │ │ ├── data │ │ │ ├── job_network_data_provider.dart │ │ │ └── job_repository.dart │ │ └── widget │ │ │ ├── edit │ │ │ ├── country_picker.dart │ │ │ ├── job_bottom_sheet.dart │ │ │ ├── job_description_input.dart │ │ │ ├── job_edit_buttons.dart │ │ │ ├── job_edit_flow.dart │ │ │ ├── job_edit_form.dart │ │ │ ├── job_employments_input.dart │ │ │ ├── job_levels_input.dart │ │ │ ├── job_relocation_input.dart │ │ │ ├── job_remote_input.dart │ │ │ └── job_single_line_input.dart │ │ │ ├── job_not_found.dart │ │ │ ├── job_page.dart │ │ │ └── view │ │ │ ├── job_view.dart │ │ │ └── share_button.dart │ │ ├── not_found │ │ └── widget │ │ │ └── not_found_screen.dart │ │ └── settings │ │ ├── bloc │ │ └── settings_bloc.dart │ │ ├── data │ │ └── settings_repository.dart │ │ ├── model │ │ └── user_settings.dart │ │ └── widget │ │ ├── flags │ │ ├── horizontal_lines_flag_painter.dart │ │ ├── russia_flag_painter.dart │ │ └── usa_flag_painter.dart │ │ ├── platform.dart │ │ ├── platform │ │ ├── platform_io.dart │ │ └── platform_web.dart │ │ ├── settings_list.dart │ │ ├── settings_scope.dart │ │ ├── settings_screen.dart │ │ └── settings_tile.dart ├── linux │ ├── .gitignore │ ├── CMakeLists.txt │ ├── flutter │ │ ├── CMakeLists.txt │ │ ├── generated_plugin_registrant.cc │ │ ├── generated_plugin_registrant.h │ │ └── generated_plugins.cmake │ ├── main.cc │ ├── my_application.cc │ └── my_application.h ├── macos │ ├── .gitignore │ ├── Flutter │ │ ├── Flutter-Debug.xcconfig │ │ ├── Flutter-Release.xcconfig │ │ └── GeneratedPluginRegistrant.swift │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ └── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── app_icon_1024.png │ │ │ ├── app_icon_128.png │ │ │ ├── app_icon_16.png │ │ │ ├── app_icon_256.png │ │ │ ├── app_icon_32.png │ │ │ ├── app_icon_512.png │ │ │ └── app_icon_64.png │ │ ├── Base.lproj │ │ └── MainMenu.xib │ │ ├── Configs │ │ ├── AppInfo.xcconfig │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── Warnings.xcconfig │ │ ├── DebugProfile.entitlements │ │ ├── Info.plist │ │ ├── MainFlutterWindow.swift │ │ └── Release.entitlements ├── pubspec.yaml ├── remoteconfig.template.json ├── test │ └── unit_test.dart ├── tools │ └── set_version.dart ├── web │ ├── .well-known │ │ └── assetlinks.json │ ├── android-chrome-144x144.png │ ├── android-chrome-192x192.png │ ├── android-chrome-256x256.png │ ├── android-chrome-36x36.png │ ├── android-chrome-384x384.png │ ├── android-chrome-48x48.png │ ├── android-chrome-512x512.png │ ├── android-chrome-72x72.png │ ├── android-chrome-96x96.png │ ├── apple-touch-icon-114x114-precomposed.png │ ├── apple-touch-icon-114x114.png │ ├── apple-touch-icon-120x120-precomposed.png │ ├── apple-touch-icon-120x120.png │ ├── apple-touch-icon-144x144-precomposed.png │ ├── apple-touch-icon-144x144.png │ ├── apple-touch-icon-152x152-precomposed.png │ ├── apple-touch-icon-152x152.png │ ├── apple-touch-icon-180x180-precomposed.png │ ├── apple-touch-icon-180x180.png │ ├── apple-touch-icon-57x57-precomposed.png │ ├── apple-touch-icon-57x57.png │ ├── apple-touch-icon-60x60-precomposed.png │ ├── apple-touch-icon-60x60.png │ ├── apple-touch-icon-72x72-precomposed.png │ ├── apple-touch-icon-72x72.png │ ├── apple-touch-icon-76x76-precomposed.png │ ├── apple-touch-icon-76x76.png │ ├── apple-touch-icon-precomposed.png │ ├── apple-touch-icon.png │ ├── browserconfig.xml │ ├── favicon-16x16.png │ ├── favicon-194x194.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── favicon.png │ ├── index.html │ ├── js │ │ └── firebase-messaging-sw.js │ ├── manifest.json │ ├── mstile-144x144.png │ ├── mstile-150x150.png │ ├── mstile-310x150.png │ ├── mstile-310x310.png │ ├── mstile-70x70.png │ ├── robots.txt │ ├── safari-pinned-tab.svg │ ├── site.webmanifest │ └── splash │ │ ├── img │ │ ├── dark-1x.png │ │ ├── dark-2x.png │ │ ├── dark-3x.png │ │ ├── dark-4x.png │ │ ├── light-1x.png │ │ ├── light-2x.png │ │ ├── light-3x.png │ │ └── light-4x.png │ │ └── style.css └── windows │ ├── .gitignore │ ├── CMakeLists.txt │ ├── flutter │ ├── CMakeLists.txt │ ├── generated_plugin_registrant.cc │ ├── generated_plugin_registrant.h │ └── generated_plugins.cmake │ └── runner │ ├── CMakeLists.txt │ ├── Runner.rc │ ├── flutter_window.cpp │ ├── flutter_window.h │ ├── main.cpp │ ├── resource.h │ ├── resources │ └── app_icon.ico │ ├── runner.exe.manifest │ ├── utils.cpp │ ├── utils.h │ ├── win32_window.cpp │ └── win32_window.h ├── dart-jobs.prod.compose.yml ├── dart-jobs.prod.stack.yml ├── dockerfiles ├── jwt_validator_firebase.dockerfile └── site.dockerfile ├── server └── jwt_validator_firebase │ ├── JwtValidatorFirebase.sln │ ├── JwtValidatorFirebase.sln.DotSettings │ └── JwtValidatorFirebase │ ├── .dockerignore │ ├── Controllers │ ├── HealthCheckController.cs │ └── JwtValidatorController.cs │ ├── Enums │ └── HasuraRoles.cs │ ├── Interfaces │ └── IJwtValidatorService.cs │ ├── JwtValidatorFirebase.csproj │ ├── JwtValidatorFirebase.csproj.user │ ├── Models │ └── ValidatorResponse.cs │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── Services │ └── JwtValidatorService.cs │ ├── Startup.cs │ ├── appsettings.Development.json │ └── appsettings.json └── shared ├── .gitignore ├── CHANGELOG.md ├── Makefile ├── README.md ├── analysis_options.yaml ├── build.yaml ├── lib ├── graphql.dart ├── model.dart ├── src │ ├── graphql │ │ ├── api.dart │ │ ├── api.graphql.dart │ │ ├── api.graphql.g.dart │ │ ├── client.dart │ │ ├── exceptions.dart │ │ ├── links │ │ │ ├── exception_link.dart │ │ │ ├── handler_link.dart │ │ │ ├── interceptor_link.dart │ │ │ └── metadata_link.dart │ │ └── parsers │ │ │ ├── bpchar.dart │ │ │ ├── employment.dart │ │ │ ├── int_array.dart │ │ │ ├── level.dart │ │ │ ├── relocation.dart │ │ │ ├── text.dart │ │ │ └── timestamp.dart │ ├── model │ │ ├── country.dart │ │ ├── enum.dart │ │ ├── enum.freezed.dart │ │ ├── enum.g.dart │ │ ├── job.dart │ │ ├── job.freezed.dart │ │ └── job.g.dart │ └── util │ │ ├── date_util.dart │ │ ├── graphql_array.dart │ │ ├── money_util.dart │ │ └── null_or.dart └── util.dart ├── pubspec.yaml ├── queries ├── create_job.graphql ├── delete_job.graphql ├── get_job.graphql ├── paginate.graphql ├── recent.graphql └── update_job.graphql └── schema.graphql /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG] Untitled Bug Issue" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Environment (please complete the following information):** 27 | - [ ] OS: [e.g. Android] 28 | - [ ] Browser [e.g. Chrome, Safari] 29 | - [ ] Version [e.g. 22] 30 | 31 | **Additional context** 32 | - Context [e.g. flutter doctor -v] 33 | Add any other context about the problem here. 34 | 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FR] Untitled Feature Request" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **1~3 main use cases of the proposed feature** 11 | 12 | **What types of users can benefit from using your proposed feature** 13 | 14 | **Additional context** 15 | 16 | 17 | -------------------------------------------------------------------------------- /.github/workflows/client_checkout.yml: -------------------------------------------------------------------------------- 1 | name: CLIENT / CHECKOUT 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'feature/**' 7 | - 'bugfix/**' 8 | - 'hotfix/**' 9 | - 'support/**' 10 | paths: 11 | - 'client/lib/**.dart' 12 | pull_request: 13 | branches: 14 | - 'feature/**' 15 | - 'bugfix/**' 16 | - 'hotfix/**' 17 | - 'support/**' 18 | paths: 19 | - 'client/lib/**.dart' 20 | workflow_dispatch: 21 | 22 | jobs: 23 | check-client: 24 | name: 'Checkout client' 25 | timeout-minutes: 5 26 | runs-on: ubuntu-latest 27 | container: 28 | image: plugfox/flutter:2.10.3 29 | options: --user root 30 | env: 31 | working-directory: ./client 32 | steps: 33 | - name: 🚂 Get latest code 34 | uses: actions/checkout@v2 35 | 36 | - name: 🚃 Cache pub modules 37 | uses: actions/cache@v2 38 | env: 39 | cache-name: cache-pub-modules 40 | with: 41 | path: | 42 | $PWD/.pub_cache/ 43 | key: ${{ runner.os }}-dart 44 | 45 | - name: 🗄️ Export pub cache directory 46 | run: export PUB_CACHE=$PWD/.pub_cache/ 47 | 48 | - name: 🚚 Get dependencies 49 | working-directory: ${{ env.working-directory }} 50 | run: | 51 | flutter pub get \ 52 | && flutter pub global activate intl_utils 53 | 54 | - name: 🏗️ Codegen 55 | working-directory: ${{ env.working-directory }} 56 | run: | 57 | flutter pub run build_runner build --delete-conflicting-outputs --release ; \ 58 | flutter pub global run intl_utils:generate 59 | 60 | - name: ✔️ Check sources with analyzer 61 | working-directory: ${{ env.working-directory }} 62 | run: | 63 | flutter analyze --no-pub --current-package --congratulate \ 64 | --current-package --no-fatal-infos --fatal-warnings \ 65 | --no-preamble --write=analyze.txt 66 | 67 | - name: 📁 Upload result 68 | uses: actions/upload-artifact@v2 69 | with: 70 | name: test 71 | if-no-files-found: ignore 72 | retention-days: 7 73 | path: | 74 | client/analyze.txt 75 | -------------------------------------------------------------------------------- /.github/workflows/server_checkout.yml: -------------------------------------------------------------------------------- 1 | name: SERVER / CHECKOUT 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'master' 7 | - 'develop' 8 | - 'feature/**' 9 | - 'release/**' 10 | - 'bugfix/**' 11 | - 'hotfix/**' 12 | - 'support/**' 13 | paths: 14 | - 'server/**.dart' 15 | pull_request: 16 | branches: 17 | - 'master' 18 | - 'develop' 19 | - 'feature/**' 20 | - 'release/**' 21 | - 'bugfix/**' 22 | - 'hotfix/**' 23 | - 'support/**' 24 | paths: 25 | - 'server/**.dart' 26 | workflow_dispatch: 27 | 28 | jobs: 29 | check-server: 30 | name: 'Checkout server' 31 | runs-on: ubuntu-latest 32 | container: 33 | image: google/dart:beta 34 | steps: 35 | - uses: actions/checkout@v2 36 | - name: 🔎 Checkout server source code 37 | run: | 38 | cd server 39 | echo Install Dependencies; \ 40 | dart pub get 41 | echo Check format; \ 42 | dart format --set-exit-if-changed -l 120 -o none . 43 | echo Check analyzer; \ 44 | dart analyze --fatal-infos --fatal-warnings lib -------------------------------------------------------------------------------- /.github/workflows/server_deploy_production.yml: -------------------------------------------------------------------------------- 1 | name: SERVER / DEPLOY 2 | 3 | on: 4 | workflow_dispatch: 5 | #inputs: 6 | # version: 7 | # description: 'Flutter version from https://github.com/flutter/flutter/tags' 8 | # required: true 9 | 10 | jobs: 11 | build-and-deploy: 12 | name: 'Build and deploy server' 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | 18 | - name: ✨ Log into registry 19 | run: echo "${{ secrets.DOCKER_REGISTRY_LOGIN_PASSWORD }}" | docker login -u ${{ secrets.DOCKER_REGISTRY_LOGIN_USERNAME }} --password-stdin https://registry.plugfox.dev 20 | 21 | - name: 📦 Pull dart:beta 22 | run: docker pull dart:stable 23 | 24 | - name: 🏗️ Build images 25 | run: make -C ./ -f ./Makefile build 26 | 27 | - name: 💾 Push images to registry 28 | run: make -C ./ -f ./Makefile push 29 | 30 | - name: 🔥 Deploy to swarm 31 | run: | 32 | INPUT_REMOTE_HOST="api.plugfox.dev" 33 | 34 | echo "Registering SSH keys..." 35 | mkdir -p "$HOME/.ssh" 36 | printf '%s' "${{ secrets.DOCKER_SWARM_SSH_PRIVATE_KEY }}" >> "${HOME}/.ssh/docker" 37 | chmod 600 "$HOME/.ssh/docker" && eval $(ssh-agent) && ssh-add "${HOME}/.ssh/docker" 38 | 39 | echo "Add public key to known hosts..." 40 | printf '%s %s\n' "$INPUT_REMOTE_HOST" "${{ secrets.DOCKER_SWARM_SSH_PUBLIC_KEY }}" >> "${HOME}/.ssh/known_hosts" 41 | 42 | echo "Deploying..." 43 | make -C ./ -f ./Makefile deploy 44 | 45 | rm -rf "$HOME/.ssh" 46 | 47 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean format get upgrade outdated push run deploy 2 | 3 | all: 4 | @echo "Мейкфайл управляющий монорепо. Можно собрать и перераскатить весь сервер." 5 | 6 | clean: 7 | @echo "Cleaning the project" 8 | dart clean --directory=server 9 | 10 | format: 11 | @echo "Formatting the code" 12 | @dart fix --apply server 13 | dart format -l 120 --fix server 14 | 15 | get: 16 | @echo "Geting dependencies" 17 | dart pub get --directory=server 18 | 19 | upgrade: get 20 | @echo "Upgrading dependencies" 21 | dart pub upgrade --directory=server 22 | 23 | build-server: 24 | @echo "Build release docker images" 25 | docker-compose -f ./dart-jobs.prod.compose.yml build --no-cache --force-rm --compress --parallel 26 | 27 | push-server: 28 | @echo "Push release docker images" 29 | docker-compose -f ./dart-jobs.prod.compose.yml push 30 | 31 | start-server: 32 | @echo "Run local server" 33 | docker-compose -f ./dart-jobs.prod.compose.yml up --detach 34 | 35 | stop-server: 36 | @echo "Stop local server" 37 | docker-compose -f ./dart-jobs.prod.compose.yml down 38 | 39 | redeploy-server: 40 | @echo "Deploy release into docker swarm" 41 | docker --log-level debug --host "ssh://pfx@api.plugfox.dev" stack deploy --compose-file ./dart-jobs.prod.stack.yml --orchestrator swarm --prune --with-registry-auth dart-jobs-prod 42 | 43 | build-and-push-server: build-server push-server 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DART JOBS 2 | [![CLIENT / DEPLOY](https://github.com/PlugFox/dart-jobs/actions/workflows/client_deploy_production.yml/badge.svg)](https://github.com/PlugFox/dart-jobs/actions/workflows/client_deploy_production.yml) 3 | [![Effective Dart](https://img.shields.io/badge/Style-effective_dart-40c4ff.svg)](https://github.com/tenhobi/effective_dart) 4 | [![License: Proprietary](https://img.shields.io/badge/License-proprietary-red.svg)](https://en.wikipedia.org/wiki/Proprietary_software) 5 | [![Localizely](https://img.shields.io/badge/Localizely-projects-ab47bc.svg)](https://app.localizely.com/projects/) 6 | [![Firebase](https://img.shields.io/badge/Firebase-overview-blue.svg)](https://console.firebase.google.com/u/0/project/dart-job/overview) 7 | [![PWA](https://img.shields.io/badge/Application-Progressive_Web_App-brightgreen.svg)](https://dart-jobs.plugfox.dev) 8 | --- 9 | 10 | -------------------------------------------------------------------------------- /client/.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "dart-job" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | .vscode/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .packages 31 | .pub-cache/ 32 | .pub/ 33 | /build/ 34 | pubspec.lock 35 | 36 | # Web related 37 | lib/generated_plugin_registrant.dart 38 | 39 | # Symbolication related 40 | app.*.symbols 41 | 42 | # Obfuscation related 43 | app.*.map.json 44 | 45 | # Codegen 46 | *.g.dart 47 | *.g.java 48 | *.g.kt 49 | *.freezed.dart 50 | *.gen.dart 51 | *.moor.dart 52 | *.mocks.dart 53 | *.hive.dart 54 | /lib/src/common/localization/l10n.dart 55 | /lib/src/common/localization/intl/messages_??.dart 56 | /lib/src/common/localization/intl/messages_???.dart 57 | 58 | # Database 59 | *.db 60 | *.sqlite 61 | *.hive 62 | *.lock 63 | 64 | # Logs 65 | /logs/ 66 | /coverage/ 67 | 68 | # Firebase 69 | #.firebase/ 70 | 71 | android/keystore.properties 72 | 73 | # SKSL shaders warm-up 74 | *.sksl.json 75 | 76 | version.env -------------------------------------------------------------------------------- /client/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 19c61fed0da681ba2068c97e22add3bde38e51e4 8 | channel: beta 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /client/.run/App [dev].run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /client/.run/App [prod].run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /client/.run/App [stg].run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /client/LICENSE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/LICENSE -------------------------------------------------------------------------------- /client/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean format get upgrade upgrade-major outdated codegen init build build-site 2 | 3 | init: 4 | @npm install -g firebase-tools 5 | @firebase init 6 | -firebase login 7 | @flutter pub global activate intl_utils 8 | @dart pub global activate flutterfire_cli 9 | @flutterfire configure \ 10 | -i dev.plugfox.dartjobs \ 11 | -m dev.plugfox.dartjobs \ 12 | -a dev.plugfox.dartjobs \ 13 | -p dart-job \ 14 | -e plugfox@gmail.com \ 15 | -o lib/src/common/constant/firebase_options.g.dart 16 | 17 | clean: 18 | @echo "Cleaning the project" 19 | @flutter clean 20 | 21 | format: 22 | @echo "Formatting the code" 23 | @dart fix --apply . 24 | @dart format -l 120 --fix . 25 | 26 | get: 27 | @echo "Getting dependencies" 28 | @flutter pub get 29 | 30 | upgrade: get 31 | @echo "Upgrading dependencies" 32 | @flutter pub upgrade 33 | 34 | upgrade-major: get 35 | @echo "Upgrading dependencies --major-versions" 36 | @flutter pub upgrade --major-versions 37 | 38 | outdated: 39 | @flutter pub outdated 40 | 41 | codegen: get 42 | @echo "Running codegeneration" 43 | -dart run tools/set_version.dart 44 | @flutter pub run build_runner build --delete-conflicting-outputs --release 45 | @flutter pub global run intl_utils:generate 46 | 47 | build: codegen build-site 48 | 49 | build-site: 50 | @flutter build web --release --no-source-maps --pwa-strategy offline-first --web-renderer auto --base-href / 51 | 52 | deploy: 53 | @firebase deploy 54 | 55 | serve: 56 | @firebase serve --only hosting -p 80 -------------------------------------------------------------------------------- /client/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /client/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /client/android/app/src/dev/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /client/android/app/src/main/kotlin/dev/plugfox/dartjobs/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package dev.plugfox.dartjobs 2 | 3 | import android.os.Bundle 4 | import android.os.PersistableBundle 5 | import io.flutter.embedding.android.FlutterActivity 6 | 7 | class MainActivity: FlutterActivity() { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /client/android/app/src/main/res/drawable-hdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/android/app/src/main/res/drawable-hdpi/splash.png -------------------------------------------------------------------------------- /client/android/app/src/main/res/drawable-mdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/android/app/src/main/res/drawable-mdpi/splash.png -------------------------------------------------------------------------------- /client/android/app/src/main/res/drawable-v21/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/android/app/src/main/res/drawable-v21/background.png -------------------------------------------------------------------------------- /client/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /client/android/app/src/main/res/drawable-xhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/android/app/src/main/res/drawable-xhdpi/splash.png -------------------------------------------------------------------------------- /client/android/app/src/main/res/drawable-xxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/android/app/src/main/res/drawable-xxhdpi/splash.png -------------------------------------------------------------------------------- /client/android/app/src/main/res/drawable-xxxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/android/app/src/main/res/drawable-xxxhdpi/splash.png -------------------------------------------------------------------------------- /client/android/app/src/main/res/drawable/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/android/app/src/main/res/drawable/background.png -------------------------------------------------------------------------------- /client/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /client/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /client/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /client/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /client/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /client/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /client/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /client/android/app/src/main/res/values-v31/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 14 | 17 | -------------------------------------------------------------------------------- /client/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 16 | 19 | -------------------------------------------------------------------------------- /client/android/app/src/prod/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /client/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /client/android/app/src/stage/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /client/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.6.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.google.gms:google-services:4.3.10' 10 | classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1' 11 | classpath 'com.android.tools.build:gradle:4.1.0' 12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | google() 19 | mavenCentral() 20 | } 21 | } 22 | 23 | rootProject.buildDir = '../build' 24 | subprojects { 25 | project.buildDir = "${rootProject.buildDir}/${project.name}" 26 | } 27 | subprojects { 28 | project.evaluationDependsOn(':app') 29 | } 30 | 31 | task clean(type: Delete) { 32 | delete rootProject.buildDir 33 | } -------------------------------------------------------------------------------- /client/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /client/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip 7 | -------------------------------------------------------------------------------- /client/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | 13 | // Fixed https://github.com/flutter/flutter/issues/97729 by adding the lines below 14 | 15 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 16 | 17 | def plugins = new Properties() 18 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 19 | if (pluginsFile.exists()) { 20 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 21 | } 22 | 23 | plugins.each { name, path -> 24 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 25 | include ":$name" 26 | project(":$name").projectDir = pluginDirectory 27 | } -------------------------------------------------------------------------------- /client/assets/icon/0.5x/icon@0.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/assets/icon/0.5x/icon@0.5x.png -------------------------------------------------------------------------------- /client/assets/icon/0.75x/icon@0.75x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/assets/icon/0.75x/icon@0.75x.png -------------------------------------------------------------------------------- /client/assets/icon/1.5x/icon@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/assets/icon/1.5x/icon@1.5x.png -------------------------------------------------------------------------------- /client/assets/icon/1x/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/assets/icon/1x/icon.png -------------------------------------------------------------------------------- /client/assets/icon/2x/icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/assets/icon/2x/icon@2x.png -------------------------------------------------------------------------------- /client/assets/icon/3x/icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/assets/icon/3x/icon@3x.png -------------------------------------------------------------------------------- /client/assets/icon/4x/icon@4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/assets/icon/4x/icon@4x.png -------------------------------------------------------------------------------- /client/assets/icon/SVG/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/assets/image/dart_logo/SVG/dart_logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/assets/image/dart_logo/dart_logo_toolbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/assets/image/dart_logo/dart_logo_toolbar.png -------------------------------------------------------------------------------- /client/assets/splash/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/assets/splash/image.png -------------------------------------------------------------------------------- /client/build.yaml: -------------------------------------------------------------------------------- 1 | targets: 2 | $default: 3 | sources: 4 | - $package$ 5 | - lib/** 6 | - pubspec.yaml 7 | - test/** 8 | builders: 9 | json_serializable: 10 | enabled: true 11 | generate_for: 12 | include: 13 | - lib/** 14 | - test/** 15 | options: 16 | any_map: false 17 | checked: false 18 | create_factory: true 19 | create_to_json: true 20 | disallow_unrecognized_keys: false 21 | explicit_to_json: true 22 | field_rename: none 23 | ignore_unannotated: false 24 | include_if_null: true 25 | #nullable: true 26 | freezed: 27 | enabled: true 28 | options: 29 | union_key: type 30 | union_value_case: snake 31 | generate_for: 32 | include: 33 | - lib/** 34 | - test/** 35 | #built_value_generator|built_value: 36 | # enabled: true 37 | # generate_for: 38 | # exclude: 39 | # - test 40 | # include: 41 | # - lib/** 42 | build_web_compilers:entrypoint: 43 | # These are globs for the entrypoints you want to compile. 44 | generate_for: 45 | - test/**.browser_test.dart 46 | - web/**.dart 47 | enabled: true 48 | options: 49 | sound_null_safety: true 50 | compiler: dart2js 51 | # List any dart2js specific args here, or omit it. 52 | dart2js_args: 53 | - -DIS_WEB=true 54 | - -O2 55 | pubspec_generator: 56 | options: 57 | output: lib/src/common/constant/pubspec.yaml.g.dart 58 | -------------------------------------------------------------------------------- /client/firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "firestore": { 3 | "rules": "firestore.rules", 4 | "indexes": "firestore.indexes.json" 5 | }, 6 | "hosting": { 7 | "public": "build/web", 8 | "ignore": [ 9 | "firebase.json", 10 | "**/node_modules/**" 11 | ], 12 | "appAssociation": "AUTO", 13 | "rewrites": [ 14 | { 15 | "source": "!/.well-known/assetlinks.json", 16 | "destination": "/index.html" 17 | }, 18 | { 19 | "source": "/link/**", 20 | "dynamicLinks": true 21 | }, 22 | { 23 | "source": "**", 24 | "destination": "/index.html" 25 | } 26 | ] 27 | }, 28 | "emulators": { 29 | "auth": { 30 | "port": 9099 31 | }, 32 | "firestore": { 33 | "port": 8080 34 | }, 35 | "hosting": { 36 | "port": 5000 37 | }, 38 | "ui": { 39 | "enabled": true 40 | } 41 | }, 42 | "remoteconfig": { 43 | "template": "remoteconfig.template.json" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /client/firestore.indexes.json: -------------------------------------------------------------------------------- 1 | { 2 | "indexes": [], 3 | "fieldOverrides": [ 4 | { 5 | "collectionGroup": "feed", 6 | "fieldPath": "updated", 7 | "indexes": [ 8 | { 9 | "order": "ASCENDING", 10 | "queryScope": "COLLECTION" 11 | }, 12 | { 13 | "order": "DESCENDING", 14 | "queryScope": "COLLECTION" 15 | }, 16 | { 17 | "arrayConfig": "CONTAINS", 18 | "queryScope": "COLLECTION" 19 | } 20 | ] 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /client/ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/ephemeral/ 22 | Flutter/app.flx 23 | Flutter/app.zip 24 | Flutter/flutter_assets/ 25 | Flutter/flutter_export_environment.sh 26 | ServiceDefinitions.json 27 | Runner/GeneratedPluginRegistrant.* 28 | 29 | # Exceptions to above rules. 30 | !default.mode1v3 31 | !default.mode2v3 32 | !default.pbxuser 33 | !default.perspectivev3 34 | -------------------------------------------------------------------------------- /client/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 9.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /client/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /client/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /client/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /client/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /client/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /client/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /client/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /client/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /client/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /client/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | var window: UIWindow? 7 | 8 | override func application( 9 | _ application: UIApplication, 10 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 11 | ) -> Bool { 12 | FirebaseApp.configure() 13 | GeneratedPluginRegistrant.register(with: self) 14 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /client/ios/Runner/Assets.xcassets/BrandingImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "BrandingImage.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "BrandingImage@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "BrandingImage@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /client/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "background.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /client/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png -------------------------------------------------------------------------------- /client/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "LaunchImage.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "LaunchImage@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "LaunchImage@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /client/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /client/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /client/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /client/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | 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. -------------------------------------------------------------------------------- /client/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /client/ios/Runner/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CLIENT_ID 6 | 174403318860-gaiim23hsprlr34j16amt3evcmg1ov91.apps.googleusercontent.com 7 | REVERSED_CLIENT_ID 8 | com.googleusercontent.apps.174403318860-gaiim23hsprlr34j16amt3evcmg1ov91 9 | ANDROID_CLIENT_ID 10 | 174403318860-8febsjk05jafs4u5ru5te1ilu7aiiv1b.apps.googleusercontent.com 11 | API_KEY 12 | AIzaSyB2U4Hy4d9x7AVo-k1LtMpH01-1ADYKeig 13 | GCM_SENDER_ID 14 | 174403318860 15 | PLIST_VERSION 16 | 1 17 | BUNDLE_ID 18 | dev.plugfox.dartjobs 19 | PROJECT_ID 20 | dart-job 21 | STORAGE_BUCKET 22 | dart-job.appspot.com 23 | IS_ADS_ENABLED 24 | 25 | IS_ANALYTICS_ENABLED 26 | 27 | IS_APPINVITE_ENABLED 28 | 29 | IS_GCM_ENABLED 30 | 31 | IS_SIGNIN_ENABLED 32 | 33 | GOOGLE_APP_ID 34 | 1:174403318860:ios:98002ef72b0503fc8230e9 35 | 36 | -------------------------------------------------------------------------------- /client/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | Dart Jobs 15 | CFBundleDisplayName 16 | Dart Jobs 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | UIStatusBarHidden 47 | 48 | 49 | -------------------------------------------------------------------------------- /client/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /client/l10n.yaml: -------------------------------------------------------------------------------- 1 | arb-dir: lib/src/common/localization 2 | template-arb-file: intl_en.arb 3 | output-localization-file: l10n.dart -------------------------------------------------------------------------------- /client/lib/runner_stub.dart: -------------------------------------------------------------------------------- 1 | /// Запуск для неизвестной платформы 2 | Future run() => throw UnsupportedError('Unknown host platform'); 3 | -------------------------------------------------------------------------------- /client/lib/runner_web.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: unnecessary_lambdas 2 | import 'dart:html' as html; 3 | 4 | import 'package:dart_jobs_client/src/app.dart'; 5 | import 'package:flutter_web_plugins/flutter_web_plugins.dart'; 6 | 7 | /// Запуск для веба 8 | Future run() { 9 | setUrlStrategy(const HashUrlStrategy()); 10 | 11 | // Инициализировать и запустить приложение 12 | return App.initializeAndRun( 13 | onSuccessfulInitialization: (_) { 14 | html.document.getElementsByClassName('loading').toList(growable: false).forEach((element) => element.remove()); 15 | }, 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /client/lib/src/common/bloc/app_bloc_observer.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc_concurrency/bloc_concurrency.dart' as bloc_concurrency; 2 | import 'package:dart_jobs_client/src/common/bloc/platform/app_observer_io.dart' 3 | if (dart.library.html) 'package:dart_jobs_client/src/common/bloc/platform/app_observer_web.dart' as platform; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | import 'package:meta/meta.dart'; 6 | 7 | @sealed 8 | abstract class AppBlocObserver { 9 | static BlocObserver get instance => _singleton ??= platform.createBlocObserver(); 10 | static BlocObserver? _singleton; 11 | AppBlocObserver._(); 12 | 13 | static Future runZoned(Future Function() body) => BlocOverrides.runZoned>( 14 | body, 15 | blocObserver: instance, 16 | eventTransformer: bloc_concurrency.sequential(), 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /client/lib/src/common/bloc/bloc_set_state_mixin.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart' show Emittable; 2 | 3 | mixin SetStateMixin implements Emittable { 4 | void setState(State state) => emit(state); 5 | } 6 | -------------------------------------------------------------------------------- /client/lib/src/common/constant/environment.dart: -------------------------------------------------------------------------------- 1 | import 'package:platform_info/platform_info.dart'; 2 | 3 | /// Окружение, например: 4 | /// + development 5 | /// + integration 6 | /// + testing 7 | /// + staging 8 | /// + production 9 | String get environment => _kEnvironment.isEmpty 10 | ? platform.buildMode.when( 11 | debug: () => 'development', 12 | profile: () => 'development', 13 | release: () => 'production', 14 | ) 15 | : _kEnvironment; 16 | const String _kEnvironment = String.fromEnvironment( 17 | 'environment', 18 | defaultValue: '', 19 | ); 20 | 21 | /// Использовать fake значения 22 | /// --dart-define=fake=true 23 | const bool kFake = bool.fromEnvironment( 24 | 'fake', 25 | defaultValue: false, 26 | ); 27 | 28 | /// Эндпоинт графкл 29 | const String kGraphQLEndpoint = String.fromEnvironment( 30 | 'graphql', 31 | defaultValue: 'https://job.api.plugfox.dev/v1/graphql', 32 | ); 33 | 34 | /// Расширенная аналитика, если это не продуктовый релиз 35 | bool get expandedAnalytics => environment != 'production'; 36 | 37 | /// Топик FCM для подписки на создание работы 38 | const String kFCMNotificationTopicJobCreated = 'job_created'; 39 | -------------------------------------------------------------------------------- /client/lib/src/common/constant/layout_constraints.dart: -------------------------------------------------------------------------------- 1 | /// Максимальная ширина колонки с основным контентом 2 | const double kBodyWidth = 620; // or 540 3 | 4 | // /// Ширина правой колонки с дополнительным контентом 5 | //const double sidePaneWidth = 320; // or 256 or 316; 6 | 7 | /// Отступ боковых панелей от краев экрана, между собой и от рельсы (Drawer) 8 | const double kSidePadding = 24; 9 | 10 | /// Ширина выдвинутого Drawer'а в роли рельсы 11 | /// На планшетах, в случае выдвижения, может быть больше и достигать 400 dip 12 | const double kRailWidth = 320; 13 | 14 | /// Максимально возможная ширина лейаута с правой панелью, но без учета Drawer'а 15 | const double kScaffoldWidth = kSidePadding + kBodyWidth + kSidePadding; // + sidePaneWidth + sidePadding; 16 | 17 | /// Минимальная ширина экрана для отображения выдвинутого Drawer'а в роли рельсы 18 | const double kScaffoldWithRail = kRailWidth + kScaffoldWidth; 19 | -------------------------------------------------------------------------------- /client/lib/src/common/constant/storage_namespace.dart: -------------------------------------------------------------------------------- 1 | /// Неймспейс для всех ключей в SharedPreference 2 | const String kStorageNamespace = 'dev.plugfox.dartjobs.flutter'; 3 | 4 | /// Ключи по которым сохраняем текущую версию приложения 5 | const String kVersionMajorKey = '$kStorageNamespace.version_major'; 6 | const String kVersionMinorKey = '$kStorageNamespace.version_minor'; 7 | const String kVersionPatchKey = '$kStorageNamespace.version_patch'; 8 | 9 | /// Ключ по которому хранится информация, запрашивалось ли уже разрешение на пуши 10 | const String kCloudMessagingPushRequested = '$kStorageNamespace.fcm_push_requested'; 11 | -------------------------------------------------------------------------------- /client/lib/src/common/model/exceptions.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | /// Исключение содержащее исходный StackTrace 4 | @immutable 5 | abstract class Throwable implements Exception { 6 | Throwable([StackTrace? stackTrace]) : stackTrace = stackTrace ?? StackTrace.current; 7 | 8 | /// Исходный стектрейс 9 | final StackTrace stackTrace; 10 | } 11 | 12 | /// Не важные, предсказуемые исключения 13 | /// Например "такого элемента не существует" или "отсутсвует интернет" 14 | abstract class MinorException implements Throwable {} 15 | 16 | /// Исключение приложения 17 | @immutable 18 | abstract class AppException implements Throwable { 19 | const AppException( 20 | this.stackTrace, [ 21 | final this.message, 22 | ]); 23 | 24 | /// Исходный стектрейс 25 | @override 26 | final StackTrace stackTrace; 27 | 28 | /// Сообщение об ошибки или исходная ошибка строкой 29 | final String? message; 30 | 31 | @override 32 | String toString() { 33 | if (message == null) return 'AppException'; 34 | return 'AppException: $message'; 35 | } 36 | } 37 | 38 | /// Не найдено 39 | class NotFoundException extends AppException implements MinorException { 40 | const NotFoundException( 41 | StackTrace stackTrace, [ 42 | final String? message, 43 | ]) : super( 44 | stackTrace, 45 | message, 46 | ); 47 | 48 | @override 49 | String toString() { 50 | if (message == null) return 'NotFoundException'; 51 | return 'NotFoundException: $message'; 52 | } 53 | } 54 | 55 | /// Не авторизован 56 | class NotAuthorized extends AppException implements MinorException { 57 | const NotAuthorized( 58 | StackTrace stackTrace, [ 59 | final String? message, 60 | ]) : super( 61 | stackTrace, 62 | message, 63 | ); 64 | 65 | @override 66 | String toString() { 67 | if (message == null) return 'NotAuthorized'; 68 | return 'NotAuthorized: $message'; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /client/lib/src/common/router/information_parser.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_jobs_client/src/common/router/configuration.dart'; 2 | import 'package:dart_jobs_client/src/common/router/router_util.dart'; 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/widgets.dart'; 5 | import 'package:l/l.dart'; 6 | 7 | class AppRouteInformationParser = RouteInformationParser 8 | with _RestoreRouteInformationMixin, _ParseRouteInformationMixin; 9 | 10 | mixin _RestoreRouteInformationMixin on RouteInformationParser { 11 | @override 12 | RouteInformation? restoreRouteInformation(IRouteConfiguration configuration) { 13 | try { 14 | //if (configuration == _currentConfiguration) { 15 | // // Конфигурация не изменилась, не сообщаем платформе об изменениях 16 | // return null; 17 | //} 18 | final location = RouteInformationUtil.normalize(configuration.location); 19 | final state = configuration.state; 20 | final route = RouteInformation( 21 | location: location, 22 | state: state, 23 | ); 24 | //_currentConfiguration = configuration; 25 | return route; 26 | } on Object catch (error) { 27 | l.w('Ошибка навигации restoreRouteInformation: $error'); 28 | return const RouteInformation(location: 'feed/404'); 29 | } 30 | } 31 | } 32 | 33 | mixin _ParseRouteInformationMixin on RouteInformationParser { 34 | @override 35 | Future parseRouteInformation(RouteInformation routeInformation) { 36 | try { 37 | if (routeInformation is IRouteConfiguration) return SynchronousFuture(routeInformation); 38 | final location = RouteInformationUtil.normalize(routeInformation.location); 39 | var state = routeInformation.state; 40 | if (state is! Map?>?) { 41 | state = null; 42 | } 43 | final configuration = DynamicRouteConfiguration(location, state); 44 | return SynchronousFuture(configuration); 45 | } on Object catch (error) { 46 | l.w('Ошибка навигации parseRouteInformation: $error'); 47 | return SynchronousFuture(const NotFoundRouteConfiguration()); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /client/lib/src/common/router/information_provider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:dart_jobs_client/src/common/router/router_util.dart'; 4 | import 'package:flutter/widgets.dart'; 5 | 6 | class AppInformationProvider extends PlatformRouteInformationProvider { 7 | AppInformationProvider({ 8 | final RouteInformation? initialRouteInformation, 9 | }) : super(initialRouteInformation: initialRouteInformation ?? _getDefaultRoute()); 10 | 11 | static RouteInformation _getDefaultRoute() { 12 | final route = RouteInformation( 13 | location: RouteInformationUtil.normalize(PlatformDispatcher.instance.defaultRouteName), 14 | ); 15 | return route; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /client/lib/src/common/router/navigator_observer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | class PageObserver extends RouteObserver> implements NavigatorObserver {} 4 | 5 | class ModalObserver extends RouteObserver> implements NavigatorObserver {} 6 | -------------------------------------------------------------------------------- /client/lib/src/common/router/no_animation_transition_delegate.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | class NoAnimationTransitionDelegate extends TransitionDelegate { 4 | const NoAnimationTransitionDelegate(); 5 | 6 | @override 7 | Iterable resolve({ 8 | required List newPageRouteHistory, 9 | required Map locationToExitingPageRoute, 10 | required Map> pageRouteToPagelessRoutes, 11 | }) { 12 | final results = []; 13 | for (final pageRoute in newPageRouteHistory) { 14 | if (pageRoute.isWaitingForEnteringDecision) { 15 | pageRoute.markForAdd(); 16 | } 17 | results.add(pageRoute); 18 | } 19 | for (final exitingPageRoute in locationToExitingPageRoute.values) { 20 | if (exitingPageRoute.isWaitingForExitingDecision) { 21 | exitingPageRoute.markForRemove(); 22 | final pagelessRoutes = pageRouteToPagelessRoutes[exitingPageRoute]; 23 | if (pagelessRoutes != null) { 24 | for (final pagelessRoute in pagelessRoutes) { 25 | pagelessRoute.markForRemove(); 26 | } 27 | } 28 | } 29 | results.add(exitingPageRoute); 30 | } 31 | return results; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /client/lib/src/common/router/router_util.dart: -------------------------------------------------------------------------------- 1 | import 'package:path/path.dart' as p; 2 | 3 | abstract class RouteInformationUtil { 4 | RouteInformationUtil._(); 5 | 6 | /// Выбросить из локации повторяющиеся сегменты 7 | /// Роут всегда должен начинаться с home 8 | static String normalize(String? sourceLocation) { 9 | if (sourceLocation == null) return 'feed'; 10 | final segments = []; 11 | sourceLocation 12 | .toLowerCase() 13 | .split('/') 14 | .map((e) => e.trim()) 15 | .where((e) => e.isNotEmpty && e != 'feed') 16 | .forEach( 17 | (e) => segments 18 | ..remove(e) 19 | ..add(e), 20 | ); 21 | return p.normalize( 22 | p.joinAll( 23 | [ 24 | 'feed', 25 | ...segments, 26 | ], 27 | ), 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /client/lib/src/common/utils/authentication_observer.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:dart_jobs_client/src/common/utils/platform/authentication_observer_io.dart' 4 | // ignore: uri_does_not_exist 5 | if (dart.library.html) 'package:dart_jobs_client/src/common/utils/platform/authentication_observer_web.dart' 6 | as platform_observe_callback; 7 | import 'package:firebase_auth/firebase_auth.dart'; 8 | import 'package:l/l.dart'; 9 | 10 | extension AuthenticationObserverX on FirebaseAuth { 11 | static StreamSubscription? _subscription; 12 | 13 | StreamSubscription observe() { 14 | if (_subscription != null) { 15 | _subscription?.cancel(); 16 | _subscription = null; 17 | } 18 | 19 | void onError(Object error, StackTrace stackTrace) { 20 | l.w( 21 | 'Произошла ошибка в функции наблюдателя аутентификации: $error', 22 | stackTrace, 23 | ); 24 | } 25 | 26 | try { 27 | platform_observe_callback.onAuthStateChanges(FirebaseAuth.instance.currentUser); 28 | } on Object catch (error, stackTrace) { 29 | onError(error, stackTrace); 30 | } 31 | 32 | return _subscription = FirebaseAuth.instance.userChanges().listen( 33 | platform_observe_callback.onAuthStateChanges, 34 | cancelOnError: false, 35 | onDone: () => _subscription = null, 36 | onError: onError, 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /client/lib/src/common/utils/firebase_initializer.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_jobs_client/src/common/constant/firebase_options.dart' as firebase_options; 2 | import 'package:dart_jobs_client/src/common/utils/authentication_observer.dart'; 3 | import 'package:firebase_auth/firebase_auth.dart' as firebase_auth; 4 | import 'package:firebase_core/firebase_core.dart' as firebase_core; 5 | import 'package:flutter/foundation.dart'; 6 | import 'package:l/l.dart'; 7 | 8 | // ignore: avoid_classes_with_only_static_members 9 | abstract class FirebaseInitializer { 10 | /// Инициализация фаербейза 11 | static Future initialize() async { 12 | // Инициализировать Firebase 13 | l.vvvvvv('Инициализируем Firebase'); 14 | await firebase_core.Firebase.initializeApp( 15 | options: firebase_options.DefaultFirebaseOptions.currentPlatform, 16 | ).then( 17 | (final firebaseApp) => Future.wait( 18 | >[ 19 | firebaseApp.setAutomaticDataCollectionEnabled(kReleaseMode), 20 | firebaseApp.setAutomaticResourceManagementEnabled(kReleaseMode), 21 | ], 22 | ), 23 | ); 24 | // Устанавливаем идентификаторы и свойства пользователя 25 | firebase_auth.FirebaseAuth.instance.observe(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /client/lib/src/common/utils/iterable_to_stream_coverter.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | class IterableToStreamConverter { 4 | const IterableToStreamConverter(); 5 | 6 | Stream convert(final Iterable iterable) async* { 7 | final stopwatch = Stopwatch()..start(); 8 | final iterator = iterable.iterator; 9 | while (iterator.moveNext()) { 10 | if (stopwatch.elapsedMilliseconds > 8) { 11 | await Future.delayed(Duration.zero); 12 | stopwatch.reset(); 13 | } 14 | yield iterator.current; 15 | } 16 | stopwatch.stop(); 17 | } 18 | } 19 | 20 | extension IterableToStreamConverterX on Iterable { 21 | Stream convert() => const IterableToStreamConverter().convert(this); 22 | } 23 | -------------------------------------------------------------------------------- /client/lib/src/common/utils/list_unique.dart: -------------------------------------------------------------------------------- 1 | extension Unique on List { 2 | List unique(final Id Function(E element) id) { 3 | final ids = {}; 4 | return this..retainWhere((final x) => ids.add(id(x))); 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /client/lib/src/common/utils/locale_util.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_jobs_client/src/common/localization/localizations.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:intl/intl.dart'; 4 | import 'package:meta/meta.dart'; 5 | 6 | /// Namespace 7 | @sealed 8 | abstract class LocaleUtil { 9 | LocaleUtil._(); 10 | 11 | /// Красивое представление даты в зависимости от текущей локали 12 | static String dateToString(BuildContext context, DateTime dateTime) { 13 | final languageCode = context.localization.language_code; 14 | final dt = dateTime.toLocal(); 15 | final now = DateTime.now(); 16 | if (dt.day == now.day) { 17 | // (_dateFormat['${languageCode}_hms'] ??= DateFormat.Hms()).format(dt) 18 | return TimeOfDay.fromDateTime(dt).format(context); 19 | } 20 | return (_dateFormat['${languageCode}_yMMMMd'] ??= DateFormat.yMMMMd()).format(dt); 21 | } 22 | 23 | static final Map _dateFormat = {}; 24 | 25 | /// Форматирование даты в зависимости от текущей локали и паттерна 26 | static String formatDate(final BuildContext context, final DateTime date, final String pattern) => 27 | AppLocalization.dateFormat(pattern, AppLocalization.localeOf(context)).format(date.toLocal()); 28 | 29 | /// Получить локализованные строки в зависимости от текущего языка 30 | static Localized localizationOf(BuildContext context) => AppLocalization.localize(context); 31 | 32 | /// Текущая локаль 33 | static Locale localeOf(BuildContext context) => AppLocalization.localeOf(context); 34 | 35 | /// Поддерживаемые локали 36 | static List get supportedLocales => AppLocalization.supportedLocales; 37 | 38 | /// Языковой код локали 39 | static String localeToString(final Locale locale) => locale.languageCode; 40 | 41 | /// Локаль из языкового кода 42 | static Locale stringToLocale(final String languageCode) => Locale(languageCode); 43 | } 44 | -------------------------------------------------------------------------------- /client/lib/src/common/utils/platform/authentication_observer_io.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_analytics/firebase_analytics.dart'; 2 | import 'package:firebase_auth/firebase_auth.dart'; 3 | import 'package:firebase_crashlytics/firebase_crashlytics.dart'; 4 | import 'package:sentry/sentry_io.dart'; 5 | 6 | void onAuthStateChanges(User? user) { 7 | FirebaseAnalytics.instance.setUserId(id: user?.uid); 8 | FirebaseAnalytics.instance.setUserProperty(name: 'email', value: user?.email); 9 | FirebaseAnalytics.instance.setUserProperty(name: 'name', value: user?.displayName); 10 | if (user?.uid.isNotEmpty ?? false) { 11 | FirebaseCrashlytics.instance.setUserIdentifier(user?.uid ?? ''); 12 | Sentry.configureScope((scope) => scope..setTag('uid', user?.uid ?? '')); 13 | } 14 | if (user?.email?.isNotEmpty ?? false) { 15 | FirebaseCrashlytics.instance.setCustomKey('email', user?.email ?? ''); 16 | Sentry.configureScope((scope) => scope.setTag('email', user?.email ?? '')); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /client/lib/src/common/utils/platform/authentication_observer_web.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_analytics/firebase_analytics.dart'; 2 | import 'package:firebase_auth/firebase_auth.dart'; 3 | import 'package:sentry/sentry.dart'; 4 | 5 | void onAuthStateChanges(User? user) { 6 | FirebaseAnalytics.instance.setUserId(id: user?.uid); 7 | FirebaseAnalytics.instance.setUserProperty(name: 'email', value: user?.email); 8 | FirebaseAnalytics.instance.setUserProperty(name: 'name', value: user?.displayName); 9 | if (user?.uid.isNotEmpty ?? false) { 10 | Sentry.configureScope((scope) => scope.setTag('uid', user?.uid ?? '')); 11 | } 12 | if (user?.email?.isNotEmpty ?? false) { 13 | Sentry.configureScope((scope) => scope.setTag('email', user?.email ?? '')); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /client/lib/src/common/utils/platform/error_util_io.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_crashlytics/firebase_crashlytics.dart'; 2 | import 'package:meta/meta.dart'; 3 | import 'package:sentry/sentry_io.dart'; 4 | 5 | @internal 6 | void logError( 7 | Object exception, { 8 | StackTrace? stackTrace, 9 | String? hint, 10 | }) { 11 | Sentry.captureException(exception, stackTrace: stackTrace, hint: hint); 12 | FirebaseCrashlytics.instance.recordError(exception, stackTrace, reason: hint, fatal: false); 13 | } 14 | 15 | @internal 16 | void logMessage( 17 | String message, { 18 | required bool warning, 19 | StackTrace? stackTrace, 20 | String? hint, 21 | List? params, 22 | }) { 23 | Sentry.captureMessage( 24 | message, 25 | level: warning ? SentryLevel.warning : SentryLevel.info, 26 | hint: hint, 27 | params: [ 28 | ...?params, 29 | if (stackTrace != null) 'StackTrace: $stackTrace', 30 | ], 31 | ); 32 | if (warning || stackTrace != null) { 33 | FirebaseCrashlytics.instance.recordError(message, stackTrace ?? StackTrace.current); 34 | } else { 35 | FirebaseCrashlytics.instance.log('$message${hint != null ? '\r\n$hint' : ''}'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /client/lib/src/common/utils/platform/error_util_web.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | import 'package:sentry/sentry.dart'; 3 | 4 | @internal 5 | void logError( 6 | Object exception, { 7 | StackTrace? stackTrace, 8 | String? hint, 9 | }) { 10 | Sentry.captureException(exception, stackTrace: stackTrace, hint: hint); 11 | } 12 | 13 | @internal 14 | void logMessage( 15 | String message, { 16 | required bool warning, 17 | StackTrace? stackTrace, 18 | String? hint, 19 | List? params, 20 | }) { 21 | Sentry.captureMessage( 22 | message, 23 | level: warning ? SentryLevel.warning : SentryLevel.info, 24 | hint: hint, 25 | params: [ 26 | ...?params, 27 | if (stackTrace != null) 'StackTrace: $stackTrace', 28 | ], 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /client/lib/src/common/widget/dart_logo_icon.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_jobs_client/src/common/constant/assets.gen.dart' as assets; 2 | import 'package:flutter/material.dart'; 3 | 4 | @immutable 5 | class DartLogoIcon extends StatelessWidget { 6 | const DartLogoIcon({ 7 | Key? key, 8 | }) : super(key: key); 9 | 10 | @override 11 | Widget build(BuildContext context) => Center( 12 | child: SizedBox.square( 13 | dimension: 42, 14 | child: Card( 15 | color: Theme.of(context).cardColor, 16 | shape: RoundedRectangleBorder( 17 | borderRadius: BorderRadius.circular(12), 18 | ), 19 | elevation: 2, 20 | child: Padding( 21 | padding: const EdgeInsets.all(5), 22 | child: const assets.$AssetsImageGen().dartLogo.dartLogoToolbar.image(), 23 | ), 24 | ), 25 | ), 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /client/lib/src/common/widget/error_snackbar.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math; 2 | import 'dart:ui' as ui; 3 | 4 | import 'package:dart_jobs_client/src/common/constant/layout_constraints.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | /// Снекбар ошибки 8 | class ErrorSnackBar extends SnackBar { 9 | /// Отобразить снекбар с ошибкой 10 | static void show( 11 | final BuildContext context, { 12 | final String? error, 13 | }) => 14 | ScaffoldMessenger.maybeOf(context)?.showSnackBar( 15 | ErrorSnackBar( 16 | error: error, 17 | ), 18 | ); 19 | 20 | ErrorSnackBar({ 21 | final String? error, 22 | Key? key, 23 | }) : super( 24 | key: key, 25 | //action: length > 1 26 | // ? SnackBarAction( 27 | // label: '${context.localization.next} ($length)', 28 | // textColor: Colors.white, 29 | // onPressed: () { 30 | // controller?.close(); 31 | // _showSnackBarError(errors.sublist(1)); 32 | // }, 33 | // ) 34 | // : null, 35 | backgroundColor: Colors.redAccent, 36 | content: SizedBox( 37 | height: 48, 38 | child: Center( 39 | child: Text( 40 | error ?? 'An error has occurred', 41 | maxLines: 2, 42 | overflow: TextOverflow.ellipsis, 43 | style: const TextStyle( 44 | color: Colors.white, 45 | ), 46 | ), 47 | ), 48 | ), 49 | duration: const Duration(milliseconds: 6000), 50 | margin: EdgeInsets.symmetric( 51 | horizontal: math.max( 52 | (ui.window.physicalSize.width / ui.window.devicePixelRatio - kBodyWidth) / 2, 53 | 12, 54 | ), 55 | vertical: 16, 56 | ), 57 | padding: const EdgeInsets.symmetric( 58 | horizontal: 8, 59 | ), 60 | behavior: SnackBarBehavior.floating, 61 | shape: RoundedRectangleBorder( 62 | borderRadius: BorderRadius.circular(4), 63 | ), 64 | ); 65 | } 66 | -------------------------------------------------------------------------------- /client/lib/src/common/widget/snackbars_for_global_scope.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_jobs_client/src/common/widget/error_snackbar.dart'; 2 | import 'package:dart_jobs_client/src/feature/authentication/bloc/authentication_bloc.dart'; 3 | import 'package:dart_jobs_client/src/feature/cloud_messaging/bloc/cloud_messaging_bloc.dart'; 4 | import 'package:dart_jobs_client/src/feature/feed/bloc/feed_bloc.dart'; 5 | import 'package:flutter/widgets.dart'; 6 | import 'package:flutter_bloc/flutter_bloc.dart'; 7 | 8 | @immutable 9 | class SnackBarsForGlobalScope extends StatelessWidget { 10 | const SnackBarsForGlobalScope({ 11 | required final this.child, 12 | Key? key, 13 | }) : super(key: key); 14 | 15 | final Widget child; 16 | 17 | @override 18 | Widget build(BuildContext context) => BlocListener( 19 | listener: (context, state) => state.mapOrNull( 20 | error: (error) => ErrorSnackBar.show( 21 | context, 22 | error: error.message, 23 | ), 24 | ), 25 | child: BlocListener( 26 | listener: (context, state) => state.mapOrNull( 27 | error: (error) => ErrorSnackBar.show( 28 | context, 29 | error: error.message, 30 | ), 31 | ), 32 | child: BlocListener( 33 | listener: (context, state) => state.mapOrNull( 34 | error: (error) => ErrorSnackBar.show( 35 | context, 36 | error: error.message, 37 | ), 38 | ), 39 | child: child, 40 | ), 41 | ), 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /client/lib/src/common/widget/statuses_overlay.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_jobs_client/src/common/constant/pubspec.yaml.g.dart' as pubspec; 2 | import 'package:flutter/material.dart'; 3 | 4 | @immutable 5 | class StatusesOverlay extends StatelessWidget { 6 | const StatusesOverlay({ 7 | required this.child, 8 | Key? key, 9 | }) : super(key: key); 10 | 11 | final Widget child; 12 | 13 | @override 14 | Widget build(BuildContext context) => Stack( 15 | children: [ 16 | Positioned.fill(child: child), 17 | Positioned( 18 | left: 8, 19 | bottom: 4, 20 | height: 12, 21 | right: 0, 22 | child: Row( 23 | mainAxisSize: MainAxisSize.min, 24 | mainAxisAlignment: MainAxisAlignment.start, 25 | crossAxisAlignment: CrossAxisAlignment.center, 26 | children: [ 27 | //Tooltip( 28 | // message: context.localization.connection_status, 29 | // child: Icon( 30 | // Icons.signal_cellular_null, 31 | // size: 12, 32 | // color: Colors.black26, 33 | // ), 34 | //), 35 | //SizedBox(width: 4), 36 | Text( 37 | 'v${pubspec.major}.${pubspec.minor}.${pubspec.patch}', 38 | style: Theme.of(context).primaryTextTheme.caption?.copyWith( 39 | color: Colors.black26, 40 | height: 1, 41 | ), 42 | maxLines: 1, 43 | textAlign: TextAlign.left, 44 | overflow: TextOverflow.clip, 45 | textDirection: TextDirection.ltr, 46 | ), 47 | ], 48 | ), 49 | ), 50 | ], 51 | ); 52 | } 53 | -------------------------------------------------------------------------------- /client/lib/src/common/widget/successful_snackbar.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math; 2 | import 'dart:ui' as ui; 3 | 4 | import 'package:dart_jobs_client/src/common/constant/layout_constraints.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | /// Снекбар успеха 8 | class SuccessfulSnackBar extends SnackBar { 9 | /// Отобразить снекбар с ошибкой 10 | static void show( 11 | final BuildContext context, { 12 | final String? message, 13 | }) => 14 | ScaffoldMessenger.maybeOf(context)?.showSnackBar( 15 | SuccessfulSnackBar( 16 | message: message, 17 | ), 18 | ); 19 | 20 | SuccessfulSnackBar({ 21 | final String? message, 22 | Key? key, 23 | }) : super( 24 | key: key, 25 | backgroundColor: Colors.green, 26 | content: SizedBox( 27 | height: 48, 28 | child: Center( 29 | child: Text( 30 | message ?? 'Successful', 31 | maxLines: 2, 32 | overflow: TextOverflow.ellipsis, 33 | style: const TextStyle( 34 | color: Colors.white, 35 | ), 36 | ), 37 | ), 38 | ), 39 | duration: const Duration(milliseconds: 6000), 40 | margin: EdgeInsets.symmetric( 41 | horizontal: math.max( 42 | (ui.window.physicalSize.width / ui.window.devicePixelRatio - kBodyWidth) / 2, 43 | 12, 44 | ), 45 | vertical: 16, 46 | ), 47 | padding: const EdgeInsets.symmetric( 48 | horizontal: 8, 49 | ), 50 | behavior: SnackBarBehavior.floating, 51 | shape: RoundedRectangleBorder( 52 | borderRadius: BorderRadius.circular(4), 53 | ), 54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /client/lib/src/feature/authentication/widget/profile_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_jobs_client/src/common/localization/localizations.dart'; 2 | import 'package:dart_jobs_client/src/common/widget/adaptive_scaffold.dart'; 3 | import 'package:dart_jobs_client/src/feature/authentication/widget/profile_widget.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class ProfileScreen extends StatelessWidget { 7 | const ProfileScreen({final Key? key}) : super(key: key); 8 | 9 | @override 10 | Widget build(final BuildContext context) => AdaptiveScaffold( 11 | appBar: AppBar( 12 | leading: const BackButton(), 13 | title: Text(context.localization.profile), 14 | ), 15 | body: const SafeArea( 16 | child: _ProfileBody(), 17 | ), 18 | ); 19 | } 20 | 21 | @immutable 22 | class _ProfileBody extends StatefulWidget { 23 | const _ProfileBody({ 24 | final Key? key, 25 | }) : super(key: key); 26 | 27 | @override 28 | State<_ProfileBody> createState() => _ProfileBodyState(); 29 | } 30 | 31 | /// TODO: Hero анимация для аватара 32 | class _ProfileBodyState extends State<_ProfileBody> { 33 | @override 34 | Widget build(final BuildContext context) => const ProfileWidget(); 35 | } 36 | -------------------------------------------------------------------------------- /client/lib/src/feature/browser_capabilities/models/browser_capabilities_data.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_jobs_client/src/feature/browser_capabilities/models/browser_capabilities_data_io.dart' 2 | // ignore: uri_does_not_exist 3 | if (dart.library.html) 'package:dart_jobs_client/src/feature/browser_capabilities/models/browser_capabilities_data_web.dart'; 4 | 5 | abstract class BrowserCapabilitiesData { 6 | bool get isBrowser; 7 | 8 | String get browserName; 9 | 10 | String get browserVersion; 11 | 12 | bool get isChrome; 13 | 14 | bool get isFirefox; 15 | 16 | bool get isInternetExplorer; 17 | 18 | bool get isSafari; 19 | 20 | bool get isWKWebView; 21 | 22 | bool get serviceWorkerSupported; 23 | 24 | bool get cacheSupported; 25 | 26 | bool get storageSupported; 27 | 28 | bool get supportedPWA; 29 | 30 | /// Поддержка Indexed DB 31 | bool get indexedDbSupported; 32 | 33 | /// Запущено в режиме PWA или TWA 34 | bool get standalone; 35 | 36 | WhenResult when({ 37 | required WhenResult Function() web, 38 | required WhenResult Function() io, 39 | }); 40 | 41 | factory BrowserCapabilitiesData() => getBrowserCapabilitiesData(); 42 | } 43 | -------------------------------------------------------------------------------- /client/lib/src/feature/browser_capabilities/models/browser_capabilities_data_io.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_jobs_client/src/feature/browser_capabilities/models/browser_capabilities_data.dart'; 2 | import 'package:meta/meta.dart'; 3 | 4 | BrowserCapabilitiesData getBrowserCapabilitiesData() => const BrowserCapabilitiesDataIO._(); 5 | 6 | class BrowserCapabilitiesDataIO implements BrowserCapabilitiesData { 7 | @override 8 | bool get isBrowser => false; 9 | 10 | @override 11 | String get browserName => 'Не браузер'; 12 | 13 | @override 14 | String get browserVersion => '0.0.0+0-0'; 15 | 16 | @override 17 | bool get isChrome => false; 18 | 19 | @override 20 | bool get isFirefox => false; 21 | 22 | @override 23 | bool get isInternetExplorer => false; 24 | 25 | @override 26 | bool get isSafari => false; 27 | 28 | @override 29 | bool get isWKWebView => false; 30 | 31 | @override 32 | bool get serviceWorkerSupported => false; 33 | 34 | @override 35 | bool get cacheSupported => false; 36 | 37 | @override 38 | bool get storageSupported => false; 39 | 40 | @override 41 | bool get indexedDbSupported => false; 42 | 43 | @override 44 | bool get standalone => true; 45 | 46 | @override 47 | bool get supportedPWA => true; 48 | 49 | @literal 50 | const BrowserCapabilitiesDataIO._(); 51 | 52 | @override 53 | WhenResult when({ 54 | required WhenResult Function() web, 55 | required WhenResult Function() io, 56 | }) => 57 | io(); 58 | } 59 | -------------------------------------------------------------------------------- /client/lib/src/feature/bug_report/logic/bug_report_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_jobs_client/src/feature/bug_report/model/bug_report_entity.dart'; 2 | import 'package:sentry/sentry.dart'; 3 | 4 | // ignore: one_member_abstracts 5 | abstract class IBugReportRepository { 6 | Future send(BugReportEntity report); 7 | } 8 | 9 | class BugReportRepository implements IBugReportRepository { 10 | @override 11 | Future send(BugReportEntity report) async { 12 | final message = '${report.typeRepresentation}\n' 13 | '${report.hasUserId ? 'From: ${report.userRepresentation}\n' : ''}' 14 | '${report.message}'; 15 | final id = await Sentry.captureMessage( 16 | message, 17 | level: SentryLevel.info, 18 | hint: 'User feedback', 19 | ); 20 | await Sentry.captureUserFeedback( 21 | SentryUserFeedback( 22 | eventId: id, 23 | name: report.userId, 24 | email: report.email, 25 | comments: message, 26 | ), 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /client/lib/src/feature/bug_report/model/bug_report_entity.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | /// Репорт от пользователя 4 | @immutable 5 | class BugReportEntity { 6 | const BugReportEntity({ 7 | required final this.type, 8 | required final this.message, 9 | this.userId, 10 | this.email, 11 | }); 12 | 13 | /// Представление типа баг репорта 14 | String get typeRepresentation { 15 | switch (type) { 16 | case BugReportType.feature: 17 | return 'FEATURE'; 18 | case BugReportType.improvement: 19 | return 'IMPROVEMENT'; 20 | case BugReportType.bug: 21 | return 'BUG'; 22 | } 23 | } 24 | 25 | /// Представление пользователя 26 | /// Может быть null 27 | String? get userRepresentation { 28 | if (userId == null) return null; 29 | return '$userId${email == null ? '' : ' <$email>'}'; 30 | } 31 | 32 | /// Обладает идентификатором пользователя 33 | bool get hasUserId => userId != null; 34 | 35 | /// Тип репорта 36 | final BugReportType type; 37 | 38 | /// Сообщение 39 | final String message; 40 | 41 | /// Идентификатор пользователя. 42 | /// Может быть null 43 | final String? userId; 44 | 45 | /// е-мейл пользователя 46 | /// Может быть null 47 | final String? email; 48 | } 49 | 50 | /// Тип репорта 51 | enum BugReportType { 52 | /// Новая фишка 53 | feature, 54 | 55 | /// Улучшение 56 | improvement, 57 | 58 | /// Баг 59 | bug, 60 | } 61 | -------------------------------------------------------------------------------- /client/lib/src/feature/cloud_messaging/model/notification_status.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | @immutable 4 | class NotificationStatus { 5 | const NotificationStatus({ 6 | required final this.isAuthorized, 7 | required final this.isSupported, 8 | }); 9 | 10 | @literal 11 | const NotificationStatus.notSupported() 12 | : isAuthorized = false, 13 | isSupported = false; 14 | 15 | @literal 16 | const NotificationStatus.authorized() 17 | : isAuthorized = true, 18 | isSupported = true; 19 | 20 | @literal 21 | const NotificationStatus.notAuthorized() 22 | : isAuthorized = false, 23 | isSupported = true; 24 | 25 | final bool isSupported; 26 | 27 | bool get isNotSupported => !isSupported; 28 | 29 | final bool isAuthorized; 30 | 31 | bool get notAuthorized => !isAuthorized; 32 | 33 | @override 34 | bool operator ==(Object other) => 35 | identical(this, other) || 36 | (other is NotificationStatus && other.isAuthorized == isAuthorized && other.isSupported == isSupported); 37 | 38 | @override 39 | int get hashCode => isAuthorized ? 2 : (isSupported ? 1 : 0); 40 | } 41 | -------------------------------------------------------------------------------- /client/lib/src/feature/cloud_messaging/widget/cloud_messaging_scope.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_jobs_client/src/feature/cloud_messaging/bloc/cloud_messaging_bloc.dart'; 2 | import 'package:dart_jobs_client/src/feature/initialization/widget/repository_scope.dart'; 3 | import 'package:flutter/widgets.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | 6 | @immutable 7 | class CloudMessagingScope extends StatelessWidget { 8 | const CloudMessagingScope({ 9 | required this.child, 10 | Key? key, 11 | }) : super(key: key); 12 | 13 | static void request(BuildContext context, {bool ifNotAlreadyRequested = false}) => 14 | BlocProvider.of(context, listen: false).add( 15 | CloudMessagingEvent.request(ifNotAlreadyRequested: ifNotAlreadyRequested), 16 | ); 17 | 18 | static void check(BuildContext context) => BlocProvider.of(context, listen: false).add( 19 | const CloudMessagingEvent.check(), 20 | ); 21 | 22 | static void subscribeToCreatedTopic(BuildContext context) => 23 | BlocProvider.of(context, listen: false).add( 24 | const CloudMessagingEvent.subscribeToCreatedTopic(), 25 | ); 26 | 27 | static void unsubscribeToCreatedTopic(BuildContext context) => 28 | BlocProvider.of(context, listen: false).add( 29 | const CloudMessagingEvent.unsubscribeToCreatedTopic(), 30 | ); 31 | 32 | final Widget child; 33 | 34 | @override 35 | Widget build(BuildContext context) => BlocProvider( 36 | create: (context) { 37 | final bloc = CloudMessagingBLoC( 38 | service: RepositoryScope.of(context).cloudMessagingService, 39 | )..add(const CloudMessagingEvent.check()); 40 | // Через 5 секунд после инициализации блока - запросить разрешение на пуши 41 | // если оно еще не запрашивалось 42 | /* 43 | Future.delayed( 44 | const Duration(seconds: 5), 45 | () { 46 | if (bloc.isClosed) return; 47 | bloc.add(const CloudMessagingEvent.request(ifNotAlreadyRequested: true)); 48 | }, 49 | ); 50 | */ 51 | return bloc; 52 | }, 53 | child: child, 54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /client/lib/src/feature/feed/widget/feed_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_jobs_client/src/feature/feed/bloc/feed_bloc.dart'; 2 | import 'package:dart_jobs_client/src/feature/feed/widget/feed_tile.dart'; 3 | import 'package:flutter/widgets.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | 6 | @immutable 7 | class FeedList extends StatelessWidget { 8 | const FeedList({ 9 | final Key? key, 10 | }) : super(key: key); 11 | 12 | @override 13 | Widget build(final BuildContext context) => BlocBuilder( 14 | //buildWhen: (prev, next) => prev.list.length != next.list.length, 15 | builder: (final context, final state) { 16 | final length = state.list.length; 17 | return SliverFixedExtentList( 18 | itemExtent: FeedTile.height, 19 | delegate: SliverChildBuilderDelegate( 20 | (final context, final index) { 21 | if (index >= length) { 22 | return const FeedTile.loading(); 23 | } 24 | final job = state.list[index]; 25 | return FeedTile.job( 26 | job: job, 27 | key: ValueKey('${job.id}_${job.updated.millisecondsSinceEpoch}'), 28 | ); 29 | }, 30 | childCount: state.maybeMap( 31 | orElse: () => length, 32 | pagination: (final processed) => length + processed.filter.limit, 33 | ), 34 | ), 35 | ); 36 | }, 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /client/lib/src/feature/initialization/data/app_migrator.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_jobs_client/src/common/constant/pubspec.yaml.g.dart' as pubspec; 2 | import 'package:dart_jobs_client/src/common/constant/storage_namespace.dart'; 3 | import 'package:l/l.dart'; 4 | import 'package:meta/meta.dart'; 5 | import 'package:shared_preferences/shared_preferences.dart'; 6 | 7 | @sealed 8 | abstract class AppMigrator { 9 | AppMigrator._(); 10 | 11 | static Future migrate(SharedPreferences sharedPreferences) async { 12 | try { 13 | final prevMajor = sharedPreferences.getInt(kVersionMajorKey); 14 | final prevMinor = sharedPreferences.getInt(kVersionMinorKey); 15 | final prevPatch = sharedPreferences.getInt(kVersionPatchKey); 16 | 17 | if (pubspec.major == prevMajor && pubspec.minor == prevMinor && pubspec.patch == prevPatch) { 18 | return; 19 | } else if (prevMajor != null && prevMinor != null && prevPatch != null) { 20 | l.i('Переходим с версии $prevMajor.$prevMinor.$prevPatch на ${pubspec.major}.${pubspec.minor}.${pubspec.patch}'); 21 | } 22 | 23 | await Future.wait( 24 | >[ 25 | sharedPreferences.setInt(kVersionMajorKey, pubspec.major), 26 | sharedPreferences.setInt(kVersionMinorKey, pubspec.minor), 27 | sharedPreferences.setInt(kVersionPatchKey, pubspec.patch), 28 | ], 29 | ); 30 | } on Object { 31 | l.e('Ошибка миграции приложения'); 32 | rethrow; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /client/lib/src/feature/initialization/data/graphql_client_creator.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_jobs_client/src/common/constant/environment.dart'; 2 | import 'package:dart_jobs_client/src/common/utils/error_util.dart'; 3 | import 'package:dart_jobs_shared/graphql.dart'; 4 | 5 | abstract class GraphQLClientCreator { 6 | GraphQLClientCreator._(); 7 | 8 | static GQLClient create() => GQLClient(_getLinks()); 9 | 10 | static Link _getLinks() { 11 | final links = []; 12 | 13 | /// Exception link with logging 14 | final exceptionLink = ExceptionLink( 15 | onGraphQLError: ErrorUtil.logGraphQLError, 16 | onLinkException: ErrorUtil.logLinkException, 17 | ); 18 | links.add(exceptionLink); 19 | 20 | /// Dedupe link 21 | final dedupeLink = DedupeLink(); 22 | links.add(dedupeLink); 23 | 24 | /// Performance link 25 | /// https://github.com/FirebaseExtended/flutterfire/issues/6140 26 | //final performanceLink = PerformanceLink(performance: FirebasePerformance.instance); 27 | //links.add(performanceLink); 28 | 29 | /// Metadata link 30 | final metadataLink = MetadataLink(metadata: {}); 31 | links.add(metadataLink); 32 | 33 | /// TODO: Cache Link 34 | 35 | /// TODO: Log link 36 | 37 | /// Handler 38 | final handlerLink = HandlerLink(kGraphQLEndpoint); 39 | links.add(handlerLink); 40 | 41 | return Link.from( 42 | links, 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /client/lib/src/feature/initialization/widget/repository_scope.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_jobs_client/src/feature/initialization/model/initialization_progress.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | 4 | export 'package:dart_jobs_client/src/feature/initialization/bloc/initialization_bloc.dart'; 5 | export 'package:dart_jobs_client/src/feature/initialization/model/initialization_progress.dart'; 6 | 7 | @immutable 8 | class RepositoryScope extends StatelessWidget { 9 | const RepositoryScope({ 10 | required final this.repositoryStore, 11 | required this.builder, 12 | final Key? key, 13 | }) : super(key: key); 14 | 15 | final RepositoryStore repositoryStore; 16 | final WidgetBuilder builder; 17 | 18 | /// Получить результат инициализации из контекста 19 | /// Работает только в контексте проинициализированного приложения 20 | static RepositoryStore of(final BuildContext context) { 21 | final inhW = context.getElementForInheritedWidgetOfExactType<_InheritedRepositoryScope>()?.widget; 22 | final store = inhW is _InheritedRepositoryScope ? inhW.repositoryStore : null; 23 | return store ?? _throwNotInitializedYet(); 24 | } 25 | 26 | static Never _throwNotInitializedYet() => throw UnsupportedError('The application has not been initialized yet'); 27 | 28 | @override 29 | Widget build(final BuildContext context) => _InheritedRepositoryScope( 30 | repositoryStore: repositoryStore, 31 | child: Builder( 32 | builder: builder, 33 | ), 34 | ); 35 | } 36 | 37 | @immutable 38 | class _InheritedRepositoryScope extends InheritedWidget { 39 | const _InheritedRepositoryScope({ 40 | required final this.repositoryStore, 41 | required final Widget child, 42 | final Key? key, 43 | }) : super(key: key, child: child); 44 | 45 | final RepositoryStore repositoryStore; 46 | 47 | @override 48 | bool updateShouldNotify(final _InheritedRepositoryScope oldWidget) => false; 49 | } 50 | -------------------------------------------------------------------------------- /client/lib/src/feature/job/widget/edit/job_bottom_sheet.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | @immutable 4 | class JobBottomSheet extends StatelessWidget implements PreferredSizeWidget { 5 | const JobBottomSheet({ 6 | required final this.height, 7 | required final this.child, 8 | Key? key, 9 | }) : super(key: key); 10 | 11 | static Future show({ 12 | required final BuildContext context, 13 | required final double height, 14 | required final Widget child, 15 | }) => 16 | showModalBottomSheet( 17 | context: context, 18 | shape: const RoundedRectangleBorder( 19 | borderRadius: BorderRadius.vertical( 20 | top: Radius.circular(10), 21 | ), 22 | ), 23 | builder: (context) => JobBottomSheet( 24 | height: height, 25 | child: child, 26 | ), 27 | ); 28 | 29 | /// Высота контента (без учета заголовка) 30 | final double height; 31 | 32 | /// Контент 33 | final Widget child; 34 | 35 | @override 36 | Size get preferredSize => Size.fromHeight(24 + height + 16); 37 | 38 | @override 39 | Widget build(BuildContext context) => SizedBox( 40 | height: 24 + 16 + height, 41 | child: Column( 42 | mainAxisSize: MainAxisSize.min, 43 | mainAxisAlignment: MainAxisAlignment.start, 44 | crossAxisAlignment: CrossAxisAlignment.center, 45 | children: [ 46 | const SizedBox( 47 | height: 24, 48 | child: Center( 49 | child: DecoratedBox( 50 | decoration: BoxDecoration( 51 | color: Color(0xFFD6DBE0), 52 | borderRadius: BorderRadius.all(Radius.circular(2)), 53 | ), 54 | child: SizedBox( 55 | width: 38, 56 | height: 4, 57 | ), 58 | ), 59 | ), 60 | ), 61 | Expanded( 62 | child: child, 63 | ), 64 | const SizedBox( 65 | height: 16, 66 | ), 67 | ], 68 | ), 69 | ); 70 | } 71 | -------------------------------------------------------------------------------- /client/lib/src/feature/job/widget/edit/job_remote_input.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math; 2 | 3 | import 'package:dart_jobs_client/src/common/localization/localizations.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | @immutable 7 | class JobRemoteInput extends StatelessWidget { 8 | const JobRemoteInput({ 9 | required final this.controller, 10 | Key? key, 11 | }) : super(key: key); 12 | 13 | final ValueNotifier controller; 14 | 15 | @override 16 | Widget build(BuildContext context) => SizedBox( 17 | height: 60, 18 | child: Center( 19 | child: ValueListenableBuilder( 20 | valueListenable: controller, 21 | builder: (context, value, child) => LayoutBuilder( 22 | builder: (context, constraints) => ToggleButtons( 23 | constraints: BoxConstraints( 24 | minWidth: math.min(constraints.maxWidth / 3, 150), 25 | minHeight: 45, 26 | maxHeight: 45, 27 | ), 28 | onPressed: (i) { 29 | controller.value = i == 0; 30 | FocusScope.of(context).unfocus(); 31 | }, 32 | isSelected: [ 33 | value, 34 | !value, 35 | ], 36 | children: [ 37 | Text( 38 | context.localization.job_field_remote, 39 | maxLines: 1, 40 | overflow: TextOverflow.ellipsis, 41 | //style: Theme.of(context).textTheme.subtitle1, 42 | ), 43 | Text( 44 | context.localization.job_field_office, 45 | maxLines: 1, 46 | overflow: TextOverflow.ellipsis, 47 | //style: Theme.of(context).textTheme.subtitle1, 48 | ), 49 | ], 50 | ), 51 | ), 52 | ), 53 | ), 54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /client/lib/src/feature/job/widget/job_not_found.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_jobs_client/src/common/localization/localizations.dart'; 2 | import 'package:dart_jobs_client/src/common/router/router.dart'; 3 | import 'package:dart_jobs_client/src/common/widget/adaptive_scaffold.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | @immutable 7 | class JobNotFound extends StatelessWidget { 8 | const JobNotFound({ 9 | final Key? key, 10 | }) : super(key: key); 11 | 12 | @override 13 | Widget build(final BuildContext context) => AdaptiveScaffold( 14 | appBar: AppBar( 15 | leading: const BackButton(), 16 | title: Text( 17 | context.localization.job_not_found, 18 | maxLines: 1, 19 | ), 20 | ), 21 | body: SafeArea( 22 | child: Center( 23 | child: ListView( 24 | shrinkWrap: true, 25 | physics: const ClampingScrollPhysics(), 26 | children: [ 27 | Center( 28 | child: Text( 29 | context.localization.job_not_found, 30 | style: Theme.of(context).textTheme.headline5, 31 | ), 32 | ), 33 | const SizedBox( 34 | height: 24, 35 | ), 36 | Center( 37 | child: IconButton( 38 | onPressed: () => AppRouter.goHome(context), 39 | icon: Icon( 40 | _kHomeIcon, 41 | color: Theme.of(context).primaryColor, 42 | ), 43 | iconSize: 48, 44 | ), 45 | ), 46 | ], 47 | ), 48 | ), 49 | ), 50 | ); 51 | } 52 | 53 | const IconData _kHomeIcon = Icons.home; 54 | -------------------------------------------------------------------------------- /client/lib/src/feature/not_found/widget/not_found_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_jobs_client/src/common/localization/localizations.dart'; 2 | import 'package:dart_jobs_client/src/common/router/router.dart'; 3 | import 'package:dart_jobs_client/src/common/widget/adaptive_scaffold.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | @immutable 7 | class NotFoundScreen extends StatelessWidget { 8 | const NotFoundScreen({ 9 | final Key? key, 10 | }) : super(key: key); 11 | 12 | @override 13 | Widget build(final BuildContext context) => AdaptiveScaffold( 14 | appBar: AppBar( 15 | leading: const BackButton(), 16 | title: Text(context.localization.not_found), 17 | ), 18 | body: SafeArea( 19 | child: Center( 20 | child: TextButton( 21 | onPressed: () => AppRouter.maybePop(context).then( 22 | (final value) { 23 | if (!value) { 24 | AppRouter.goHome(context); 25 | } 26 | }, 27 | ), 28 | child: Text( 29 | context.materialLocalizations.backButtonTooltip, 30 | ), 31 | ), 32 | ), 33 | ), 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /client/lib/src/feature/settings/model/user_settings.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui' as ui show Brightness, window; 2 | 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | import 'package:platform_info/platform_info.dart'; 5 | 6 | part 'user_settings.freezed.dart'; 7 | part 'user_settings.g.dart'; 8 | 9 | @freezed 10 | class UserSettings with _$UserSettings { 11 | const factory UserSettings({ 12 | // ignore: invalid_annotation_target 13 | @JsonKey(name: 'locale', required: true, disallowNullValue: true) required final String locale, 14 | // ignore: invalid_annotation_target 15 | @JsonKey(name: 'theme', required: true, disallowNullValue: true) required final String theme, 16 | //@JsonKey(name: 'tip_of_the_day', required: true, disallowNullValue: true) required String tipOfTheDay, 17 | }) = _UserSettings; 18 | 19 | static UserSettings? _initial; 20 | 21 | // ignore: prefer_constructors_over_static_methods 22 | static UserSettings get initial => _initial ??= UserSettings( 23 | locale: platform.locale, 24 | theme: ui.window.platformBrightness == ui.Brightness.dark ? 'dark' : 'light', 25 | ); 26 | 27 | /// Generate Class from Map 28 | factory UserSettings.fromJson(final Map json) => _$UserSettingsFromJson(json); 29 | } 30 | -------------------------------------------------------------------------------- /client/lib/src/feature/settings/widget/flags/horizontal_lines_flag_painter.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 4 | /// Paints flag with amount of horizontal rectangles equal to number of colors passed 5 | /// Hint: default flag size is 3x2 6 | /// 7 | class HorizontalLinesFlagPainter extends CustomPainter { 8 | final double frameWidth; 9 | final double frameDiff; 10 | final Color frameColor; 11 | 12 | final List stripesColors; 13 | 14 | const HorizontalLinesFlagPainter({ 15 | this.stripesColors = const [Colors.white, Colors.blue, Colors.red], 16 | this.frameWidth = 1, 17 | this.frameColor = Colors.black, 18 | }) : frameDiff = frameWidth / 2; 19 | 20 | //This method is called whenever the object needs to be repainted. 21 | @override 22 | void paint(Canvas canvas, Size size) { 23 | final elemH = (size.height - frameWidth) / stripesColors.length; 24 | 25 | //draw stripes: 26 | for (var i = 0; i < stripesColors.length; i++) { 27 | final paint = Paint()..color = stripesColors[i]; 28 | canvas.drawRect(Rect.fromLTRB(0, i * elemH + frameDiff, size.width, (i + 1) * elemH + frameDiff), paint); 29 | } 30 | 31 | //draw frame: 32 | if (frameWidth > 0) { 33 | final paintFrame = Paint() 34 | ..color = frameColor 35 | ..strokeWidth = frameWidth 36 | ..strokeCap = StrokeCap.round; 37 | canvas 38 | ..drawLine(Offset.zero, Offset(0, size.height), paintFrame) 39 | ..drawLine(Offset.zero, Offset(size.width, 0), paintFrame) 40 | ..drawLine(Offset(size.width, size.height), Offset(0, size.height), paintFrame) 41 | ..drawLine(Offset(size.width, size.height), Offset(size.width, 0), paintFrame); 42 | } 43 | } 44 | 45 | //This method is called when a new instance of the class is provided. 46 | @override 47 | bool shouldRepaint(covariant CustomPainter oldDelegate) => false; 48 | } 49 | -------------------------------------------------------------------------------- /client/lib/src/feature/settings/widget/flags/russia_flag_painter.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:dart_jobs_client/src/feature/settings/widget/flags/horizontal_lines_flag_painter.dart'; 4 | 5 | class RussiaFlagPainter extends HorizontalLinesFlagPainter { 6 | const RussiaFlagPainter({ 7 | final double frameWidth = 1, 8 | final Color frameColor = const Color(0xFF000000), 9 | }) : super( 10 | frameWidth: frameWidth, 11 | frameColor: frameColor, 12 | stripesColors: const [ 13 | _white, 14 | _blue, 15 | _red, 16 | ], 17 | ); 18 | 19 | static const _white = Color(0xFFFFFFFF); 20 | static const _blue = Color(0xFF0000FF); 21 | static const _red = Color(0xFFFF0000); 22 | } 23 | -------------------------------------------------------------------------------- /client/lib/src/feature/settings/widget/platform.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_jobs_client/src/feature/settings/widget/platform/platform_io.dart' 2 | if (dart.library.html) 'package:dart_jobs_client/src/feature/settings/widget/platform/platform_web.dart' 3 | as platform; 4 | 5 | void changeTheme(String theme) => platform.changeTheme(theme); 6 | -------------------------------------------------------------------------------- /client/lib/src/feature/settings/widget/platform/platform_io.dart: -------------------------------------------------------------------------------- 1 | void changeTheme(String theme) {} 2 | -------------------------------------------------------------------------------- /client/lib/src/feature/settings/widget/platform/platform_web.dart: -------------------------------------------------------------------------------- 1 | import 'dart:html' as html; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | void changeTheme(String theme) { 6 | ThemeData.dark(); 7 | final primaryColor = theme == 'dark' ? '#212121' : '#2196f3'; 8 | final backgroundColor = theme == 'dark' ? '#303030' : '#fafafa'; 9 | _setMeta('theme-color', primaryColor); 10 | _setMeta('msapplication-navbutton-color', primaryColor); 11 | html.document.body?.style.backgroundColor = backgroundColor; 12 | } 13 | 14 | void _setMeta(String key, String value) { 15 | final element = html.document.querySelector('meta[name="$key"]'); 16 | if (element == null) { 17 | html.document.head?.append( 18 | html.MetaElement() 19 | ..name = key 20 | ..content = value, 21 | ); 22 | } else { 23 | element.setAttribute('content', value); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /client/lib/src/feature/settings/widget/settings_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_jobs_client/src/common/localization/localizations.dart'; 2 | import 'package:dart_jobs_client/src/common/widget/adaptive_scaffold.dart'; 3 | import 'package:dart_jobs_client/src/feature/settings/widget/settings_list.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | @immutable 7 | class SettingsScreen extends StatelessWidget { 8 | const SettingsScreen({ 9 | final Key? key, 10 | }) : super(key: key); 11 | 12 | @override 13 | Widget build(final BuildContext context) => AdaptiveScaffold( 14 | appBar: AppBar( 15 | leading: const BackButton(), 16 | title: Text(context.localization.settings), 17 | ), 18 | body: const SafeArea( 19 | child: SettingsList(), 20 | ), 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /client/lib/src/feature/settings/widget/settings_tile.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | @immutable 4 | class SettingsTile extends StatelessWidget { 5 | const SettingsTile({ 6 | required final this.title, 7 | final this.leading, 8 | final this.subtitle, 9 | final this.enabled = true, 10 | final this.trailing, 11 | final this.onTap, 12 | Key? key, 13 | }) : super(key: key); 14 | 15 | final Widget? title; 16 | final Widget? subtitle; 17 | final Widget? leading; 18 | final bool enabled; 19 | final Widget? trailing; 20 | final VoidCallback? onTap; 21 | 22 | @override 23 | Widget build(BuildContext context) => ListTile( 24 | title: title, 25 | subtitle: subtitle, 26 | leading: leading, 27 | enabled: enabled, 28 | trailing: trailing, 29 | onTap: enabled ? onTap : null, 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /client/linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /client/linux/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | 12 | void fl_register_plugins(FlPluginRegistry* registry) { 13 | g_autoptr(FlPluginRegistrar) sentry_flutter_registrar = 14 | fl_plugin_registry_get_registrar_for_plugin(registry, "SentryFlutterPlugin"); 15 | sentry_flutter_plugin_register_with_registrar(sentry_flutter_registrar); 16 | g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = 17 | fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); 18 | url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); 19 | } 20 | -------------------------------------------------------------------------------- /client/linux/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void fl_register_plugins(FlPluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /client/linux/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | sentry_flutter 7 | url_launcher_linux 8 | ) 9 | 10 | set(PLUGIN_BUNDLED_LIBRARIES) 11 | 12 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 13 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) 14 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 15 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 16 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 17 | endforeach(plugin) 18 | -------------------------------------------------------------------------------- /client/linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /client/linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /client/macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/xcuserdata/ 7 | -------------------------------------------------------------------------------- /client/macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "ephemeral/Flutter-Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /client/macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "ephemeral/Flutter-Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /client/macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | import cloud_firestore 9 | import firebase_analytics 10 | import firebase_auth 11 | import firebase_core 12 | import firebase_crashlytics 13 | import firebase_messaging 14 | import firebase_remote_config 15 | import package_info_plus_macos 16 | import path_provider_macos 17 | import sentry_flutter 18 | import share_plus_macos 19 | import shared_preferences_macos 20 | import url_launcher_macos 21 | 22 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 23 | FLTFirebaseFirestorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseFirestorePlugin")) 24 | FLTFirebaseAnalyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAnalyticsPlugin")) 25 | FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin")) 26 | FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) 27 | FLTFirebaseCrashlyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCrashlyticsPlugin")) 28 | FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin")) 29 | FLTFirebaseRemoteConfigPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseRemoteConfigPlugin")) 30 | FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) 31 | PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) 32 | SentryFlutterPlugin.register(with: registry.registrar(forPlugin: "SentryFlutterPlugin")) 33 | SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) 34 | SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) 35 | UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) 36 | } 37 | -------------------------------------------------------------------------------- /client/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /client/macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /client/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /client/macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @NSApplicationMain 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /client/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "app_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "app_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "app_icon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "app_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "app_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "app_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "app_icon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "app_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "app_icon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "app_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /client/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /client/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /client/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /client/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /client/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /client/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /client/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /client/macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = dart_jobs 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = dev.plugfox.dartjobs 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2021 dev.plugfox. All rights reserved. 15 | -------------------------------------------------------------------------------- /client/macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /client/macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /client/macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /client/macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /client/macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /client/macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController.init() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /client/macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /client/remoteconfig.template.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /client/test/unit_test.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: unnecessary_lambdas 2 | 3 | import 'package:flutter_test/flutter_test.dart'; 4 | 5 | void main() { 6 | group( 7 | 'Тесты', 8 | () { 9 | test( 10 | 'Тест', 11 | () { 12 | expect(true, isTrue); 13 | }, 14 | ); 15 | }, 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /client/web/.well-known/assetlinks.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "relation": [ 3 | "delegate_permission/common.handle_all_urls" 4 | ], 5 | "target": { 6 | "namespace": "android_app", 7 | "package_name": "dev.plugfox.dartjobs", 8 | "sha256_cert_fingerprints": [ 9 | "f5:a6:2c:02:92:8b:bc:8d:7a:bb:b6:18:c1:58:a3:6d:92:da:86:28:6c:55:dd:36:df:d2:bb:04:ad:fe:9f:c9", 10 | "bb:23:a8:38:4d:52:5e:ce:56:45:00:0c:9c:52:ed:c4:37:b6:a4:10:66:f3:14:a4:b5:0d:ab:fb:aa:d3:cf:3f" 11 | ] 12 | } 13 | }] -------------------------------------------------------------------------------- /client/web/android-chrome-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/android-chrome-144x144.png -------------------------------------------------------------------------------- /client/web/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/android-chrome-192x192.png -------------------------------------------------------------------------------- /client/web/android-chrome-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/android-chrome-256x256.png -------------------------------------------------------------------------------- /client/web/android-chrome-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/android-chrome-36x36.png -------------------------------------------------------------------------------- /client/web/android-chrome-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/android-chrome-384x384.png -------------------------------------------------------------------------------- /client/web/android-chrome-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/android-chrome-48x48.png -------------------------------------------------------------------------------- /client/web/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/android-chrome-512x512.png -------------------------------------------------------------------------------- /client/web/android-chrome-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/android-chrome-72x72.png -------------------------------------------------------------------------------- /client/web/android-chrome-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/android-chrome-96x96.png -------------------------------------------------------------------------------- /client/web/apple-touch-icon-114x114-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/apple-touch-icon-114x114-precomposed.png -------------------------------------------------------------------------------- /client/web/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /client/web/apple-touch-icon-120x120-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/apple-touch-icon-120x120-precomposed.png -------------------------------------------------------------------------------- /client/web/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /client/web/apple-touch-icon-144x144-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/apple-touch-icon-144x144-precomposed.png -------------------------------------------------------------------------------- /client/web/apple-touch-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/apple-touch-icon-144x144.png -------------------------------------------------------------------------------- /client/web/apple-touch-icon-152x152-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/apple-touch-icon-152x152-precomposed.png -------------------------------------------------------------------------------- /client/web/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /client/web/apple-touch-icon-180x180-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/apple-touch-icon-180x180-precomposed.png -------------------------------------------------------------------------------- /client/web/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /client/web/apple-touch-icon-57x57-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/apple-touch-icon-57x57-precomposed.png -------------------------------------------------------------------------------- /client/web/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /client/web/apple-touch-icon-60x60-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/apple-touch-icon-60x60-precomposed.png -------------------------------------------------------------------------------- /client/web/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /client/web/apple-touch-icon-72x72-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/apple-touch-icon-72x72-precomposed.png -------------------------------------------------------------------------------- /client/web/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /client/web/apple-touch-icon-76x76-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/apple-touch-icon-76x76-precomposed.png -------------------------------------------------------------------------------- /client/web/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /client/web/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /client/web/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/apple-touch-icon.png -------------------------------------------------------------------------------- /client/web/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | #2196F3 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /client/web/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/favicon-16x16.png -------------------------------------------------------------------------------- /client/web/favicon-194x194.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/favicon-194x194.png -------------------------------------------------------------------------------- /client/web/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/favicon-32x32.png -------------------------------------------------------------------------------- /client/web/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/favicon.ico -------------------------------------------------------------------------------- /client/web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/favicon.png -------------------------------------------------------------------------------- /client/web/js/firebase-messaging-sw.js: -------------------------------------------------------------------------------- 1 | importScripts("https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js"); 2 | importScripts("https://www.gstatic.com/firebasejs/8.10.0/firebase-messaging.js"); 3 | 4 | firebase.initializeApp({ 5 | apiKey: "AIzaSyBst2CWEUfNuaHh9OA3XW3JdZ3vpLGPMcg", 6 | authDomain: "dart-job.firebaseapp.com", 7 | projectId: "dart-job", 8 | storageBucket: "dart-job.appspot.com", 9 | messagingSenderId: "174403318860", 10 | appId: "1:174403318860:web:d8f54a5b305667d08230e9", 11 | measurementId: "G-BYS0GKHDNS" 12 | }); 13 | 14 | // Necessary to receive background messages: 15 | const messaging = firebase.messaging(); 16 | 17 | // Optional: 18 | messaging.onBackgroundMessage((m) => { 19 | console.log("onBackgroundMessage", m); 20 | }); -------------------------------------------------------------------------------- /client/web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Dart Jobs", 3 | "short_name": "Jobs", 4 | "description": "Dart Jobs", 5 | "start_url": ".", 6 | "display": "standalone", 7 | "background_color": "#2196F3", 8 | "theme_color": "#2196F3", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "/android-chrome-36x36.png", 14 | "sizes": "36x36", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "/android-chrome-48x48.png", 19 | "sizes": "48x48", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "/android-chrome-72x72.png", 24 | "sizes": "72x72", 25 | "type": "image/png" 26 | }, 27 | { 28 | "src": "/android-chrome-96x96.png", 29 | "sizes": "96x96", 30 | "type": "image/png" 31 | }, 32 | { 33 | "src": "/android-chrome-144x144.png", 34 | "sizes": "144x144", 35 | "type": "image/png" 36 | }, 37 | { 38 | "src": "/android-chrome-192x192.png", 39 | "sizes": "192x192", 40 | "type": "image/png" 41 | }, 42 | { 43 | "src": "/android-chrome-256x256.png", 44 | "sizes": "256x256", 45 | "type": "image/png" 46 | }, 47 | { 48 | "src": "/android-chrome-384x384.png", 49 | "sizes": "384x384", 50 | "type": "image/png" 51 | }, 52 | { 53 | "src": "/android-chrome-512x512.png", 54 | "sizes": "512x512", 55 | "type": "image/png" 56 | } 57 | ] 58 | } 59 | -------------------------------------------------------------------------------- /client/web/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/mstile-144x144.png -------------------------------------------------------------------------------- /client/web/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/mstile-150x150.png -------------------------------------------------------------------------------- /client/web/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/mstile-310x150.png -------------------------------------------------------------------------------- /client/web/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/mstile-310x310.png -------------------------------------------------------------------------------- /client/web/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/mstile-70x70.png -------------------------------------------------------------------------------- /client/web/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /client/web/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.14, written by Peter Selinger 2001-2017 9 | 10 | 12 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /client/web/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Dart Jobs", 3 | "short_name": "Jobs", 4 | "description": "Dart Jobs", 5 | "start_url": ".", 6 | "display": "standalone", 7 | "background_color": "#2196F3", 8 | "theme_color": "#2196F3", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "/android-chrome-36x36.png", 14 | "sizes": "36x36", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "/android-chrome-48x48.png", 19 | "sizes": "48x48", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "/android-chrome-72x72.png", 24 | "sizes": "72x72", 25 | "type": "image/png" 26 | }, 27 | { 28 | "src": "/android-chrome-96x96.png", 29 | "sizes": "96x96", 30 | "type": "image/png" 31 | }, 32 | { 33 | "src": "/android-chrome-144x144.png", 34 | "sizes": "144x144", 35 | "type": "image/png" 36 | }, 37 | { 38 | "src": "/android-chrome-192x192.png", 39 | "sizes": "192x192", 40 | "type": "image/png" 41 | }, 42 | { 43 | "src": "/android-chrome-256x256.png", 44 | "sizes": "256x256", 45 | "type": "image/png" 46 | }, 47 | { 48 | "src": "/android-chrome-384x384.png", 49 | "sizes": "384x384", 50 | "type": "image/png" 51 | }, 52 | { 53 | "src": "/android-chrome-512x512.png", 54 | "sizes": "512x512", 55 | "type": "image/png" 56 | } 57 | ] 58 | } 59 | -------------------------------------------------------------------------------- /client/web/splash/img/dark-1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/splash/img/dark-1x.png -------------------------------------------------------------------------------- /client/web/splash/img/dark-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/splash/img/dark-2x.png -------------------------------------------------------------------------------- /client/web/splash/img/dark-3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/splash/img/dark-3x.png -------------------------------------------------------------------------------- /client/web/splash/img/dark-4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/splash/img/dark-4x.png -------------------------------------------------------------------------------- /client/web/splash/img/light-1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/splash/img/light-1x.png -------------------------------------------------------------------------------- /client/web/splash/img/light-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/splash/img/light-2x.png -------------------------------------------------------------------------------- /client/web/splash/img/light-3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/splash/img/light-3x.png -------------------------------------------------------------------------------- /client/web/splash/img/light-4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/web/splash/img/light-4x.png -------------------------------------------------------------------------------- /client/web/splash/style.css: -------------------------------------------------------------------------------- 1 | body, html { 2 | margin:0; 3 | height:100%; 4 | background: #cfd8dc; 5 | 6 | background-size: 100% 100%; 7 | } 8 | 9 | .center { 10 | margin: 0; 11 | position: absolute; 12 | top: 50%; 13 | left: 50%; 14 | -ms-transform: translate(-50%, -50%); 15 | transform: translate(-50%, -50%); 16 | } 17 | 18 | .contain { 19 | display:block; 20 | width:100%; height:100%; 21 | object-fit: contain; 22 | } 23 | 24 | .stretch { 25 | display:block; 26 | width:100%; height:100%; 27 | } 28 | 29 | .cover { 30 | display:block; 31 | width:100%; height:100%; 32 | object-fit: cover; 33 | } 34 | 35 | @media (prefers-color-scheme: dark) { 36 | body { 37 | margin:0; 38 | height:100%; 39 | background: #cfd8dc; 40 | 41 | background-size: 100% 100%; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /client/windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /client/windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | 12 | void RegisterPlugins(flutter::PluginRegistry* registry) { 13 | SentryFlutterPluginRegisterWithRegistrar( 14 | registry->GetRegistrarForPlugin("SentryFlutterPlugin")); 15 | UrlLauncherWindowsRegisterWithRegistrar( 16 | registry->GetRegistrarForPlugin("UrlLauncherWindows")); 17 | } 18 | -------------------------------------------------------------------------------- /client/windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /client/windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | sentry_flutter 7 | url_launcher_windows 8 | ) 9 | 10 | set(PLUGIN_BUNDLED_LIBRARIES) 11 | 12 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 13 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 14 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 15 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 16 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 17 | endforeach(plugin) 18 | -------------------------------------------------------------------------------- /client/windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | project(runner LANGUAGES CXX) 3 | 4 | add_executable(${BINARY_NAME} WIN32 5 | "flutter_window.cpp" 6 | "main.cpp" 7 | "utils.cpp" 8 | "win32_window.cpp" 9 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 10 | "Runner.rc" 11 | "runner.exe.manifest" 12 | ) 13 | apply_standard_settings(${BINARY_NAME}) 14 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 15 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 16 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 17 | add_dependencies(${BINARY_NAME} flutter_assemble) 18 | -------------------------------------------------------------------------------- /client/windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project) 8 | : project_(project) {} 9 | 10 | FlutterWindow::~FlutterWindow() {} 11 | 12 | bool FlutterWindow::OnCreate() { 13 | if (!Win32Window::OnCreate()) { 14 | return false; 15 | } 16 | 17 | RECT frame = GetClientArea(); 18 | 19 | // The size here must match the window dimensions to avoid unnecessary surface 20 | // creation / destruction in the startup path. 21 | flutter_controller_ = std::make_unique( 22 | frame.right - frame.left, frame.bottom - frame.top, project_); 23 | // Ensure that basic setup of the controller was successful. 24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 25 | return false; 26 | } 27 | RegisterPlugins(flutter_controller_->engine()); 28 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 29 | return true; 30 | } 31 | 32 | void FlutterWindow::OnDestroy() { 33 | if (flutter_controller_) { 34 | flutter_controller_ = nullptr; 35 | } 36 | 37 | Win32Window::OnDestroy(); 38 | } 39 | 40 | LRESULT 41 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 42 | WPARAM const wparam, 43 | LPARAM const lparam) noexcept { 44 | // Give Flutter, including plugins, an opportunity to handle window messages. 45 | if (flutter_controller_) { 46 | std::optional result = 47 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 48 | lparam); 49 | if (result) { 50 | return *result; 51 | } 52 | } 53 | 54 | switch (message) { 55 | case WM_FONTCHANGE: 56 | flutter_controller_->engine()->ReloadSystemFonts(); 57 | break; 58 | } 59 | 60 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 61 | } 62 | -------------------------------------------------------------------------------- /client/windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /client/windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 9 | _In_ wchar_t *command_line, _In_ int show_command) { 10 | // Attach to console when present (e.g., 'flutter run') or create a 11 | // new console when running with a debugger. 12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 13 | CreateAndAttachConsole(); 14 | } 15 | 16 | // Initialize COM, so that it is available for use in the library and/or 17 | // plugins. 18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 19 | 20 | flutter::DartProject project(L"data"); 21 | 22 | std::vector command_line_arguments = 23 | GetCommandLineArguments(); 24 | 25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 26 | 27 | FlutterWindow window(project); 28 | Win32Window::Point origin(10, 10); 29 | Win32Window::Size size(1280, 720); 30 | if (!window.CreateAndShow(L"dart_jobs", origin, size)) { 31 | return EXIT_FAILURE; 32 | } 33 | window.SetQuitOnClose(true); 34 | 35 | ::MSG msg; 36 | while (::GetMessage(&msg, nullptr, 0, 0)) { 37 | ::TranslateMessage(&msg); 38 | ::DispatchMessage(&msg); 39 | } 40 | 41 | ::CoUninitialize(); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /client/windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /client/windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlugFox/dart-jobs/c16e0e32f4fef35b60c4146e204795cbc569c3e0/client/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /client/windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /client/windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr); 51 | if (target_length == 0) { 52 | return std::string(); 53 | } 54 | std::string utf8_string; 55 | utf8_string.resize(target_length); 56 | int converted_length = ::WideCharToMultiByte( 57 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 58 | -1, utf8_string.data(), 59 | target_length, nullptr, nullptr); 60 | if (converted_length == 0) { 61 | return std::string(); 62 | } 63 | return utf8_string; 64 | } 65 | -------------------------------------------------------------------------------- /client/windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | -------------------------------------------------------------------------------- /dart-jobs.prod.compose.yml: -------------------------------------------------------------------------------- 1 | # docker compose -f ./dart-jobs.prod.compose.yml up -d site 2 | 3 | version: '3.9' 4 | 5 | services: 6 | dart-jobs-jwt-validator-firebase: 7 | hostname: 'dart-jobs-jwt-validator-firebase' 8 | container_name: 'dart-jobs-jwt-validator-firebase' 9 | image: registry.plugfox.dev/dart-jobs-jwt-validator-firebase 10 | restart: unless-stopped 11 | ports: 12 | - '888:80' 13 | build: 14 | context: ./ 15 | dockerfile: ./dockerfiles/jwt_validator_firebase.dockerfile 16 | 17 | #site: 18 | # hostname: dart-jobs-site 19 | # container_name: dart-jobs-site 20 | # image: registry.plugfox.dev/dart-jobs-site 21 | # restart: unless-stopped 22 | # ports: 23 | # - '80:80' 24 | # build: 25 | # context: ./ 26 | # dockerfile: dockerfiles/site.dockerfile 27 | # healthcheck: 28 | # test: ["CMD", "curl", "-f", "http://localhost:80"] 29 | # interval: 60s 30 | # timeout: 10s 31 | # retries: 3 32 | # deploy: 33 | # replicas: 1 34 | # resources: 35 | # reservations: 36 | # cpus: '0.10' 37 | # memory: 64M 38 | # limits: 39 | # cpus: '0.25' 40 | # memory: 256M 41 | -------------------------------------------------------------------------------- /dockerfiles/jwt_validator_firebase.dockerfile: -------------------------------------------------------------------------------- 1 | # docker build 2 | # docker pull registry.plugfox.dev/dart-jobs-jwt-validator-firebase 3 | 4 | # Base 5 | FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base 6 | WORKDIR /app 7 | EXPOSE 80/tcp 8 | 9 | # Build 10 | FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build 11 | WORKDIR /src 12 | COPY server/jwt_validator_firebase/JwtValidatorFirebase/JwtValidatorFirebase.csproj JwtValidatorFirebase/ 13 | RUN dotnet restore "JwtValidatorFirebase/JwtValidatorFirebase.csproj" 14 | COPY server/jwt_validator_firebase/ . 15 | WORKDIR "/src/JwtValidatorFirebase" 16 | RUN dotnet build "JwtValidatorFirebase.csproj" -c Release -o /app/build 17 | 18 | # Publish 19 | FROM build AS publish 20 | RUN dotnet publish "JwtValidatorFirebase.csproj" -c Release -o /app/publish 21 | 22 | # Final 23 | FROM base AS final 24 | WORKDIR /app 25 | 26 | COPY --from=publish /app/publish . 27 | ADD server/jwt_validator_firebase/JwtValidatorFirebase/dart-job-jwt-validator-firebase.json dart-job-jwt-validator-firebase.json 28 | 29 | # Add lables 30 | LABEL name="registry.plugfox.dev/dart-jobs-jwt-validator-firebase" \ 31 | vcs-url="https://github.com/PlugFox/dart-jobs" \ 32 | github="https://github.com/PlugFox/dart-jobs" \ 33 | maintainer="Plague Fox " \ 34 | authors="@plugfox" \ 35 | family="PlugFox/dart-jobs" 36 | 37 | # Start server. 38 | EXPOSE 80/tcp 39 | 40 | CMD ["dotnet", "JwtValidatorFirebase.dll"] 41 | -------------------------------------------------------------------------------- /dockerfiles/site.dockerfile: -------------------------------------------------------------------------------- 1 | # Prepare web release 2 | FROM plugfox/flutter:stable-web AS build 3 | 4 | # USER root 5 | WORKDIR /home 6 | 7 | # Copy app source code and compile it 8 | COPY --chown=101:101 client client 9 | COPY --chown=101:101 shared shared 10 | 11 | RUN set -eux; \ 12 | # Change directory to client for monorepo 13 | cd client \ 14 | # Ensure packages are still up-to-date if anything has changed 15 | && flutter pub get \ 16 | # Codegeneration 17 | && flutter pub run build_runner build --delete-conflicting-outputs --release \ 18 | # Localization 19 | #&& flutter pub global run intl_utils:generate 20 | # Build web release (opt --tree-shake-icons) 21 | && flutter build web --release --no-source-maps \ 22 | --pwa-strategy offline-first \ 23 | --web-renderer auto --base-href / 24 | 25 | # Production from scratch 26 | FROM nginx:alpine as production 27 | 28 | COPY --from=build --chown=101:101 /home/client/build/web /usr/share/nginx/html 29 | 30 | # Expose server 31 | EXPOSE 80/tcp 32 | -------------------------------------------------------------------------------- /server/jwt_validator_firebase/JwtValidatorFirebase.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31321.278 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JwtValidatorFirebase", "JwtValidatorFirebase\JwtValidatorFirebase.csproj", "{8EA0C053-39DE-4593-95B2-5227BA0C3D54}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {8EA0C053-39DE-4593-95B2-5227BA0C3D54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {8EA0C053-39DE-4593-95B2-5227BA0C3D54}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {8EA0C053-39DE-4593-95B2-5227BA0C3D54}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {8EA0C053-39DE-4593-95B2-5227BA0C3D54}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {96F8E1A9-AF40-438F-A3D9-2CE9DC245F34} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /server/jwt_validator_firebase/JwtValidatorFirebase.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True 3 | True -------------------------------------------------------------------------------- /server/jwt_validator_firebase/JwtValidatorFirebase/Controllers/HealthCheckController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.Extensions.Logging; 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | 9 | namespace JwtValidatorFirebase.Controllers 10 | { 11 | [ApiController] 12 | [Route("[controller]")] 13 | public class HealthCheckController : ControllerBase 14 | { 15 | private static readonly string[] Summaries = new[] 16 | { 17 | "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" 18 | }; 19 | 20 | private readonly ILogger _logger; 21 | 22 | public HealthCheckController(ILogger logger) 23 | { 24 | _logger = logger; 25 | } 26 | 27 | [HttpGet] 28 | public IActionResult Get() 29 | { 30 | var rng = new Random(); 31 | return Ok(Summaries[rng.Next(Summaries.Length)]); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /server/jwt_validator_firebase/JwtValidatorFirebase/Enums/HasuraRoles.cs: -------------------------------------------------------------------------------- 1 | namespace JwtValidatorFirebase.Enums 2 | { 3 | public enum HasuraRoles 4 | { 5 | Anonymous, 6 | User 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /server/jwt_validator_firebase/JwtValidatorFirebase/Interfaces/IJwtValidatorService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using JwtValidatorFirebase.Models; 3 | 4 | namespace JwtValidatorFirebase.Interfaces 5 | { 6 | public interface IJwtValidatorService 7 | { 8 | Task GenerateCustomTokenAsync(string uid); 9 | 10 | Task ValidateIdTokenAsync(string rawJwt, bool useCache = true); 11 | 12 | int GetCacheSize(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /server/jwt_validator_firebase/JwtValidatorFirebase/JwtValidatorFirebase.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /server/jwt_validator_firebase/JwtValidatorFirebase/JwtValidatorFirebase.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | ProjectDebugger 5 | 6 | 7 | JwtValidatorFirebase 8 | 9 | -------------------------------------------------------------------------------- /server/jwt_validator_firebase/JwtValidatorFirebase/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.Hosting; 4 | using Microsoft.Extensions.Logging; 5 | 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Threading.Tasks; 10 | 11 | namespace JwtValidatorFirebase 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IHostBuilder CreateHostBuilder(string[] args) => 21 | Host.CreateDefaultBuilder(args) 22 | .ConfigureWebHostDefaults(webBuilder => 23 | { 24 | webBuilder.UseStartup(); 25 | }); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /server/jwt_validator_firebase/JwtValidatorFirebase/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:34473", 7 | "sslPort": 44351 8 | } 9 | }, 10 | "$schema": "http://json.schemastore.org/launchsettings.json", 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "swagger", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "JwtValidatorFirebase": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "swagger", 24 | "environmentVariables": { 25 | "ASPNETCORE_ENVIRONMENT": "Development" 26 | }, 27 | "dotnetRunMessages": "true", 28 | "applicationUrl": "http://localhost:5000" 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /server/jwt_validator_firebase/JwtValidatorFirebase/Startup.cs: -------------------------------------------------------------------------------- 1 | using JwtValidatorFirebase.Interfaces; 2 | using JwtValidatorFirebase.Services; 3 | 4 | using Microsoft.AspNetCore.Builder; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Microsoft.Extensions.Hosting; 9 | using Microsoft.OpenApi.Models; 10 | 11 | namespace JwtValidatorFirebase 12 | { 13 | public class Startup 14 | { 15 | public Startup(IConfiguration configuration) 16 | { 17 | Configuration = configuration; 18 | } 19 | 20 | public IConfiguration Configuration { get; } 21 | 22 | // This method gets called by the runtime. Use this method to add services to the container. 23 | public void ConfigureServices(IServiceCollection services) 24 | { 25 | services.AddSingleton(); 26 | services.AddControllers(); 27 | services.AddSwaggerGen(c => 28 | { 29 | c.SwaggerDoc("v1", new OpenApiInfo { Title = "JwtValidatorFirebase", Version = "v1" }); 30 | }); 31 | } 32 | 33 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 34 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 35 | { 36 | if (env.IsDevelopment()) 37 | { 38 | app.UseDeveloperExceptionPage(); 39 | app.UseSwagger(); 40 | app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "JwtValidatorFirebase v1")); 41 | } 42 | 43 | app.UseHttpsRedirection(); 44 | 45 | app.UseRouting(); 46 | 47 | app.UseAuthorization(); 48 | 49 | app.UseEndpoints(endpoints => 50 | { 51 | endpoints.MapControllers(); 52 | }); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /server/jwt_validator_firebase/JwtValidatorFirebase/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /server/jwt_validator_firebase/JwtValidatorFirebase/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /shared/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | pubspec.lock 34 | 35 | # Web related 36 | lib/generated_plugin_registrant.dart 37 | 38 | # Symbolication related 39 | app.*.symbols 40 | 41 | # Obfuscation related 42 | app.*.map.json 43 | 44 | # Codegen 45 | #*.g.dart 46 | #*.g.java 47 | #*.g.kt 48 | #*.freezed.dart 49 | #*.gen.dart 50 | #*.moor.dart 51 | #*.mocks.dart 52 | #*.hive.dart 53 | #/lib/src/common/localization/l10n.dart 54 | #/lib/src/common/localization/intl/messages_??.dart 55 | #/lib/src/common/localization/intl/messages_???.dart 56 | 57 | # Database 58 | *.db 59 | *.sqlite 60 | *.hive 61 | *.lock 62 | 63 | # Logs 64 | /logs/ 65 | /coverage/ 66 | 67 | # Firebase 68 | #.firebase/ 69 | 70 | android/keystore.properties 71 | 72 | # SKSL shaders warm-up 73 | *.sksl.json 74 | 75 | version.env 76 | 77 | # GraphQL 78 | .graphqlconfig -------------------------------------------------------------------------------- /shared/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.0 2 | 3 | - Initial version. 4 | -------------------------------------------------------------------------------- /shared/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean format get upgrade upgrade-major outdated 2 | 3 | clean: 4 | @echo "Cleaning the project" 5 | @dart clean 6 | 7 | format: 8 | @echo "Formatting the code" 9 | @dart fix --apply . 10 | @dart format -l 120 --fix . 11 | 12 | get: 13 | @echo "Geting dependencies" 14 | @dart pub get 15 | 16 | upgrade: get 17 | @echo "Upgrading dependencies" 18 | @dart pub upgrade 19 | 20 | upgrade-major: get 21 | @echo "Upgrading dependencies --major-versions" 22 | @dart pub upgrade --major-versions 23 | 24 | outdated: 25 | @dart pub outdated 26 | 27 | codegen: get 28 | @echo "Running codegeneration" 29 | @dart run build_runner build --delete-conflicting-outputs --release 30 | @dart format -l 120 --fix lib/src/generated lib/src/model -------------------------------------------------------------------------------- /shared/README.md: -------------------------------------------------------------------------------- 1 | 13 | 14 | TODO: Put a short description of the package here that helps potential users 15 | know whether this package might be useful for them. 16 | 17 | ## Features 18 | 19 | TODO: List what your package can do. Maybe include images, gifs, or videos. 20 | 21 | ## Getting started 22 | 23 | TODO: List prerequisites and provide or point to information on how to 24 | start using the package. 25 | 26 | ## Usage 27 | 28 | TODO: Include short and useful examples for package users. Add longer examples 29 | to `/example` folder. 30 | 31 | ```dart 32 | const like = 'sample'; 33 | ``` 34 | 35 | ## Additional information 36 | 37 | TODO: Tell users more about the package: where to find more information, how to 38 | contribute to the package, how to file issues, what response they can expect 39 | from the package authors, and more. 40 | -------------------------------------------------------------------------------- /shared/lib/graphql.dart: -------------------------------------------------------------------------------- 1 | export 'package:artemis/artemis.dart' show GraphQLQuery, GraphQLResponse; 2 | export 'package:dart_jobs_shared/src/graphql/api.dart'; 3 | export 'package:dart_jobs_shared/src/graphql/client.dart' show GQLClient; 4 | export 'package:dart_jobs_shared/src/graphql/exceptions.dart'; 5 | export 'package:dart_jobs_shared/src/graphql/links/exception_link.dart'; 6 | export 'package:dart_jobs_shared/src/graphql/links/handler_link.dart'; 7 | export 'package:dart_jobs_shared/src/graphql/links/interceptor_link.dart'; 8 | export 'package:dart_jobs_shared/src/graphql/links/metadata_link.dart'; 9 | export 'package:gql_dedupe_link/gql_dedupe_link.dart' show DedupeLink; 10 | export 'package:gql_exec/gql_exec.dart' show Context, Operation, Request, GraphQLError, ContextEntry; 11 | export 'package:gql_link/gql_link.dart'; 12 | export 'package:json_annotation/json_annotation.dart' show JsonSerializable; 13 | -------------------------------------------------------------------------------- /shared/lib/model.dart: -------------------------------------------------------------------------------- 1 | library dart_jobs_shared.models; 2 | 3 | export 'package:dart_jobs_shared/src/model/country.dart'; 4 | export 'package:dart_jobs_shared/src/model/enum.dart'; 5 | export 'package:dart_jobs_shared/src/model/job.dart'; 6 | -------------------------------------------------------------------------------- /shared/lib/src/graphql/api.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | export 'api.graphql.dart'; 3 | -------------------------------------------------------------------------------- /shared/lib/src/graphql/client.dart: -------------------------------------------------------------------------------- 1 | import 'package:artemis/artemis.dart' show GraphQLQuery, GraphQLResponse; 2 | import 'package:gql_exec/gql_exec.dart' show Context, Operation, Request; 3 | import 'package:gql_link/gql_link.dart' show Link; 4 | import 'package:json_annotation/json_annotation.dart' show JsonSerializable; 5 | import 'package:meta/meta.dart' show immutable; 6 | 7 | @immutable 8 | class GQLClient { 9 | const GQLClient(final Link link) : _link = link; 10 | 11 | final Link _link; 12 | 13 | /// Executes a [GraphQLQuery], returning a typed response. 14 | /// [T] - результат ответа 15 | /// [U] - переменные 16 | Future> execute( 17 | GraphQLQuery query, { 18 | Context context = const Context(), 19 | }) async { 20 | final request = Request( 21 | operation: Operation( 22 | document: query.document, 23 | operationName: query.operationName, 24 | ), 25 | variables: query.getVariablesMap(), 26 | context: context, 27 | ); 28 | final response = await _link.request(request).first; 29 | return GraphQLResponse( 30 | data: response.data == null ? null : query.parse(response.data ?? {}), 31 | errors: response.errors, 32 | context: response.context, 33 | ); 34 | } 35 | 36 | /// Streams a [GraphQLQuery], returning a typed response stream. 37 | /// [T] - результат ответа 38 | /// [U] - переменные 39 | Stream> stream( 40 | GraphQLQuery query, { 41 | Context context = const Context(), 42 | }) { 43 | final request = Request( 44 | operation: Operation( 45 | document: query.document, 46 | operationName: query.operationName, 47 | ), 48 | variables: query.getVariablesMap(), 49 | context: context, 50 | ); 51 | return _link.request(request).map>( 52 | (response) => GraphQLResponse( 53 | data: response.data == null ? null : query.parse(response.data ?? {}), 54 | errors: response.errors, 55 | context: response.context, 56 | ), 57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /shared/lib/src/graphql/exceptions.dart: -------------------------------------------------------------------------------- 1 | import 'package:gql_exec/gql_exec.dart'; 2 | import 'package:gql_link/gql_link.dart'; 3 | import 'package:http/http.dart' as http; 4 | import 'package:meta/meta.dart'; 5 | 6 | /// Exception occurring when parsing fails. 7 | @immutable 8 | class HttpLinkParserException extends ResponseFormatException { 9 | /// Response which caused the exception 10 | final http.Response response; 11 | 12 | const HttpLinkParserException({ 13 | required dynamic originalException, 14 | required this.response, 15 | }) : super( 16 | originalException: originalException, 17 | ); 18 | } 19 | 20 | /// Exception occurring when network fails 21 | /// or parsed response is missing both `data` and `errors`. 22 | @immutable 23 | class HttpLinkServerException extends ServerException { 24 | /// Response which caused the exception 25 | final http.Response response; 26 | 27 | const HttpLinkServerException({ 28 | required this.response, 29 | required Response parsedResponse, 30 | }) : super( 31 | parsedResponse: parsedResponse, 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /shared/lib/src/graphql/links/metadata_link.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:dart_jobs_shared/src/graphql/links/handler_link.dart'; 4 | import 'package:dart_jobs_shared/src/graphql/links/interceptor_link.dart'; 5 | 6 | /// Линк отвечающий за установку метаданных в заголовки запросов GraphQL 7 | /// [metadata] - дополнительные метаданные, которыми нужно сопровождать каждый запрос 8 | class MetadataLink extends InterceptorLink { 9 | final Map? metadata; 10 | 11 | MetadataLink({this.metadata}); 12 | 13 | @override 14 | Future onRequest(Request request) => super.onRequest( 15 | request.updateContextEntry( 16 | (entry) { 17 | // ignore: prefer_const_constructors, prefer_const_literals_to_create_immutables 18 | final linkHeaders = entry ?? HttpLinkHeaders(headers: {}); 19 | return linkHeaders 20 | ..headers.addAll( 21 | { 22 | 'Meta-Device-Timestamp': (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(), 23 | ...?metadata, 24 | }, 25 | ); 26 | }, 27 | ), 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /shared/lib/src/graphql/parsers/bpchar.dart: -------------------------------------------------------------------------------- 1 | /* String <-> Bpchar */ 2 | 3 | String fromGraphQLBpcharToDartString(String bpchar) => bpchar; 4 | 5 | String fromDartStringToGraphQLBpchar(String string) => string; 6 | 7 | String? fromGraphQLBpcharNullableToDartStringNullable(String? bpchar) => bpchar; 8 | 9 | String? fromDartStringNullableToGraphQLBpcharNullable(String? string) => string; 10 | -------------------------------------------------------------------------------- /shared/lib/src/graphql/parsers/employment.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_jobs_shared/model.dart'; 2 | import 'package:dart_jobs_shared/util.dart'; 3 | 4 | /* Employment <-> employment */ 5 | 6 | Employment fromGraphQLemploymentToDartEmployment(String employment) => Employment.fromName(employment); 7 | 8 | String fromDartEmploymentToGraphQLemployment(Employment employment) => employment.name; 9 | 10 | Employment? fromGraphQLEmploymentNullableToDartEmploymentNullable(String? employment) => 11 | employment.nullOr(Employment.fromName); 12 | 13 | String? fromDartEmploymentNullableToGraphQLEmploymentNullable(Employment? employment) => employment?.name; 14 | 15 | /* List <-> _employment */ 16 | 17 | List fromGraphQL$employmentToDartListEmployment(Object employments) => 18 | GraphQLArray.toDart(employments, Employment.fromName).toList(); 19 | 20 | String fromDartListEmploymentToGraphQL$employment(List employments) => 21 | GraphQLArray.toGraphQL(employments, (e) => e.name); 22 | 23 | List? fromGraphQL$employmentNullableToDartListNullableEmployment(Object? employments) => 24 | employments.nullOr(fromGraphQL$employmentToDartListEmployment); 25 | 26 | String? fromDartListNullableEmploymentToGraphQL$employmentNullable(List? employments) => 27 | employments.nullOr(fromDartListEmploymentToGraphQL$employment); 28 | -------------------------------------------------------------------------------- /shared/lib/src/graphql/parsers/int_array.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_jobs_shared/util.dart'; 2 | 3 | /* List <-> _level */ 4 | 5 | List fromGraphQL$int4ToDartListint(Object ints) => GraphQLArray.toDart(ints, int.parse).toList(); 6 | 7 | String fromDartListintToGraphQL$int4(List ints) => GraphQLArray.toGraphQL(ints, (i) => i.toString()); 8 | -------------------------------------------------------------------------------- /shared/lib/src/graphql/parsers/level.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_jobs_shared/model.dart'; 2 | import 'package:dart_jobs_shared/util.dart'; 3 | 4 | /* DeveloperLevel <-> level */ 5 | 6 | DeveloperLevel fromGraphQLlevelToDartDeveloperLevel(String level) => DeveloperLevel.fromName(level); 7 | 8 | String fromDartDeveloperLevelToGraphQLlevel(DeveloperLevel level) => level.name; 9 | 10 | DeveloperLevel? fromGraphQLLevelNullableToDartDeveloperLevelNullable(String? level) => 11 | level.nullOr(DeveloperLevel.fromName); 12 | 13 | String? fromDartDeveloperLevelNullableToGraphQLLevelNullable(DeveloperLevel? level) => level?.name; 14 | 15 | /* List <-> _level */ 16 | 17 | List fromGraphQL$levelToDartListDeveloperLevel(Object levels) => 18 | GraphQLArray.toDart(levels, DeveloperLevel.fromName).toList(); 19 | 20 | String fromDartListDeveloperLevelToGraphQL$level(List levels) => 21 | GraphQLArray.toGraphQL(levels, (e) => e.name); 22 | 23 | List? fromGraphQL$levelNullableToDartListNullableDeveloperLevel(Object? levels) => 24 | levels.nullOr(fromGraphQL$levelToDartListDeveloperLevel); 25 | 26 | String? fromDartListNullableDeveloperLevelToGraphQL$levelNullable(List? levels) => 27 | levels.nullOr(fromDartListDeveloperLevelToGraphQL$level); 28 | -------------------------------------------------------------------------------- /shared/lib/src/graphql/parsers/relocation.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_jobs_shared/model.dart'; 2 | import 'package:dart_jobs_shared/util.dart'; 3 | 4 | /* Relocation <-> relocation */ 5 | 6 | Relocation fromGraphQLRelocationToDartRelocation(String relocation) => Relocation.fromName(relocation); 7 | 8 | String fromDartRelocationToGraphQLRelocation(Relocation relocation) => relocation.name; 9 | 10 | Relocation? fromGraphQLRelocationNullableToDartRelocationNullable(String? relocation) => 11 | relocation.nullOr(Relocation.fromName); 12 | 13 | String? fromDartRelocationNullableToGraphQLRelocationNullable(Relocation? relocation) => relocation?.name; 14 | -------------------------------------------------------------------------------- /shared/lib/src/graphql/parsers/text.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_jobs_shared/util.dart'; 2 | 3 | /* String <-> text */ 4 | 5 | String fromGraphQLtextToDartString(String text) => text; 6 | 7 | String fromDartStringToGraphQLtext(String string) => string; 8 | 9 | String? fromGraphQLtextNullableToDartStringNullable(String? text) => text; 10 | 11 | String? fromDartStringNullableToGraphQLtextNullable(String? string) => string; 12 | 13 | /* List <-> _text */ 14 | 15 | List fromGraphQL$textToDartListString(Object texts) => GraphQLArray.toDart(texts, (e) => e).toList(); 16 | 17 | String fromDartListStringToGraphQL$text(List strings) => GraphQLArray.toGraphQL(strings, (e) => e); 18 | 19 | List? fromGraphQL$textNullableToDartListNullableString(Object? texts) => 20 | texts.nullOr(fromGraphQL$textToDartListString); 21 | 22 | String? fromDartListNullableStringToGraphQL$textNullable(List? strings) => 23 | strings.nullOr(fromDartListStringToGraphQL$text); 24 | -------------------------------------------------------------------------------- /shared/lib/src/graphql/parsers/timestamp.dart: -------------------------------------------------------------------------------- 1 | /* Timestamp <-> DateTime */ 2 | 3 | DateTime fromGraphQLTimestampToDartDateTime(final String timestamp) => 4 | DateTime.parse(_normalizeTimestamp(timestamp)).toUtc(); 5 | 6 | String fromDartDateTimeToGraphQLTimestamp(final DateTime timestamp) => timestamp.toUtc().toIso8601String(); 7 | 8 | DateTime? fromGraphQLTimestampNullableToDartDateTimeNullable(final String? timestamp) => 9 | timestamp == null ? null : DateTime.tryParse(_normalizeTimestamp(timestamp))?.toUtc(); 10 | 11 | String? fromDartDateTimeNullableToGraphQLTimestampNullable(final DateTime? timestamp) => 12 | timestamp?.toUtc().toIso8601String(); 13 | 14 | String _normalizeTimestamp(String timestamp) { 15 | final time = timestamp.split('T').last; 16 | if (time.endsWith('Z') || time.contains('+') || time.contains('-')) return timestamp; 17 | return '${timestamp}Z'; 18 | } 19 | -------------------------------------------------------------------------------- /shared/lib/src/util/date_util.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | /// Namespace 4 | @sealed 5 | abstract class DateUtil { 6 | DateUtil._(); 7 | 8 | /// Дату в миллисекунды UnixTime 9 | static int toUnixTime(final DateTime dateTime) => dateTime.millisecondsSinceEpoch; 10 | 11 | /// Миллисекунды UnixTime в дату 12 | static DateTime fromUnixTime(final int millisecondsSinceEpoch) => 13 | DateTime.fromMillisecondsSinceEpoch(millisecondsSinceEpoch).toUtc(); 14 | 15 | /// Преобразовать значение JSON в Дату 16 | static DateTime fromJson(final Object date) { 17 | if (date is DateTime) { 18 | return date; 19 | } else if (date is int) { 20 | return DateTime.fromMillisecondsSinceEpoch(date).toUtc(); 21 | } else if (date is String) { 22 | return DateTime.parse(date).toUtc(); 23 | } else { 24 | throw FormatException('Invalid date type: ${date.runtimeType}'); 25 | } 26 | } 27 | 28 | /// Преобразовать Дату в значение JSON 29 | static String toJson(final DateTime date) => date.toIso8601String(); 30 | } 31 | -------------------------------------------------------------------------------- /shared/lib/src/util/graphql_array.dart: -------------------------------------------------------------------------------- 1 | /// Неймспейс позволяющий взаимодействовать с представлением массива в виде строки в GraphQL 2 | abstract class GraphQLArray { 3 | GraphQLArray._(); 4 | 5 | /// Преобразует коллекцию к представлению графкл в виде строки 6 | /// Исходные данные не должны содержать символов "{,}" 7 | static String toGraphQL( 8 | Iterable collection, [ 9 | String Function(T e) convert = _defaultGraphQLConverter, 10 | ]) => 11 | '{${collection.map(convert).join(',')}}'; 12 | 13 | /// Преобразует коллекцию графкл в виде строки к дарту 14 | /// Исходные данные не должны содержать символов "{,}" 15 | static Iterable toDart(Object collection, T Function(String e) convert) { 16 | if (collection is String) { 17 | final values = collection.trim(); 18 | return values 19 | .substring(1, values.length - 1) 20 | .split(',') 21 | .where((e) => e != 'null') 22 | .map((e) => e.trim()) 23 | .map(convert); 24 | } else if (collection is List) { 25 | return collection.cast().map(convert); 26 | } else { 27 | throw UnimplementedError('Unimplemented from GraphQL to Dart for type: ${collection.runtimeType}'); 28 | } 29 | } 30 | 31 | static String _defaultGraphQLConverter(final Object? object) => object == null ? 'null' : object.toString(); 32 | } 33 | -------------------------------------------------------------------------------- /shared/lib/src/util/money_util.dart: -------------------------------------------------------------------------------- 1 | /* 2 | import 'package:meta/meta.dart'; 3 | import 'package:money2/money2.dart'; 4 | 5 | 6 | /// Namespace 7 | @sealed 8 | abstract class MoneyUtil { 9 | MoneyUtil._(); 10 | 11 | /// Представить доллары как целое число 12 | static int usdToInt(final Money? money) => money is Money ? money.minorUnits.toInt() : 0; 13 | 14 | /// Получить доллары из целого числа 15 | static Money usdFromInt(final num? money) => money is num ? Money.fromWithCurrency(money / 100, usd) : zeroMoney; 16 | 17 | /// 0 Долларов 18 | static final Money zeroMoney = Money.fromBigIntWitCurrency(BigInt.zero, usd); 19 | 20 | /// Получить валюту USD 21 | static final Currency usd = CommonCurrencies().usd; 22 | } 23 | */ 24 | -------------------------------------------------------------------------------- /shared/lib/src/util/null_or.dart: -------------------------------------------------------------------------------- 1 | extension NullOrX on T? { 2 | R? nullOr(R? Function(T) fn) { 3 | final t = this; 4 | return t == null ? null : fn(t); 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /shared/lib/util.dart: -------------------------------------------------------------------------------- 1 | library dart_jobs_shared.util; 2 | 3 | export 'package:dart_jobs_shared/src/util/date_util.dart'; 4 | export 'package:dart_jobs_shared/src/util/graphql_array.dart'; 5 | export 'package:dart_jobs_shared/src/util/money_util.dart'; 6 | export 'package:dart_jobs_shared/src/util/null_or.dart'; 7 | -------------------------------------------------------------------------------- /shared/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: dart_jobs_shared 2 | description: Dart Jobs - shared entities 3 | publish_to: none 4 | version: 0.6.0 5 | # homepage: https://www.example.com 6 | 7 | environment: 8 | sdk: '>=2.16.0' 9 | 10 | dependencies: 11 | # Annotation & Code-generation 12 | meta: ^1.7.0 13 | json_annotation: ^4.3.0 14 | freezed_annotation: ^1.0.0 15 | equatable: ^2.0.3 16 | #built_value: ^8.1.3 17 | #built_collection: ^5.1.1 18 | 19 | # GraphQL 20 | http: ^0.13.4 21 | gql: ^0.13.1-alpha+1645425888336 22 | gql_exec: 0.3.2-alpha+1635885531651 23 | gql_link: 0.4.2-alpha+1635885531659 24 | gql_dedupe_link: 2.0.2-alpha+1635885531760 25 | artemis: ^7.3.2-beta 26 | #graphql: any 27 | #gql_http_link: ^0.4.0 28 | 29 | # Utils 30 | sentry: 6.3.0 31 | l: ^3.0.1 32 | #path: ^1.8.0 33 | #fixnum: ^1.0.0 34 | #money2: ^3.0.0-beta.6 35 | 36 | dev_dependencies: 37 | analyzer: ">=2.8.0 <4.0.0" 38 | build_runner: ^2.1.4 39 | #built_value_generator: ^8.1.3 40 | json_serializable: ^6.0.1 41 | freezed: ^1.1.0 42 | lints: ^1.0.0 43 | test: ^1.16.0 44 | 45 | #dependency_overrides: 46 | #gql_exec: 0.3.0 47 | #gql_link: 0.4.0 48 | #gql_http_link: 0.4.0 49 | #analyzer: ^2.7.0 50 | # https://github.com/gql-dart/gql/issues/289 51 | #gql_exec: 0.3.0 -------------------------------------------------------------------------------- /shared/queries/create_job.graphql: -------------------------------------------------------------------------------- 1 | mutation CreateJob( 2 | $title: String!, 3 | $company: String!, 4 | $country_code: String!, 5 | $address: String!, 6 | $remote: Boolean!, 7 | $relocation: relocation!, 8 | $english_description: String!, 9 | $russian_description: String!, 10 | $contacts: _text!, 11 | $employments: _employment!, 12 | $levels: _level, 13 | $skills: _text, 14 | ) { 15 | job_create( 16 | args: { 17 | new_title: $title, 18 | new_company: $company, 19 | new_country_code: $country_code, 20 | new_address: $address, 21 | new_remote: $remote, 22 | new_relocation: $relocation, 23 | new_employments: $employments, 24 | new_levels: $levels, 25 | new_english_description: $english_description 26 | new_russian_description: $russian_description 27 | new_skills: $skills 28 | new_contacts: $contacts 29 | } 30 | ) { 31 | id, 32 | creator_id, 33 | created, 34 | updated, 35 | deletion_mark, 36 | title, 37 | company, 38 | country_code, 39 | address, 40 | remote, 41 | relocation, 42 | employments, 43 | levels, 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /shared/queries/delete_job.graphql: -------------------------------------------------------------------------------- 1 | mutation DeleteJob( 2 | $id: Int! 3 | ) { 4 | update_job_by_pk( 5 | pk_columns: { 6 | id: $id 7 | }, 8 | _set: { 9 | deletion_mark: true, 10 | address: "", 11 | levels: "{}", 12 | employments: "{}" 13 | } 14 | ) { 15 | id, 16 | creator_id, 17 | created, 18 | updated, 19 | deletion_mark, 20 | title, 21 | company, 22 | country_code, 23 | address, 24 | remote, 25 | relocation, 26 | employments, 27 | levels 28 | } 29 | update_job_description_english_by_pk( 30 | pk_columns: { 31 | job_id: $id 32 | }, 33 | _set: { 34 | description: "" 35 | }) { 36 | description 37 | } 38 | update_job_description_russian_by_pk( 39 | pk_columns: { 40 | job_id: $id 41 | }, 42 | _set: { 43 | description: "" 44 | }) { 45 | description 46 | } 47 | update_job_contacts_by_pk( 48 | pk_columns: { 49 | job_id: $id 50 | }, 51 | _set: { 52 | contacts: "{}" 53 | }) { 54 | contacts 55 | } 56 | update_job_skills_by_pk( 57 | pk_columns: { 58 | job_id: $id 59 | }, 60 | _set: { 61 | skills: "{}" 62 | }) { 63 | skills 64 | } 65 | update_job_tags( 66 | where: { 67 | job_id: { 68 | _eq: $id 69 | } 70 | }, 71 | _set: { 72 | tag: "" 73 | }) { 74 | returning { 75 | tag 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /shared/queries/get_job.graphql: -------------------------------------------------------------------------------- 1 | query GetJob( 2 | $id: Int! 3 | ) { 4 | job_by_pk( 5 | id: $id 6 | ) { 7 | id, 8 | creator_id, 9 | created, 10 | updated, 11 | deletion_mark, 12 | title, 13 | company, 14 | country_code, 15 | address, 16 | remote, 17 | relocation, 18 | employments, 19 | levels, 20 | description_english { 21 | description 22 | }, 23 | description_russian { 24 | description 25 | }, 26 | job_skills { 27 | skills 28 | }, 29 | job_contacts { 30 | contacts 31 | }, 32 | job_tags { 33 | tag 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /shared/queries/paginate.graphql: -------------------------------------------------------------------------------- 1 | query Paginate( 2 | $before: timestamp!, 3 | $exclude: _int4!, 4 | $remote: Boolean, 5 | $country: String, 6 | $level: level, 7 | $employment: employment, 8 | $relocation: Boolean, 9 | $limit: Int! = 100 10 | ) { 11 | job_paginate( 12 | args: { 13 | filter_before: $before, 14 | filter_limit: $limit, 15 | filter_exclude: $exclude, 16 | filter_remote: $remote, 17 | filter_country_code: $country, 18 | filter_level: $level, 19 | filter_employment: $employment, 20 | filter_relocation: $relocation 21 | } 22 | ) { 23 | id, 24 | creator_id, 25 | created, 26 | updated, 27 | deletion_mark, 28 | title, 29 | company, 30 | country_code, 31 | address, 32 | remote, 33 | relocation, 34 | employments, 35 | levels 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /shared/queries/recent.graphql: -------------------------------------------------------------------------------- 1 | query Recent( 2 | $after: timestamp!, 3 | $exclude: _int4!, 4 | $remote: Boolean, 5 | $country: String, 6 | $level: level, 7 | $employment: employment, 8 | $relocation: Boolean, 9 | $limit: Int! = 100 10 | ) { 11 | job_recent( 12 | args: { 13 | filter_after: $after, 14 | filter_limit: $limit, 15 | filter_exclude: $exclude, 16 | filter_remote: $remote, 17 | filter_country_code: $country, 18 | filter_level: $level, 19 | filter_employment: $employment, 20 | filter_relocation: $relocation 21 | } 22 | ) { 23 | id, 24 | creator_id, 25 | created, 26 | updated, 27 | deletion_mark, 28 | title, 29 | company, 30 | country_code, 31 | address, 32 | remote, 33 | relocation, 34 | employments, 35 | levels 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /shared/queries/update_job.graphql: -------------------------------------------------------------------------------- 1 | mutation UpdateJob( 2 | $id: Int!, 3 | $data: job_set_input!, 4 | $description_english: String!, 5 | $description_russian: String!, 6 | $contacts: _text, 7 | $skills: _text 8 | ) { 9 | update_job_by_pk( 10 | pk_columns: { 11 | id: $id 12 | }, 13 | _set: $data 14 | ) { 15 | id, 16 | creator_id, 17 | created, 18 | updated, 19 | deletion_mark, 20 | title, 21 | company, 22 | country_code, 23 | address, 24 | remote, 25 | relocation, 26 | employments, 27 | levels 28 | } 29 | update_job_description_english_by_pk( 30 | pk_columns: { 31 | job_id: $id 32 | }, 33 | _set: { 34 | description: $description_english 35 | }) { 36 | description 37 | } 38 | update_job_description_russian_by_pk( 39 | pk_columns: { 40 | job_id: $id 41 | }, 42 | _set: { 43 | description: $description_russian 44 | }) { 45 | description 46 | } 47 | update_job_contacts_by_pk( 48 | pk_columns: { 49 | job_id: $id 50 | }, 51 | _set: { 52 | contacts: $contacts 53 | }) { 54 | contacts 55 | } 56 | update_job_skills_by_pk( 57 | pk_columns: { 58 | job_id: $id 59 | }, 60 | _set: { 61 | skills: $skills 62 | }) { 63 | skills 64 | } 65 | update_job_tags( 66 | where: { 67 | job_id: { 68 | _eq: $id 69 | } 70 | }, 71 | _set: { 72 | tag: "" 73 | }) { 74 | returning { 75 | tag 76 | } 77 | } 78 | } 79 | --------------------------------------------------------------------------------