diff --git a/android/app/build.gradle b/android/app/build.gradle
index bc059244..81f74f06 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -1,53 +1,33 @@
plugins {
id "com.android.application"
id "kotlin-android"
- // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id "dev.flutter.flutter-gradle-plugin"
}
-def localProperties = new Properties()
-def localPropertiesFile = rootProject.file("local.properties")
-if (localPropertiesFile.exists()) {
- localPropertiesFile.withReader("UTF-8") { reader ->
- localProperties.load(reader)
- }
-}
-
-def flutterVersionCode = localProperties.getProperty("flutter.versionCode")
-if (flutterVersionCode == null) {
- flutterVersionCode = "1"
-}
-
-def flutterVersionName = localProperties.getProperty("flutter.versionName")
-if (flutterVersionName == null) {
- flutterVersionName = "1.0"
-}
-
android {
namespace = "com.eatvioapp.app"
compileSdk = flutter.compileSdkVersion
- ndkVersion = "25.1.8937393"
+ ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
+ kotlinOptions {
+ jvmTarget = JavaVersion.VERSION_1_8
+ }
+
defaultConfig {
- // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.eatvioapp.app"
- // You can update the following values to match your application needs.
- // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
- minSdk = 23
+ minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
- versionCode = flutterVersionCode.toInteger()
- versionName = flutterVersionName
+ versionCode = flutter.versionCode
+ versionName = flutter.versionName
}
buildTypes {
release {
- // TODO: Add your own signing config for the release build.
- // Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.debug
}
}
@@ -56,4 +36,3 @@ android {
flutter {
source = "../.."
}
-
diff --git a/android/gradle.properties b/android/gradle.properties
index 94adc3a3..25971708 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -1,3 +1,3 @@
-org.gradle.jvmargs=-Xmx1536M
+org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
index aeaff6f8..e6b38c2d 100644
--- a/android/gradle/wrapper/gradle-wrapper.properties
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip
diff --git a/android/settings.gradle b/android/settings.gradle
index 5a6de5ce..ff583230 100644
--- a/android/settings.gradle
+++ b/android/settings.gradle
@@ -18,7 +18,7 @@ pluginManagement {
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
- id "com.android.application" version "7.3.1" apply false
+ id "com.android.application" version "8.1.4" apply false
id "org.jetbrains.kotlin.android" version "2.0.20" apply false
}
diff --git a/assets/icons/telegram.svg b/assets/icons/telegram.svg
new file mode 100644
index 00000000..3ec45ee8
--- /dev/null
+++ b/assets/icons/telegram.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/vk.svg b/assets/icons/vk.svg
new file mode 100644
index 00000000..4f20c777
--- /dev/null
+++ b/assets/icons/vk.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/web.svg b/assets/icons/web.svg
new file mode 100644
index 00000000..a3fc538c
--- /dev/null
+++ b/assets/icons/web.svg
@@ -0,0 +1,11 @@
+
diff --git a/lib/app_router.dart b/lib/app_router.dart
index e54fe2da..2f1f53e4 100644
--- a/lib/app_router.dart
+++ b/lib/app_router.dart
@@ -7,6 +7,8 @@ import 'package:eatvio_app/features/calendar/presentation/pages/calendar_screen.
import 'package:eatvio_app/features/chat/presentation/pages/chat_screen.dart';
import 'package:eatvio_app/features/chat/presentation/pages/chat_select_screen.dart';
import 'package:eatvio_app/features/chat/presentation/pages/photo_view_screen.dart';
+import 'package:eatvio_app/features/consultant/become_consultant/presentation/pages/become_consultant_screen.dart';
+import 'package:eatvio_app/features/consultant/become_consultant/presentation/pages/stepper_become_consultant_screen.dart';
import 'package:eatvio_app/features/diary/presentation/pages/diary_screen.dart';
import 'package:eatvio_app/features/diets/presentation/pages/about_diet_screen.dart';
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
@@ -23,6 +25,7 @@ import 'package:eatvio_app/features/recipes/presentation/pages/recipe_create_scr
import 'package:eatvio_app/features/recipes/presentation/pages/recipe_edit_screen.dart';
import 'package:eatvio_app/features/registration/presentation/pages/registration_screen.dart';
import 'package:eatvio_app/features/settings/entities/settings_entity.dart';
+import 'package:eatvio_app/features/settings/presentation/pages/change_email_screen.dart';
import 'package:eatvio_app/features/settings/presentation/pages/change_password_screen.dart';
import 'package:eatvio_app/features/settings/presentation/pages/general_info_screen.dart';
import 'package:eatvio_app/features/settings/presentation/pages/other_info_screen.dart';
@@ -214,6 +217,13 @@ class AppRouter {
settingsEntity: state.extra as SettingsEntity,
),
),
+ GoRoute(
+ path: 'change-email-screen',
+ name: 'changeEmailScreen',
+ builder: (context, state) => ChangeEmailScreen(
+ settingsEntity: state.extra as SettingsEntity,
+ ),
+ ),
],
),
GoRoute(
@@ -221,6 +231,16 @@ class AppRouter {
name: 'mySettingsScreen',
builder: (context, state) => MySettingsScreen.builder(context),
),
+ GoRoute(
+ path: '/become-consultant-screen',
+ name: 'becomeConsultantScreen',
+ builder: (context, state) => BecomeConsultantScreen(),
+ ),
+ GoRoute(
+ path: '/stepper-become-consultant-screen',
+ name: 'stepperBecomeConsultantScreen',
+ builder: (context, state) => StepperBecomeConsultantScreen(),
+ ),
GoRoute(
path: '/recipe-select-screen',
name: 'recipeSelectScreen',
diff --git a/lib/features/blog_post_create/presentation/pages/blog_post_create_screen.dart b/lib/features/blog_post_create/presentation/pages/blog_post_create_screen.dart
index baf87e0f..d76b7bd4 100644
--- a/lib/features/blog_post_create/presentation/pages/blog_post_create_screen.dart
+++ b/lib/features/blog_post_create/presentation/pages/blog_post_create_screen.dart
@@ -85,15 +85,13 @@ class _BlogPostCreateScreenState extends State {
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
- backgroundColor: state.image != null || state.title!.text.isNotEmpty || state.description!.text.isNotEmpty || state.youtubeURL!.text.isNotEmpty
- ? appTheme.blueA400
- : Colors.grey,
- onPressed: state.image != null || state.title!.text.isNotEmpty || state.description!.text.isNotEmpty || state.youtubeURL!.text.isNotEmpty
- ? () => _submitForm(context, state.title!.text, state.description!.text, state.youtubeURL!.text)
- : null,
+ backgroundColor: appTheme.blueA400,
+ onPressed: () => _submitForm(context, state.title!.text, state.description!.text, state.youtubeURL!.text),
label: Text(
'Создать',
- style: theme.textTheme.titleSmall,
+ style: TextStyle(
+ color: Colors.white,
+ ),
),
),
),
@@ -102,9 +100,6 @@ class _BlogPostCreateScreenState extends State {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- if (state.youtubeURL!.text.isEmpty) Text('Изображение поста'),
- if (state.youtubeURL!.text.isEmpty) SizedBox(height: 8),
- if (state.youtubeURL!.text.isEmpty) _buildAddPhoto(state),
SizedBox(height: 16),
Text('Заголовок'),
SizedBox(height: 8),
@@ -117,9 +112,13 @@ class _BlogPostCreateScreenState extends State {
CustomTextFormField(
controller: state.description,
maxLines: 6,
- minLines: 1,
+ minLines: 4,
),
SizedBox(height: 16),
+ if (state.youtubeURL!.text.isEmpty) Text('Изображение поста'),
+ if (state.youtubeURL!.text.isEmpty) SizedBox(height: 8),
+ if (state.youtubeURL!.text.isEmpty) _buildAddPhoto(state),
+ SizedBox(height: 16),
if (state.image == null) Text('Ссылка на видео'),
if (state.image == null) SizedBox(height: 8),
if (state.image == null)
@@ -184,7 +183,12 @@ class _BlogPostCreateScreenState extends State {
}
}
-_submitForm(BuildContext context, String title, String description, String youtubeURL) async {
+_submitForm(
+ BuildContext context,
+ String title,
+ String description,
+ String youtubeURL,
+) async {
context.read().add(CreateBlogPost());
}
diff --git a/lib/features/blog_post_create/services/blog_post_service.dart b/lib/features/blog_post_create/services/blog_post_service.dart
new file mode 100644
index 00000000..33524124
--- /dev/null
+++ b/lib/features/blog_post_create/services/blog_post_service.dart
@@ -0,0 +1,19 @@
+import 'package:dio/dio.dart';
+import 'package:retrofit/retrofit.dart';
+
+part 'blog_post_service.g.dart';
+
+@RestApi()
+abstract class BlogPostService {
+ factory BlogPostService(Dio dio, {String baseUrl}) = _BlogPostService;
+
+ @POST('/api/blogPosts/create')
+ @MultiPart()
+ Future sendMessage({
+ @Path() String? title,
+ @Path() required String state,
+ @Path() String? description,
+ @Part() String? youtube_url,
+ @Part(name: 'image', contentType: 'multipart/form-data') List? media_data,
+ });
+}
diff --git a/lib/features/blog_post_create/services/blog_post_service.g.dart b/lib/features/blog_post_create/services/blog_post_service.g.dart
new file mode 100644
index 00000000..fab2c34d
--- /dev/null
+++ b/lib/features/blog_post_create/services/blog_post_service.g.dart
@@ -0,0 +1,95 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'blog_post_service.dart';
+
+// **************************************************************************
+// RetrofitGenerator
+// **************************************************************************
+
+// ignore_for_file: unnecessary_brace_in_string_interps,no_leading_underscores_for_local_identifiers,unused_element
+
+class _BlogPostService implements BlogPostService {
+ _BlogPostService(
+ this._dio, {
+ this.baseUrl,
+ this.errorLogger,
+ });
+
+ final Dio _dio;
+
+ String? baseUrl;
+
+ final ParseErrorLogger? errorLogger;
+
+ @override
+ Future sendMessage({
+ String? title,
+ required String state,
+ String? description,
+ String? youtube_url,
+ List? media_data,
+ }) async {
+ final _extra = {};
+ final queryParameters = {};
+ queryParameters.removeWhere((k, v) => v == null);
+ final _headers = {};
+ final _data = FormData();
+ if (youtube_url != null) {
+ _data.fields.add(MapEntry(
+ 'youtube_url',
+ youtube_url,
+ ));
+ }
+ if (media_data != null) {
+ _data.files.addAll(media_data.map((i) => MapEntry('image', i)));
+ }
+ final _options = _setStreamType(Options(
+ method: 'POST',
+ headers: _headers,
+ extra: _extra,
+ contentType: 'multipart/form-data',
+ )
+ .compose(
+ _dio.options,
+ '/api/blogPosts/create',
+ queryParameters: queryParameters,
+ data: _data,
+ )
+ .copyWith(
+ baseUrl: _combineBaseUrls(
+ _dio.options.baseUrl,
+ baseUrl,
+ )));
+ await _dio.fetch(_options);
+ }
+
+ RequestOptions _setStreamType(RequestOptions requestOptions) {
+ if (T != dynamic &&
+ !(requestOptions.responseType == ResponseType.bytes ||
+ requestOptions.responseType == ResponseType.stream)) {
+ if (T == String) {
+ requestOptions.responseType = ResponseType.plain;
+ } else {
+ requestOptions.responseType = ResponseType.json;
+ }
+ }
+ return requestOptions;
+ }
+
+ String _combineBaseUrls(
+ String dioBaseUrl,
+ String? baseUrl,
+ ) {
+ if (baseUrl == null || baseUrl.trim().isEmpty) {
+ return dioBaseUrl;
+ }
+
+ final url = Uri.parse(baseUrl);
+
+ if (url.isAbsolute) {
+ return url.toString();
+ }
+
+ return Uri.parse(dioBaseUrl).resolveUri(url).toString();
+ }
+}
diff --git a/lib/features/chat/presentation/pages/chat_screen.dart b/lib/features/chat/presentation/pages/chat_screen.dart
index 2e96d007..ee50e04d 100644
--- a/lib/features/chat/presentation/pages/chat_screen.dart
+++ b/lib/features/chat/presentation/pages/chat_screen.dart
@@ -27,16 +27,18 @@ class ChatScreen extends StatefulWidget {
class _ChatScreenState extends State {
final scrollController = ScrollController();
final TextEditingController textEditingController = TextEditingController();
+ final ChatRoomCubit chatRoomCubit = sl();
@override
void initState() {
- scrollController.addListener(_onScroll);
super.initState();
+ scrollController.addListener(_onScroll);
+ chatRoomCubit.initChat(widget.chatId);
}
_onScroll() {
if (scrollController.offset >= scrollController.position.maxScrollExtent && !scrollController.position.outOfRange) {
- context.read().loadNextPage(widget.chatId);
+ chatRoomCubit.loadNextPage(widget.chatId);
}
}
@@ -48,8 +50,9 @@ class _ChatScreenState extends State {
Widget build(BuildContext context) {
return BlocProvider(
- create: (BuildContext context) => sl()..initChat(widget.chatId),
+ create: (BuildContext context) => chatRoomCubit,
child: BlocBuilder(
+ bloc: chatRoomCubit,
builder: (context, state) {
if (state is ChatRoomStateLoaded) {
return _buildChat(context, state);
@@ -234,10 +237,10 @@ class _ChatScreenState extends State {
if (result != null) {
List files = result.paths.map((path) => File(path!)).toList();
- context.read().sendMessage(
- chatId: widget.chatId,
- files: files,
- );
+ chatRoomCubit.sendMessage(
+ chatId: widget.chatId,
+ files: files,
+ );
} else {
// User canceled the picker
}
@@ -305,10 +308,10 @@ class _ChatScreenState extends State {
customBorder: CircleBorder(),
onTap: () {
if (textEditingController.text.isNotEmpty) {
- context.read().sendMessage(
- chatId: widget.chatId,
- data: textEditingController.text.trim(),
- );
+ chatRoomCubit.sendMessage(
+ chatId: widget.chatId,
+ data: textEditingController.text.trim(),
+ );
setState(() {
textEditingController.clear();
});
diff --git a/lib/features/chat/services/chat_service.dart b/lib/features/chat/services/chat_service.dart
index 3bbb74ff..f572b117 100644
--- a/lib/features/chat/services/chat_service.dart
+++ b/lib/features/chat/services/chat_service.dart
@@ -37,4 +37,9 @@ abstract class ChatService {
@GET('/api/profile/ws/params')
Future getWebsocket();
+
+ @GET('/api/chat/get_by_user/{user}')
+ Future getChatRoomIdByUserSlug({
+ @Path() required String user,
+ });
}
diff --git a/lib/features/chat/services/chat_service.g.dart b/lib/features/chat/services/chat_service.g.dart
index 4e84a7b3..77366053 100644
--- a/lib/features/chat/services/chat_service.g.dart
+++ b/lib/features/chat/services/chat_service.g.dart
@@ -198,6 +198,39 @@ class _ChatService implements ChatService {
return _value;
}
+ @override
+ Future getChatRoomIdByUserSlug({required String user}) async {
+ final _extra = {};
+ final queryParameters = {};
+ final _headers = {};
+ const Map? _data = null;
+ final _options = _setStreamType(Options(
+ method: 'GET',
+ headers: _headers,
+ extra: _extra,
+ )
+ .compose(
+ _dio.options,
+ '/api/chat/get_by_user/${user}',
+ queryParameters: queryParameters,
+ data: _data,
+ )
+ .copyWith(
+ baseUrl: _combineBaseUrls(
+ _dio.options.baseUrl,
+ baseUrl,
+ )));
+ final _result = await _dio.fetch