forked from eatvio/eatvio_app
feat(push notifications): test implemintation of push notifcations
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,3 +9,4 @@
|
||||
/node_modules/
|
||||
/android/app/release/app-release.aab
|
||||
/android/app/upload-keystore.jks
|
||||
/android/app/.cxx
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
plugins {
|
||||
id "com.android.application"
|
||||
// START: FlutterFire Configuration
|
||||
id 'com.google.gms.google-services'
|
||||
// END: FlutterFire Configuration
|
||||
id "kotlin-android"
|
||||
id "dev.flutter.flutter-gradle-plugin"
|
||||
}
|
||||
@@ -13,7 +16,7 @@ if (keystorePropertiesFile.exists()) {
|
||||
android {
|
||||
namespace = "com.eatvioapp.app"
|
||||
compileSdk = flutter.compileSdkVersion
|
||||
ndkVersion = flutter.ndkVersion
|
||||
ndkVersion = "27.0.12077973"
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_21
|
||||
|
||||
30
android/app/google-services.json
Normal file
30
android/app/google-services.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"project_info": {
|
||||
"project_number": "613980912922",
|
||||
"firebase_url": "https://eatvio-3467b-default-rtdb.europe-west1.firebasedatabase.app",
|
||||
"project_id": "eatvio-3467b",
|
||||
"storage_bucket": "eatvio-3467b.firebasestorage.app"
|
||||
},
|
||||
"client": [
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:613980912922:android:4de95e5f2d50ba69ecc0a3",
|
||||
"android_client_info": {
|
||||
"package_name": "com.eatvioapp.app"
|
||||
}
|
||||
},
|
||||
"oauth_client": [],
|
||||
"api_key": [
|
||||
{
|
||||
"current_key": "AIzaSyBUe1UqAAMfgMcd4hWdRoLF1Os-yn-HE9k"
|
||||
}
|
||||
],
|
||||
"services": {
|
||||
"appinvite_service": {
|
||||
"other_platform_oauth_client": []
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"configuration_version": "1"
|
||||
}
|
||||
@@ -19,6 +19,9 @@ pluginManagement {
|
||||
plugins {
|
||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||
id "com.android.application" version "8.7.3" apply false
|
||||
// START: FlutterFire Configuration
|
||||
id "com.google.gms.google-services" version "4.3.15" apply false
|
||||
// END: FlutterFire Configuration
|
||||
id "org.jetbrains.kotlin.android" version "2.0.21" apply false
|
||||
}
|
||||
|
||||
|
||||
1
firebase.json
Normal file
1
firebase.json
Normal file
@@ -0,0 +1 @@
|
||||
{"flutter":{"platforms":{"android":{"default":{"projectId":"eatvio-3467b","appId":"1:613980912922:android:4de95e5f2d50ba69ecc0a3","fileOutput":"android/app/google-services.json"}},"ios":{"default":{"projectId":"eatvio-3467b","appId":"1:613980912922:ios:c557d140a1212e63ecc0a3","uploadDebugSymbols":false,"fileOutput":"ios/Runner/GoogleService-Info.plist"}},"dart":{"lib/firebase_options.dart":{"projectId":"eatvio-3467b","configurations":{"android":"1:613980912922:android:4de95e5f2d50ba69ecc0a3","ios":"1:613980912922:ios:c557d140a1212e63ecc0a3"}}}}}}
|
||||
@@ -14,6 +14,7 @@
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||
ACF3142BB74D6F5F3C98AD82 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = EB9FE5381C3F532BD9A9456D /* GoogleService-Info.plist */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
@@ -47,6 +48,7 @@
|
||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
9D8D6181877DE17809AFF936 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||
DF0C4038A57E19D3ABA03C15 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
EB9FE5381C3F532BD9A9456D /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = "<group>"; };
|
||||
FEE92A0E2D772EA300E6E989 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@@ -91,6 +93,7 @@
|
||||
97C146EF1CF9000F007C117D /* Products */,
|
||||
6F201E010624CF963CC3A17B /* Pods */,
|
||||
AEE6B7943D197783DD1CB2EE /* Frameworks */,
|
||||
EB9FE5381C3F532BD9A9456D /* GoogleService-Info.plist */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@@ -141,6 +144,7 @@
|
||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||
1D324C14EB43C988423BC1AD /* [CP] Embed Pods Frameworks */,
|
||||
DEAA66E178B81C074317BF9A /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -194,6 +198,7 @@
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
|
||||
ACF3142BB74D6F5F3C98AD82 /* GoogleService-Info.plist in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -270,6 +275,23 @@
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
DEAA66E178B81C074317BF9A /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
@@ -363,9 +385,10 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 48;
|
||||
CURRENT_PROJECT_VERSION = 51;
|
||||
DEVELOPMENT_TEAM = VL68C36HYS;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
@@ -383,7 +406,7 @@
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.48;
|
||||
MARKETING_VERSION = 1.0.49;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.eatvioapp.app;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@@ -512,9 +535,10 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 48;
|
||||
CURRENT_PROJECT_VERSION = 51;
|
||||
DEVELOPMENT_TEAM = VL68C36HYS;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
@@ -532,7 +556,7 @@
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.48;
|
||||
MARKETING_VERSION = 1.0.49;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.eatvioapp.app;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@@ -549,9 +573,10 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 48;
|
||||
CURRENT_PROJECT_VERSION = 51;
|
||||
DEVELOPMENT_TEAM = VL68C36HYS;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
@@ -569,7 +594,7 @@
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.48;
|
||||
MARKETING_VERSION = 1.0.49;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.eatvioapp.app;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
||||
32
ios/Runner/GoogleService-Info.plist
Normal file
32
ios/Runner/GoogleService-Info.plist
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>API_KEY</key>
|
||||
<string>AIzaSyBnE8tQjL9_1HuXrTnqNy6qbXpoenO36yQ</string>
|
||||
<key>GCM_SENDER_ID</key>
|
||||
<string>613980912922</string>
|
||||
<key>PLIST_VERSION</key>
|
||||
<string>1</string>
|
||||
<key>BUNDLE_ID</key>
|
||||
<string>com.eatvioapp.app</string>
|
||||
<key>PROJECT_ID</key>
|
||||
<string>eatvio-3467b</string>
|
||||
<key>STORAGE_BUCKET</key>
|
||||
<string>eatvio-3467b.firebasestorage.app</string>
|
||||
<key>IS_ADS_ENABLED</key>
|
||||
<false></false>
|
||||
<key>IS_ANALYTICS_ENABLED</key>
|
||||
<false></false>
|
||||
<key>IS_APPINVITE_ENABLED</key>
|
||||
<true></true>
|
||||
<key>IS_GCM_ENABLED</key>
|
||||
<true></true>
|
||||
<key>IS_SIGNIN_ENABLED</key>
|
||||
<true></true>
|
||||
<key>GOOGLE_APP_ID</key>
|
||||
<string>1:613980912922:ios:c557d140a1212e63ecc0a3</string>
|
||||
<key>DATABASE_URL</key>
|
||||
<string>https://eatvio-3467b-default-rtdb.europe-west1.firebasedatabase.app</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -2,8 +2,6 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
@@ -24,6 +22,8 @@
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
@@ -42,6 +42,7 @@
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>fetch</string>
|
||||
<string>remote-notification</string>
|
||||
</array>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>aps-environment</key>
|
||||
<string>disabled</string>
|
||||
<string>development</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
String baseUrl = 'https://eatvio.ru';
|
||||
// String baseUrl = 'https://dev2.eatvio.com';
|
||||
// String baseUrl = 'https://eatvio.ru';
|
||||
String baseUrl = 'https://dev2.eatvio.com';
|
||||
|
||||
const List<int> durationValues = [7, 14, 21];
|
||||
|
||||
|
||||
11
lib/core/firebase_api.dart
Normal file
11
lib/core/firebase_api.dart
Normal file
@@ -0,0 +1,11 @@
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
|
||||
class FirebaseApi {
|
||||
|
||||
final _firebaseMessaging = FirebaseMessaging.instance;
|
||||
|
||||
Future<String?> getToken() async{
|
||||
await _firebaseMessaging.requestPermission();
|
||||
return await _firebaseMessaging.getToken();
|
||||
}
|
||||
}
|
||||
@@ -94,13 +94,13 @@ Future<void> init() async {
|
||||
|
||||
// Cubit factories
|
||||
sl.registerFactory<RegistrationCubit>(() => RegistrationCubit(registrationRepository: sl()));
|
||||
sl.registerFactory<ChatRoomCubit>(() => ChatRoomCubit(chatRepository: sl(), wsManager: sl()));
|
||||
sl.registerFactory<ChatRoomCubit>(() => ChatRoomCubit(cardsRepository: sl(), chatRepository: sl(), wsManager: sl()));
|
||||
sl.registerFactory<RecipesCubit>(() => RecipesCubit(feedRepository: sl(), recipeRepository: sl(), aboutRecipeRepository: sl()));
|
||||
sl.registerFactory<CalendarCubit>(() => CalendarCubit(calendarRepository: sl()));
|
||||
sl.registerFactory<ProfileCubit>(() => ProfileCubit(profileRepository: sl(), chatRepository: sl()));
|
||||
sl.registerFactory<profilePlans.PlansCubit>(() => profilePlans.PlansCubit(plansRepository: sl()));
|
||||
sl.registerFactory<DietsCubit>(() => DietsCubit(feedRepository: sl(), dietsRepository: sl()));
|
||||
sl.registerFactory<CommentsCubit>(() => CommentsCubit(feedRepository: sl()));
|
||||
sl.registerFactory<CommentsCubit>(() => CommentsCubit(feedRepository: sl()));
|
||||
sl.registerFactory<PlansCubit>(() => PlansCubit(feedRepository: sl(), planRepository: sl()));
|
||||
sl.registerFactory<SettingsCubit>(() => SettingsCubit(profileRepository: sl(), settingsRepository: sl()));
|
||||
sl.registerFactory<RegisterConsultantCubit>(() => RegisterConsultantCubit(consultantRepository: sl()));
|
||||
@@ -171,15 +171,7 @@ Future<void> init() async {
|
||||
|
||||
// External
|
||||
sl.registerLazySingleton(
|
||||
() => Dio(
|
||||
BaseOptions(
|
||||
baseUrl: baseUrl,
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
),
|
||||
)..interceptors.add(TokenInterceptor()),
|
||||
() => Dio(BaseOptions(baseUrl: baseUrl, headers: {'accept': 'application/json', 'content-type': 'application/json'}))..interceptors.add(TokenInterceptor()),
|
||||
);
|
||||
sl.registerLazySingleton(() async => await FlutterSecureStorage());
|
||||
}
|
||||
|
||||
@@ -25,43 +25,33 @@ class _DataErrorWidgetState extends State<DataErrorWidget> {
|
||||
children: [
|
||||
SvgPicture.asset('assets/icons/cloud.svg'),
|
||||
SizedBox(height: 30),
|
||||
Text(
|
||||
s.something_went_wrong,
|
||||
style: TextStyles.textLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
Text(s.something_went_wrong, style: TextStyles.textLSemibold.copyWith(color: appTheme.black)),
|
||||
SizedBox(height: 12),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||
child: Text(
|
||||
s.can_not_load_data_please_try_again,
|
||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
child: Text(s.can_not_load_data_please_try_again, style: TextStyles.textSMedium.copyWith(color: appTheme.black), textAlign: TextAlign.center),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
isLoading
|
||||
? Loader()
|
||||
: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: CustomOutlinedButton(
|
||||
text: s.try_again,
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
|
||||
buttonStyle: ButtonStyle(
|
||||
side: WidgetStatePropertyAll(BorderSide.none),
|
||||
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
|
||||
),
|
||||
onPressed: () async {
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
await widget.onReload();
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
},
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: CustomOutlinedButton(
|
||||
text: s.try_again,
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
|
||||
buttonStyle: ButtonStyle(side: WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.blueA400)),
|
||||
onPressed: () async {
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
await widget.onReload();
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,10 +11,13 @@ import 'package:eatvio_app/core/websockets/ws_manager.dart';
|
||||
import 'package:eatvio_app/features/chat/models/chat_message_model.dart';
|
||||
import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_state.dart';
|
||||
import 'package:eatvio_app/features/chat/repositories/chat_repository.dart';
|
||||
import 'package:eatvio_app/features/my_finance/models/card_model.dart';
|
||||
import 'package:eatvio_app/features/my_finance/repositories/cards_repository.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class ChatRoomCubit extends Cubit<ChatRoomState> {
|
||||
final ChatRepository chatRepository;
|
||||
final CardsRepository cardsRepository;
|
||||
final WsManager wsManager;
|
||||
|
||||
StreamSubscription<ChannelReadEvent>? typingSubscription;
|
||||
@@ -23,34 +26,22 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
|
||||
late final int lastPage;
|
||||
bool isLoading = false;
|
||||
|
||||
ChatRoomCubit({
|
||||
required this.chatRepository,
|
||||
required this.wsManager,
|
||||
}) : super(ChatRoomState.loading());
|
||||
ChatRoomCubit({required this.chatRepository, required this.wsManager, required this.cardsRepository}) : super(ChatRoomState.loading());
|
||||
|
||||
loadNextPage(String chatId) async {
|
||||
if (currentPage < lastPage && !isLoading) {
|
||||
isLoading = true;
|
||||
|
||||
emit(
|
||||
ChatRoomState.loaded(
|
||||
chatRoom: (state as ChatRoomStateLoaded).chatRoom,
|
||||
chatMessages: (state as ChatRoomStateLoaded).chatMessages,
|
||||
isTyping: (state as ChatRoomStateLoaded).isTyping,
|
||||
isLoading: isLoading,
|
||||
),
|
||||
);
|
||||
emit((state as ChatRoomStateLoaded).copyWith(isLoading: isLoading));
|
||||
|
||||
try {
|
||||
final chatMessages = await chatRepository.getChatMessages(chatId, currentPage + 1);
|
||||
|
||||
isLoading = false;
|
||||
emit(
|
||||
ChatRoomState.loaded(
|
||||
chatRoom: (state as ChatRoomStateLoaded).chatRoom,
|
||||
chatMessages: [...(state as ChatRoomStateLoaded).chatMessages, ...chatMessages.messagesList],
|
||||
isTyping: (state as ChatRoomStateLoaded).isTyping,
|
||||
(state as ChatRoomStateLoaded).copyWith(
|
||||
isLoading: isLoading,
|
||||
chatMessages: [...(state as ChatRoomStateLoaded).chatMessages, ...chatMessages.messagesList],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -61,6 +52,24 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> checkCards() async {
|
||||
try {
|
||||
final cards = await cardsRepository.getCards();
|
||||
setCard(cards: cards);
|
||||
} catch (e) {
|
||||
emit(ChatRoomState.error(errorMessage: 'Ошибка получеия данных'));
|
||||
}
|
||||
}
|
||||
|
||||
void setCard({required List<CardModel> cards}) {
|
||||
final currentState = state;
|
||||
if (currentState is ChatRoomStateLoaded) {
|
||||
if (cards.where((card) => card.isDefault).isNotEmpty) {
|
||||
emit(currentState.copyWith(hasCard: true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initChat(String chatId) async {
|
||||
try {
|
||||
final chatRoom = await chatRepository.getChatRoom(chatId);
|
||||
@@ -70,14 +79,9 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
|
||||
|
||||
await _subscribeToChatChannel(chatId: chatId);
|
||||
|
||||
emit(
|
||||
ChatRoomState.loaded(
|
||||
chatRoom: chatRoom,
|
||||
chatMessages: chatMessages.messagesList,
|
||||
isTyping: false,
|
||||
isLoading: isLoading,
|
||||
),
|
||||
);
|
||||
emit(ChatRoomState.loaded(chatRoom: chatRoom, chatMessages: chatMessages.messagesList, isTyping: false, isLoading: isLoading, hasCard: false));
|
||||
final cards = await cardsRepository.getCards();
|
||||
setCard(cards: cards);
|
||||
} catch (e) {
|
||||
typingSubscription?.cancel();
|
||||
messagesSubscription?.cancel();
|
||||
@@ -90,17 +94,13 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
|
||||
try {
|
||||
await chatRepository.cancelChosenPlan(id: planId);
|
||||
final chatRoom = await chatRepository.getChatRoom(chatId);
|
||||
emit(
|
||||
(state as ChatRoomStateLoaded).copyWith(chatRoom: chatRoom),
|
||||
);
|
||||
emit((state as ChatRoomStateLoaded).copyWith(chatRoom: chatRoom));
|
||||
} catch (e) {
|
||||
emit(ChatRoomState.error(errorMessage: 'Ошибка получеия данных'));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _subscribeToChatChannel({
|
||||
required String chatId,
|
||||
}) async {
|
||||
Future<void> _subscribeToChatChannel({required String chatId}) async {
|
||||
final client = wsManager.client;
|
||||
if (client == null) return;
|
||||
await wsManager.initPrivateDirectChatChannel(chatId);
|
||||
@@ -108,56 +108,20 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
|
||||
messagesSubscription = wsManager.directChatStream?.listen((event) {
|
||||
AudioPlayer().play((AssetSource('audio/recive_message.wav')));
|
||||
final message = ChatMessageModel.fromJson(jsonDecode(event.data)['message']);
|
||||
List<ChatMessageModel> messagesList = [
|
||||
message,
|
||||
...(state as ChatRoomStateLoaded).chatMessages,
|
||||
];
|
||||
List<ChatMessageModel> messagesList = [message, ...(state as ChatRoomStateLoaded).chatMessages];
|
||||
chatRepository.emitNewMessageToChats(message);
|
||||
emit(
|
||||
ChatRoomState.loaded(
|
||||
chatRoom: (state as ChatRoomStateLoaded).chatRoom,
|
||||
chatMessages: messagesList,
|
||||
isTyping: (state as ChatRoomStateLoaded).isTyping,
|
||||
isLoading: isLoading,
|
||||
),
|
||||
);
|
||||
emit((state as ChatRoomStateLoaded).copyWith(chatMessages: messagesList, isLoading: isLoading));
|
||||
});
|
||||
|
||||
typingSubscription = wsManager.clientTypingStream?.listen((event) {
|
||||
emit(
|
||||
ChatRoomState.loaded(
|
||||
chatRoom: (state as ChatRoomStateLoaded).chatRoom,
|
||||
chatMessages: (state as ChatRoomStateLoaded).chatMessages,
|
||||
isTyping: true,
|
||||
isLoading: isLoading,
|
||||
),
|
||||
);
|
||||
EasyDebounce.debounce(
|
||||
'client-typing',
|
||||
Duration(seconds: 3),
|
||||
() => emit(
|
||||
ChatRoomState.loaded(
|
||||
chatRoom: (state as ChatRoomStateLoaded).chatRoom,
|
||||
chatMessages: (state as ChatRoomStateLoaded).chatMessages,
|
||||
isTyping: false,
|
||||
isLoading: isLoading,
|
||||
),
|
||||
),
|
||||
);
|
||||
emit((state as ChatRoomStateLoaded).copyWith(isTyping: true, isLoading: isLoading));
|
||||
EasyDebounce.debounce('client-typing', Duration(seconds: 3), () => emit((state as ChatRoomStateLoaded).copyWith(isTyping: false, isLoading: isLoading)));
|
||||
});
|
||||
}
|
||||
|
||||
Future<bool> sendMessage({
|
||||
required String chatId,
|
||||
String? data,
|
||||
List<File>? files,
|
||||
}) async {
|
||||
Future<bool> sendMessage({required String chatId, String? data, List<File>? files}) async {
|
||||
try {
|
||||
await chatRepository.sendMessage(
|
||||
chatId: chatId,
|
||||
data: data,
|
||||
files: files != null ? await ImageCompressUtility.compressFilesList(files) : null,
|
||||
);
|
||||
await chatRepository.sendMessage(chatId: chatId, data: data, files: files != null ? await ImageCompressUtility.compressFilesList(files) : null);
|
||||
AudioPlayer().play((AssetSource('audio/send_message.wav')));
|
||||
return true;
|
||||
} catch (e, st) {
|
||||
@@ -176,9 +140,7 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
|
||||
currentMessages[elem.index] = currentMessages[elem.index].copyWith(isRead: true);
|
||||
});
|
||||
if (!isClosed) {
|
||||
emit(
|
||||
(state as ChatRoomStateLoaded).copyWith(chatMessages: currentMessages),
|
||||
);
|
||||
emit((state as ChatRoomStateLoaded).copyWith(chatMessages: currentMessages));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ abstract class ChatRoomState with _$ChatRoomState {
|
||||
const factory ChatRoomState.loaded({
|
||||
required ChatRoomModel chatRoom,
|
||||
required List<ChatMessageModel> chatMessages,
|
||||
required bool hasCard,
|
||||
required bool isTyping,
|
||||
required bool isLoading,
|
||||
}) = ChatRoomStateLoaded;
|
||||
|
||||
@@ -24,6 +24,7 @@ mixin _$ChatRoomState {
|
||||
required TResult Function(
|
||||
ChatRoomModel chatRoom,
|
||||
List<ChatMessageModel> chatMessages,
|
||||
bool hasCard,
|
||||
bool isTyping,
|
||||
bool isLoading,
|
||||
)
|
||||
@@ -37,6 +38,7 @@ mixin _$ChatRoomState {
|
||||
TResult? Function(
|
||||
ChatRoomModel chatRoom,
|
||||
List<ChatMessageModel> chatMessages,
|
||||
bool hasCard,
|
||||
bool isTyping,
|
||||
bool isLoading,
|
||||
)?
|
||||
@@ -50,6 +52,7 @@ mixin _$ChatRoomState {
|
||||
TResult Function(
|
||||
ChatRoomModel chatRoom,
|
||||
List<ChatMessageModel> chatMessages,
|
||||
bool hasCard,
|
||||
bool isTyping,
|
||||
bool isLoading,
|
||||
)?
|
||||
@@ -151,6 +154,7 @@ class _$ChatRoomStateImpl implements _ChatRoomState {
|
||||
required TResult Function(
|
||||
ChatRoomModel chatRoom,
|
||||
List<ChatMessageModel> chatMessages,
|
||||
bool hasCard,
|
||||
bool isTyping,
|
||||
bool isLoading,
|
||||
)
|
||||
@@ -168,6 +172,7 @@ class _$ChatRoomStateImpl implements _ChatRoomState {
|
||||
TResult? Function(
|
||||
ChatRoomModel chatRoom,
|
||||
List<ChatMessageModel> chatMessages,
|
||||
bool hasCard,
|
||||
bool isTyping,
|
||||
bool isLoading,
|
||||
)?
|
||||
@@ -185,6 +190,7 @@ class _$ChatRoomStateImpl implements _ChatRoomState {
|
||||
TResult Function(
|
||||
ChatRoomModel chatRoom,
|
||||
List<ChatMessageModel> chatMessages,
|
||||
bool hasCard,
|
||||
bool isTyping,
|
||||
bool isLoading,
|
||||
)?
|
||||
@@ -289,6 +295,7 @@ class _$ChatRoomStateLoadingImpl implements ChatRoomStateLoading {
|
||||
required TResult Function(
|
||||
ChatRoomModel chatRoom,
|
||||
List<ChatMessageModel> chatMessages,
|
||||
bool hasCard,
|
||||
bool isTyping,
|
||||
bool isLoading,
|
||||
)
|
||||
@@ -306,6 +313,7 @@ class _$ChatRoomStateLoadingImpl implements ChatRoomStateLoading {
|
||||
TResult? Function(
|
||||
ChatRoomModel chatRoom,
|
||||
List<ChatMessageModel> chatMessages,
|
||||
bool hasCard,
|
||||
bool isTyping,
|
||||
bool isLoading,
|
||||
)?
|
||||
@@ -323,6 +331,7 @@ class _$ChatRoomStateLoadingImpl implements ChatRoomStateLoading {
|
||||
TResult Function(
|
||||
ChatRoomModel chatRoom,
|
||||
List<ChatMessageModel> chatMessages,
|
||||
bool hasCard,
|
||||
bool isTyping,
|
||||
bool isLoading,
|
||||
)?
|
||||
@@ -388,6 +397,7 @@ abstract class _$$ChatRoomStateLoadedImplCopyWith<$Res> {
|
||||
$Res call({
|
||||
ChatRoomModel chatRoom,
|
||||
List<ChatMessageModel> chatMessages,
|
||||
bool hasCard,
|
||||
bool isTyping,
|
||||
bool isLoading,
|
||||
});
|
||||
@@ -411,6 +421,7 @@ class __$$ChatRoomStateLoadedImplCopyWithImpl<$Res>
|
||||
$Res call({
|
||||
Object? chatRoom = null,
|
||||
Object? chatMessages = null,
|
||||
Object? hasCard = null,
|
||||
Object? isTyping = null,
|
||||
Object? isLoading = null,
|
||||
}) {
|
||||
@@ -426,6 +437,11 @@ class __$$ChatRoomStateLoadedImplCopyWithImpl<$Res>
|
||||
? _value._chatMessages
|
||||
: chatMessages // ignore: cast_nullable_to_non_nullable
|
||||
as List<ChatMessageModel>,
|
||||
hasCard:
|
||||
null == hasCard
|
||||
? _value.hasCard
|
||||
: hasCard // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
isTyping:
|
||||
null == isTyping
|
||||
? _value.isTyping
|
||||
@@ -457,6 +473,7 @@ class _$ChatRoomStateLoadedImpl implements ChatRoomStateLoaded {
|
||||
const _$ChatRoomStateLoadedImpl({
|
||||
required this.chatRoom,
|
||||
required final List<ChatMessageModel> chatMessages,
|
||||
required this.hasCard,
|
||||
required this.isTyping,
|
||||
required this.isLoading,
|
||||
}) : _chatMessages = chatMessages;
|
||||
@@ -471,6 +488,8 @@ class _$ChatRoomStateLoadedImpl implements ChatRoomStateLoaded {
|
||||
return EqualUnmodifiableListView(_chatMessages);
|
||||
}
|
||||
|
||||
@override
|
||||
final bool hasCard;
|
||||
@override
|
||||
final bool isTyping;
|
||||
@override
|
||||
@@ -478,7 +497,7 @@ class _$ChatRoomStateLoadedImpl implements ChatRoomStateLoaded {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ChatRoomState.loaded(chatRoom: $chatRoom, chatMessages: $chatMessages, isTyping: $isTyping, isLoading: $isLoading)';
|
||||
return 'ChatRoomState.loaded(chatRoom: $chatRoom, chatMessages: $chatMessages, hasCard: $hasCard, isTyping: $isTyping, isLoading: $isLoading)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -492,6 +511,7 @@ class _$ChatRoomStateLoadedImpl implements ChatRoomStateLoaded {
|
||||
other._chatMessages,
|
||||
_chatMessages,
|
||||
) &&
|
||||
(identical(other.hasCard, hasCard) || other.hasCard == hasCard) &&
|
||||
(identical(other.isTyping, isTyping) ||
|
||||
other.isTyping == isTyping) &&
|
||||
(identical(other.isLoading, isLoading) ||
|
||||
@@ -503,6 +523,7 @@ class _$ChatRoomStateLoadedImpl implements ChatRoomStateLoaded {
|
||||
runtimeType,
|
||||
chatRoom,
|
||||
const DeepCollectionEquality().hash(_chatMessages),
|
||||
hasCard,
|
||||
isTyping,
|
||||
isLoading,
|
||||
);
|
||||
@@ -526,13 +547,14 @@ class _$ChatRoomStateLoadedImpl implements ChatRoomStateLoaded {
|
||||
required TResult Function(
|
||||
ChatRoomModel chatRoom,
|
||||
List<ChatMessageModel> chatMessages,
|
||||
bool hasCard,
|
||||
bool isTyping,
|
||||
bool isLoading,
|
||||
)
|
||||
loaded,
|
||||
required TResult Function(String errorMessage) error,
|
||||
}) {
|
||||
return loaded(chatRoom, chatMessages, isTyping, isLoading);
|
||||
return loaded(chatRoom, chatMessages, hasCard, isTyping, isLoading);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -543,13 +565,14 @@ class _$ChatRoomStateLoadedImpl implements ChatRoomStateLoaded {
|
||||
TResult? Function(
|
||||
ChatRoomModel chatRoom,
|
||||
List<ChatMessageModel> chatMessages,
|
||||
bool hasCard,
|
||||
bool isTyping,
|
||||
bool isLoading,
|
||||
)?
|
||||
loaded,
|
||||
TResult? Function(String errorMessage)? error,
|
||||
}) {
|
||||
return loaded?.call(chatRoom, chatMessages, isTyping, isLoading);
|
||||
return loaded?.call(chatRoom, chatMessages, hasCard, isTyping, isLoading);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -560,6 +583,7 @@ class _$ChatRoomStateLoadedImpl implements ChatRoomStateLoaded {
|
||||
TResult Function(
|
||||
ChatRoomModel chatRoom,
|
||||
List<ChatMessageModel> chatMessages,
|
||||
bool hasCard,
|
||||
bool isTyping,
|
||||
bool isLoading,
|
||||
)?
|
||||
@@ -568,7 +592,7 @@ class _$ChatRoomStateLoadedImpl implements ChatRoomStateLoaded {
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (loaded != null) {
|
||||
return loaded(chatRoom, chatMessages, isTyping, isLoading);
|
||||
return loaded(chatRoom, chatMessages, hasCard, isTyping, isLoading);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
@@ -615,12 +639,14 @@ abstract class ChatRoomStateLoaded implements ChatRoomState {
|
||||
const factory ChatRoomStateLoaded({
|
||||
required final ChatRoomModel chatRoom,
|
||||
required final List<ChatMessageModel> chatMessages,
|
||||
required final bool hasCard,
|
||||
required final bool isTyping,
|
||||
required final bool isLoading,
|
||||
}) = _$ChatRoomStateLoadedImpl;
|
||||
|
||||
ChatRoomModel get chatRoom;
|
||||
List<ChatMessageModel> get chatMessages;
|
||||
bool get hasCard;
|
||||
bool get isTyping;
|
||||
bool get isLoading;
|
||||
|
||||
@@ -711,6 +737,7 @@ class _$ChatRoomStateErrorImpl implements ChatRoomStateError {
|
||||
required TResult Function(
|
||||
ChatRoomModel chatRoom,
|
||||
List<ChatMessageModel> chatMessages,
|
||||
bool hasCard,
|
||||
bool isTyping,
|
||||
bool isLoading,
|
||||
)
|
||||
@@ -728,6 +755,7 @@ class _$ChatRoomStateErrorImpl implements ChatRoomStateError {
|
||||
TResult? Function(
|
||||
ChatRoomModel chatRoom,
|
||||
List<ChatMessageModel> chatMessages,
|
||||
bool hasCard,
|
||||
bool isTyping,
|
||||
bool isLoading,
|
||||
)?
|
||||
@@ -745,6 +773,7 @@ class _$ChatRoomStateErrorImpl implements ChatRoomStateError {
|
||||
TResult Function(
|
||||
ChatRoomModel chatRoom,
|
||||
List<ChatMessageModel> chatMessages,
|
||||
bool hasCard,
|
||||
bool isTyping,
|
||||
bool isLoading,
|
||||
)?
|
||||
|
||||
@@ -13,6 +13,7 @@ import 'package:eatvio_app/features/chat/presentation/widgets/chat_bubble_widget
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/features/chat/presentation/widgets/notification_banner.dart';
|
||||
import 'package:eatvio_app/features/chat/presentation/widgets/order_card.dart';
|
||||
import 'package:eatvio_app/features/plans/enums/chosen_plan_state_enum.dart';
|
||||
import 'package:eatvio_app/features/user_details/presentation/user_details_screen.dart';
|
||||
@@ -26,10 +27,7 @@ import 'package:visibility_detector/visibility_detector.dart';
|
||||
|
||||
class ChatScreen extends StatefulWidget {
|
||||
final String chatId;
|
||||
const ChatScreen({
|
||||
required this.chatId,
|
||||
super.key,
|
||||
});
|
||||
const ChatScreen({required this.chatId, super.key});
|
||||
|
||||
@override
|
||||
State<ChatScreen> createState() => _ChatScreenState();
|
||||
@@ -72,14 +70,9 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
if (state is ChatRoomStateLoaded) {
|
||||
return _buildChat(context, state);
|
||||
} else if (state is ChatRoomStateError) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(),
|
||||
body: DataErrorWidget(onReload: () => chatRoomCubit.initChat(widget.chatId)),
|
||||
);
|
||||
return Scaffold(appBar: AppBar(), body: DataErrorWidget(onReload: () => chatRoomCubit.initChat(widget.chatId)));
|
||||
} else {
|
||||
return Scaffold(
|
||||
body: const Loader(),
|
||||
);
|
||||
return Scaffold(body: const Loader());
|
||||
}
|
||||
},
|
||||
),
|
||||
@@ -90,6 +83,14 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
final chosenPlan = state.chatRoom.chosenPlan;
|
||||
final amIConsultant = chosenPlan?.consultantId == state.chatRoom.users.sender.id;
|
||||
final showAboutOrderButton = showableChatBadgeStates.contains(chosenPlan?.chosenPlanState);
|
||||
BannerType? bannerType;
|
||||
if (amIConsultant && !state.hasCard) {
|
||||
bannerType = BannerType.card;
|
||||
} else if (amIConsultant && chosenPlan?.chosenPlanState == ChosenPlanStateEnum.stateInCoordination) {
|
||||
bannerType = BannerType.date;
|
||||
} else if (!amIConsultant && chosenPlan?.chosenPlanState == ChosenPlanStateEnum.stateStartDateHasBeenSet) {
|
||||
bannerType = BannerType.payment;
|
||||
}
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: true,
|
||||
appBar: AppBar(
|
||||
@@ -99,43 +100,25 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
title: Row(
|
||||
children: [
|
||||
if (state.chatRoom.users.receiver.profilePhotoUrl != null)
|
||||
CircleAvatar(
|
||||
radius: 20,
|
||||
foregroundImage: NetworkImage(state.chatRoom.users.receiver.profilePhotoUrl!),
|
||||
),
|
||||
CircleAvatar(radius: 20, foregroundImage: NetworkImage(state.chatRoom.users.receiver.profilePhotoUrl!)),
|
||||
if (state.chatRoom.users.receiver.profilePhotoUrl == null)
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFEBF4FF),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
decoration: BoxDecoration(color: Color(0xFFEBF4FF), shape: BoxShape.circle),
|
||||
child: Text(
|
||||
state.chatRoom.users.receiver.name.split(' ').first[0] + state.chatRoom.users.receiver.name.split(' ')[1][0],
|
||||
style: TextStyle(
|
||||
color: Color(0xFF7F9CF5),
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 20,
|
||||
),
|
||||
style: TextStyle(color: Color(0xFF7F9CF5), fontWeight: FontWeight.w400, fontSize: 20),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 16),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
state.chatRoom.users.receiver.name,
|
||||
style: CustomTextStyles.titleSmallBold.copyWith(
|
||||
color: PrimaryColors().black,
|
||||
),
|
||||
),
|
||||
Text(state.chatRoom.users.receiver.name, style: CustomTextStyles.titleSmallBold.copyWith(color: PrimaryColors().black)),
|
||||
AnimatedCrossFade(
|
||||
firstChild: Text(
|
||||
'Печатает...',
|
||||
style: CustomTextStyles.bodySmallMulishPrimary,
|
||||
),
|
||||
firstChild: Text('Печатает...', style: CustomTextStyles.bodySmallMulishPrimary),
|
||||
firstCurve: Curves.easeIn,
|
||||
secondCurve: Curves.easeOut,
|
||||
secondChild: const SizedBox.shrink(),
|
||||
@@ -170,10 +153,7 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (context, index) {
|
||||
if (index == state.chatMessages.length) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: const Loader(),
|
||||
);
|
||||
return Padding(padding: const EdgeInsets.all(8), child: const Loader());
|
||||
}
|
||||
|
||||
if ((index + 1 != state.chatMessages.length &&
|
||||
@@ -188,18 +168,15 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
onVisibilityChanged: (VisibilityInfo info) {
|
||||
if (!state.chatMessages[index].isRead && !isCurrentUserAuthor && info.visibleFraction > 0) {
|
||||
unreadMessagesIdsCopy.add((id: state.chatMessages[index].id, index: index));
|
||||
EasyDebounce.debounce(
|
||||
'mark_as_read',
|
||||
Duration(milliseconds: 500),
|
||||
() async {
|
||||
final result = await context.read<ChatRoomCubit>().markMessagesAsRead(state.chatRoom.id, unreadMessagesIdsCopy);
|
||||
if (!result) return;
|
||||
context
|
||||
.read<ChatsCubit>()
|
||||
.reduceUnreadMessagesCount(chatId: state.chatRoom.id, count: unreadMessagesIdsCopy.length);
|
||||
unreadMessagesIdsCopy.clear();
|
||||
},
|
||||
);
|
||||
EasyDebounce.debounce('mark_as_read', Duration(milliseconds: 500), () async {
|
||||
final result = await context.read<ChatRoomCubit>().markMessagesAsRead(state.chatRoom.id, unreadMessagesIdsCopy);
|
||||
if (!result) return;
|
||||
context.read<ChatsCubit>().reduceUnreadMessagesCount(
|
||||
chatId: state.chatRoom.id,
|
||||
count: unreadMessagesIdsCopy.length,
|
||||
);
|
||||
unreadMessagesIdsCopy.clear();
|
||||
});
|
||||
}
|
||||
},
|
||||
child: ChatBubbleWidget(
|
||||
@@ -219,16 +196,12 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
onVisibilityChanged: (VisibilityInfo info) {
|
||||
if (!state.chatMessages[index].isRead && !isCurrentUserAuthor && info.visibleFraction > 0) {
|
||||
unreadMessagesIds.add((id: state.chatMessages[index].id, index: index));
|
||||
EasyDebounce.debounce(
|
||||
'mark_as_read',
|
||||
Duration(milliseconds: 500),
|
||||
() async {
|
||||
final result = await context.read<ChatRoomCubit>().markMessagesAsRead(state.chatRoom.id, unreadMessagesIds);
|
||||
if (!result) return;
|
||||
context.read<ChatsCubit>().reduceUnreadMessagesCount(chatId: state.chatRoom.id, count: unreadMessagesIds.length);
|
||||
unreadMessagesIds.clear();
|
||||
},
|
||||
);
|
||||
EasyDebounce.debounce('mark_as_read', Duration(milliseconds: 500), () async {
|
||||
final result = await context.read<ChatRoomCubit>().markMessagesAsRead(state.chatRoom.id, unreadMessagesIds);
|
||||
if (!result) return;
|
||||
context.read<ChatsCubit>().reduceUnreadMessagesCount(chatId: state.chatRoom.id, count: unreadMessagesIds.length);
|
||||
unreadMessagesIds.clear();
|
||||
});
|
||||
}
|
||||
},
|
||||
child: ChatBubbleWidget(
|
||||
@@ -268,6 +241,8 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
},
|
||||
),
|
||||
),
|
||||
if (bannerType != null)
|
||||
Padding(padding: EdgeInsets.symmetric(vertical: 12, horizontal: 16), child: NotificationBanner(bannerType: bannerType)),
|
||||
],
|
||||
],
|
||||
],
|
||||
@@ -310,24 +285,14 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
|
||||
if (result != null) {
|
||||
List<File> files = result.paths.map((path) => File(path!)).toList();
|
||||
await chatRoomCubit.sendMessage(
|
||||
chatId: widget.chatId,
|
||||
files: files,
|
||||
);
|
||||
await chatRoomCubit.sendMessage(chatId: widget.chatId, files: files);
|
||||
}
|
||||
},
|
||||
customBorder: CircleBorder(),
|
||||
child: Ink(
|
||||
height: 40,
|
||||
width: 40,
|
||||
child: Transform.rotate(
|
||||
angle: pi / 4,
|
||||
child: Icon(
|
||||
Icons.attach_file_rounded,
|
||||
size: 30,
|
||||
color: Color(0xFF858E99),
|
||||
),
|
||||
),
|
||||
child: Transform.rotate(angle: pi / 4, child: Icon(Icons.attach_file_rounded, size: 30, color: Color(0xFF858E99))),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
@@ -345,33 +310,16 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
isDense: true,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||||
hintText: 'Сообщение',
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
height: 1,
|
||||
color: Color(0xFFAEAEB2),
|
||||
),
|
||||
hintStyle: TextStyle(fontSize: 14, fontWeight: FontWeight.w400, height: 1, color: Color(0xFFAEAEB2)),
|
||||
filled: true,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
borderSide: BorderSide(
|
||||
color: Color(0xFFD1D1D6),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
border: OutlineInputBorder(borderRadius: BorderRadius.circular(100), borderSide: BorderSide(color: Color(0xFFD1D1D6), width: 1)),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
borderSide: BorderSide(
|
||||
color: Color(0xFFD1D1D6),
|
||||
width: 1,
|
||||
),
|
||||
borderSide: BorderSide(color: Color(0xFFD1D1D6), width: 1),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
borderSide: BorderSide(
|
||||
color: Color(0xFFD1D1D6),
|
||||
width: 1,
|
||||
),
|
||||
borderSide: BorderSide(color: Color(0xFFD1D1D6), width: 1),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -382,27 +330,17 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
customBorder: CircleBorder(),
|
||||
onTap: () {
|
||||
if (textEditingController.text.isNotEmpty) {
|
||||
chatRoomCubit.sendMessage(
|
||||
chatId: widget.chatId,
|
||||
data: textEditingController.text.trim(),
|
||||
);
|
||||
chatRoomCubit.sendMessage(chatId: widget.chatId, data: textEditingController.text.trim());
|
||||
setState(() {
|
||||
textEditingController.clear();
|
||||
});
|
||||
}
|
||||
},
|
||||
child: Ink(
|
||||
decoration: BoxDecoration(
|
||||
color: appTheme.blueA400,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
decoration: BoxDecoration(color: appTheme.blueA400, shape: BoxShape.circle),
|
||||
height: 40,
|
||||
width: 40,
|
||||
child: Icon(
|
||||
Icons.arrow_upward_rounded,
|
||||
size: 24,
|
||||
color: Colors.white,
|
||||
),
|
||||
child: Icon(Icons.arrow_upward_rounded, size: 24, color: Colors.white),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
@@ -411,13 +349,7 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
),
|
||||
],
|
||||
),
|
||||
if (showAboutOrderButton)
|
||||
Positioned(
|
||||
top: 8,
|
||||
right: 15,
|
||||
left: 16,
|
||||
child: AboutOrderButton(),
|
||||
),
|
||||
if (showAboutOrderButton) Positioned(top: 8, right: 15, left: 16, child: AboutOrderButton()),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -428,16 +360,7 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 32),
|
||||
child: Text(
|
||||
DateFormat.yMMMMd('RU').format(
|
||||
date,
|
||||
),
|
||||
style: const TextStyle(
|
||||
height: 1,
|
||||
color: Color(0xFF8393A1),
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
child: Text(DateFormat.yMMMMd('RU').format(date), style: const TextStyle(height: 1, color: Color(0xFF8393A1), fontWeight: FontWeight.w500)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/firebase_api.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/utils/toasts.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
|
||||
import 'package:eatvio_app/features/dev_tools/test_cases.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class DevToolsScreen extends StatelessWidget {
|
||||
const DevToolsScreen({super.key});
|
||||
@@ -61,9 +67,7 @@ class _DevToolsScreenContentState extends State<DevToolsScreenContent> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Для разработчиков'),
|
||||
),
|
||||
appBar: AppBar(title: Text('Для разработчиков')),
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
@@ -99,6 +103,7 @@ class _DevToolsScreenContentState extends State<DevToolsScreenContent> {
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: testCases.length,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemBuilder:
|
||||
(context, index) => Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10.0),
|
||||
@@ -151,6 +156,80 @@ class _DevToolsScreenContentState extends State<DevToolsScreenContent> {
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
CustomOutlinedButton(
|
||||
text: 'add token to server',
|
||||
buttonStyle: ButtonStyle(),
|
||||
onPressed: () async {
|
||||
try {
|
||||
final dio = sl<Dio>();
|
||||
final String platform;
|
||||
if (Platform.isAndroid) {
|
||||
platform = 'android';
|
||||
} else {
|
||||
platform = 'ios';
|
||||
}
|
||||
await dio.post('/api/profile/notification/token/register', data: {'token': await FirebaseApi().getToken(), 'platform': platform});
|
||||
showInfoToast('Токен добавлен');
|
||||
} catch (e) {
|
||||
showInfoToast('Не удалось добавть токен');
|
||||
}
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
CustomOutlinedButton(
|
||||
text: 'copy token',
|
||||
buttonStyle: ButtonStyle(),
|
||||
onPressed: () async {
|
||||
try {
|
||||
final token = await FirebaseApi().getToken();
|
||||
if (token != null) {
|
||||
Clipboard.setData(ClipboardData(text: token));
|
||||
showInfoToast('Токен скопирован');
|
||||
} else {
|
||||
showInfoToast('Token is null');
|
||||
}
|
||||
} catch (e) {
|
||||
showInfoToast('Не получилось скопировать токен');
|
||||
}
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
CustomOutlinedButton(
|
||||
text: 'remove token from server',
|
||||
buttonStyle: ButtonStyle(),
|
||||
onPressed: () async {
|
||||
try {
|
||||
final dio = sl<Dio>();
|
||||
final String platform;
|
||||
if (Platform.isAndroid) {
|
||||
platform = 'android';
|
||||
} else {
|
||||
platform = 'ios';
|
||||
}
|
||||
await dio.post('/api/profile/notification/token/unregister', data: {'token': await FirebaseApi().getToken(), 'platform': platform});
|
||||
showInfoToast('Токен удалён');
|
||||
} catch (e) {
|
||||
showInfoToast('Не удалось удалить токен');
|
||||
}
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
CustomOutlinedButton(
|
||||
text: 'send notification',
|
||||
buttonStyle: ButtonStyle(),
|
||||
onPressed: () async {
|
||||
try {
|
||||
final dio = sl<Dio>();
|
||||
Future.delayed(Duration(seconds: 3), () async {
|
||||
await dio.post('/api/profile/notification/token/test');
|
||||
});
|
||||
} catch (e) {
|
||||
showInfoToast('Не удалось добавть токен');
|
||||
}
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 300),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:easy_debounce/easy_debounce.dart';
|
||||
import 'package:eatvio_app/core/enums/base_state.dart';
|
||||
import 'package:eatvio_app/features/my_finance/models/payment_url_model.dart';
|
||||
import 'package:eatvio_app/features/my_finance/presentation/cubit/user_cards_cubit/user_cards_state.dart';
|
||||
import 'package:eatvio_app/features/my_finance/repositories/cards_repository.dart';
|
||||
@@ -22,10 +23,12 @@ class UserCardsCubit extends Cubit<UserCardsState> {
|
||||
Future<void> getCards() async {
|
||||
try {
|
||||
if (isClosed) return;
|
||||
emit(state.copyWith(state: BaseState.loading));
|
||||
final cards = await cardsRepository.getCards();
|
||||
emit(state.copyWith(cards: cards));
|
||||
emit(state.copyWith(cards: cards, state: BaseState.success));
|
||||
} catch (e, st) {
|
||||
log(e.toString(), stackTrace: st);
|
||||
emit(state.copyWith(state: BaseState.error, error: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +62,7 @@ class UserCardsCubit extends Cubit<UserCardsState> {
|
||||
emit(state.copyWith(cards: cards));
|
||||
await cardsRepository.deleteCard(id: id);
|
||||
} catch (e, st) {
|
||||
emit(state.copyWith(cards: oldCards));
|
||||
emit(state.copyWith(cards: oldCards, error: e.toString(), state: BaseState.error));
|
||||
log(e.toString(), stackTrace: st);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:eatvio_app/core/enums/base_state.dart';
|
||||
import 'package:eatvio_app/features/my_finance/models/card_model.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
@@ -7,8 +8,9 @@ part 'user_cards_state.freezed.dart';
|
||||
class UserCardsState with _$UserCardsState {
|
||||
|
||||
factory UserCardsState({
|
||||
@Default([]) List<CardModel> cards,
|
||||
@Default(false) bool isLoading,
|
||||
@Default([]) List<CardModel> cards,
|
||||
@Default(null) String? error,
|
||||
@Default(BaseState.initial) BaseState state,
|
||||
}) = _UserCardsState;
|
||||
|
||||
}
|
||||
@@ -18,7 +18,8 @@ final _privateConstructorUsedError = UnsupportedError(
|
||||
/// @nodoc
|
||||
mixin _$UserCardsState {
|
||||
List<CardModel> get cards => throw _privateConstructorUsedError;
|
||||
bool get isLoading => throw _privateConstructorUsedError;
|
||||
String? get error => throw _privateConstructorUsedError;
|
||||
BaseState get state => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of UserCardsState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -34,7 +35,7 @@ abstract class $UserCardsStateCopyWith<$Res> {
|
||||
$Res Function(UserCardsState) then,
|
||||
) = _$UserCardsStateCopyWithImpl<$Res, UserCardsState>;
|
||||
@useResult
|
||||
$Res call({List<CardModel> cards, bool isLoading});
|
||||
$Res call({List<CardModel> cards, String? error, BaseState state});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -51,7 +52,11 @@ class _$UserCardsStateCopyWithImpl<$Res, $Val extends UserCardsState>
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({Object? cards = null, Object? isLoading = null}) {
|
||||
$Res call({
|
||||
Object? cards = null,
|
||||
Object? error = freezed,
|
||||
Object? state = null,
|
||||
}) {
|
||||
return _then(
|
||||
_value.copyWith(
|
||||
cards:
|
||||
@@ -59,11 +64,16 @@ class _$UserCardsStateCopyWithImpl<$Res, $Val extends UserCardsState>
|
||||
? _value.cards
|
||||
: cards // ignore: cast_nullable_to_non_nullable
|
||||
as List<CardModel>,
|
||||
isLoading:
|
||||
null == isLoading
|
||||
? _value.isLoading
|
||||
: isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
error:
|
||||
freezed == error
|
||||
? _value.error
|
||||
: error // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
state:
|
||||
null == state
|
||||
? _value.state
|
||||
: state // ignore: cast_nullable_to_non_nullable
|
||||
as BaseState,
|
||||
)
|
||||
as $Val,
|
||||
);
|
||||
@@ -79,7 +89,7 @@ abstract class _$$UserCardsStateImplCopyWith<$Res>
|
||||
) = __$$UserCardsStateImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({List<CardModel> cards, bool isLoading});
|
||||
$Res call({List<CardModel> cards, String? error, BaseState state});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -95,7 +105,11 @@ class __$$UserCardsStateImplCopyWithImpl<$Res>
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({Object? cards = null, Object? isLoading = null}) {
|
||||
$Res call({
|
||||
Object? cards = null,
|
||||
Object? error = freezed,
|
||||
Object? state = null,
|
||||
}) {
|
||||
return _then(
|
||||
_$UserCardsStateImpl(
|
||||
cards:
|
||||
@@ -103,11 +117,16 @@ class __$$UserCardsStateImplCopyWithImpl<$Res>
|
||||
? _value._cards
|
||||
: cards // ignore: cast_nullable_to_non_nullable
|
||||
as List<CardModel>,
|
||||
isLoading:
|
||||
null == isLoading
|
||||
? _value.isLoading
|
||||
: isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
error:
|
||||
freezed == error
|
||||
? _value.error
|
||||
: error // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
state:
|
||||
null == state
|
||||
? _value.state
|
||||
: state // ignore: cast_nullable_to_non_nullable
|
||||
as BaseState,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -118,7 +137,8 @@ class __$$UserCardsStateImplCopyWithImpl<$Res>
|
||||
class _$UserCardsStateImpl implements _UserCardsState {
|
||||
_$UserCardsStateImpl({
|
||||
final List<CardModel> cards = const [],
|
||||
this.isLoading = false,
|
||||
this.error = null,
|
||||
this.state = BaseState.initial,
|
||||
}) : _cards = cards;
|
||||
|
||||
final List<CardModel> _cards;
|
||||
@@ -132,11 +152,14 @@ class _$UserCardsStateImpl implements _UserCardsState {
|
||||
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool isLoading;
|
||||
final String? error;
|
||||
@override
|
||||
@JsonKey()
|
||||
final BaseState state;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'UserCardsState(cards: $cards, isLoading: $isLoading)';
|
||||
return 'UserCardsState(cards: $cards, error: $error, state: $state)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -145,15 +168,16 @@ class _$UserCardsStateImpl implements _UserCardsState {
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$UserCardsStateImpl &&
|
||||
const DeepCollectionEquality().equals(other._cards, _cards) &&
|
||||
(identical(other.isLoading, isLoading) ||
|
||||
other.isLoading == isLoading));
|
||||
(identical(other.error, error) || other.error == error) &&
|
||||
(identical(other.state, state) || other.state == state));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(_cards),
|
||||
isLoading,
|
||||
error,
|
||||
state,
|
||||
);
|
||||
|
||||
/// Create a copy of UserCardsState
|
||||
@@ -169,13 +193,18 @@ class _$UserCardsStateImpl implements _UserCardsState {
|
||||
}
|
||||
|
||||
abstract class _UserCardsState implements UserCardsState {
|
||||
factory _UserCardsState({final List<CardModel> cards, final bool isLoading}) =
|
||||
_$UserCardsStateImpl;
|
||||
factory _UserCardsState({
|
||||
final List<CardModel> cards,
|
||||
final String? error,
|
||||
final BaseState state,
|
||||
}) = _$UserCardsStateImpl;
|
||||
|
||||
@override
|
||||
List<CardModel> get cards;
|
||||
@override
|
||||
bool get isLoading;
|
||||
String? get error;
|
||||
@override
|
||||
BaseState get state;
|
||||
|
||||
/// Create a copy of UserCardsState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import 'package:eatvio_app/core/enums/base_state.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/widgets/data_error_widget.dart';
|
||||
import 'package:eatvio_app/core/widgets/loader.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
|
||||
import 'package:eatvio_app/features/my_finance/presentation/cubit/user_cards_cubit/user_cards_cubit.dart';
|
||||
import 'package:eatvio_app/features/my_finance/presentation/cubit/user_cards_cubit/user_cards_state.dart';
|
||||
import 'package:eatvio_app/features/my_finance/presentation/widgets/card_widget.dart';
|
||||
@@ -35,9 +39,9 @@ class CardsSettingsContent extends StatelessWidget {
|
||||
body: Column(
|
||||
children: [
|
||||
const SizedBox(height: 16),
|
||||
Material(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: Colors.white,
|
||||
Container(
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(12), color: Colors.white),
|
||||
width: double.infinity,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -64,21 +68,39 @@ class CardsSettingsContent extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
SingleChildScrollView(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: BlocBuilder<UserCardsCubit, UserCardsState>(
|
||||
builder: (context, state) {
|
||||
if (state.isLoading) {}
|
||||
return Row(
|
||||
spacing: 12,
|
||||
children: List.generate(state.cards.length + 1, (index) {
|
||||
if (index < state.cards.length) {
|
||||
final cardsNumber = state.cards[index].cardNumber.substring(state.cards[index].cardNumber.length - 7);
|
||||
return CardWidget(isSelected: state.cards[index].isDefault, cardId: state.cards[index].id, cardNumber: cardsNumber);
|
||||
}
|
||||
return CardWidget(enabledAddCard: true);
|
||||
}),
|
||||
);
|
||||
switch (state.state) {
|
||||
case BaseState.error:
|
||||
return SizedBox(
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
child: Center(
|
||||
child: DataErrorWidget(
|
||||
onReload: () async {
|
||||
context.read<UserCardsCubit>().getCards();
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
case BaseState.success:
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(left: 12),
|
||||
child: Row(
|
||||
spacing: 12,
|
||||
children: List.generate(state.cards.length + 1, (index) {
|
||||
if (index < state.cards.length) {
|
||||
final cardsNumber = state.cards[index].cardNumber.substring(state.cards[index].cardNumber.length - 7);
|
||||
return CardWidget(isSelected: state.cards[index].isDefault, cardId: state.cards[index].id, cardNumber: cardsNumber);
|
||||
}
|
||||
return CardWidget(enabledAddCard: true);
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
default:
|
||||
return SizedBox(width: MediaQuery.sizeOf(context).width, height: 200, child: Center(child: Loader()));
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
@@ -16,53 +16,68 @@ class CardWidget extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = S.of(context);
|
||||
return GestureDetector(
|
||||
onTap: enabledAddCard
|
||||
? () async {
|
||||
final addCardUrl = await context.read<UserCardsCubit>().addCard();
|
||||
return Stack(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap:
|
||||
enabledAddCard
|
||||
? () async {
|
||||
final addCardUrl = await context.read<UserCardsCubit>().addCard();
|
||||
|
||||
if (addCardUrl != null) {
|
||||
final uri = Uri.parse(addCardUrl.paymentUrl);
|
||||
if (await canLaunchUrl(uri)) {
|
||||
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||
}
|
||||
}
|
||||
}
|
||||
: () {
|
||||
context.read<UserCardsCubit>().markAsDefault(id: cardId);
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(4),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: isSelected ? appTheme.blueA400 : Color(0xFFF1F2F6), width: 2),
|
||||
if (addCardUrl != null) {
|
||||
final uri = Uri.parse(addCardUrl.paymentUrl);
|
||||
if (await canLaunchUrl(uri)) {
|
||||
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||
}
|
||||
}
|
||||
}
|
||||
: () {
|
||||
context.read<UserCardsCubit>().markAsDefault(id: cardId);
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(4),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: isSelected ? appTheme.blueA400 : Color(0xFFF1F2F6), width: 2),
|
||||
),
|
||||
child:
|
||||
enabledAddCard
|
||||
? Container(
|
||||
width: 148,
|
||||
padding: EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(color: Color(0xFFF1F2F6), borderRadius: BorderRadius.circular(4)),
|
||||
child: Column(
|
||||
children: [
|
||||
SvgPicture.asset('assets/icons/add_card.svg'),
|
||||
const SizedBox(height: 24),
|
||||
Text(s.new_card, style: TextStyles.textSSemibold.copyWith(color: appTheme.black)),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
width: 148,
|
||||
padding: EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(color: Color(0xFFF1F2F6), borderRadius: BorderRadius.circular(4)),
|
||||
child: Column(
|
||||
children: [
|
||||
SvgPicture.asset('assets/icons/card.svg'),
|
||||
const SizedBox(height: 24),
|
||||
Text(cardNumber, style: TextStyles.textSSemibold.copyWith(color: appTheme.black)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: enabledAddCard
|
||||
? Container(
|
||||
width: 148,
|
||||
padding: EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(color: Color(0xFFF1F2F6), borderRadius: BorderRadius.circular(4)),
|
||||
child: Column(
|
||||
children: [
|
||||
SvgPicture.asset('assets/icons/add_card.svg'),
|
||||
const SizedBox(height: 24),
|
||||
Text(s.new_card, style: TextStyles.textSSemibold.copyWith(color: appTheme.black)),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
width: 148,
|
||||
padding: EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(color: Color(0xFFF1F2F6), borderRadius: BorderRadius.circular(4)),
|
||||
child: Column(
|
||||
children: [
|
||||
SvgPicture.asset('assets/icons/card.svg'),
|
||||
const SizedBox(height: 24),
|
||||
Text(cardNumber, style: TextStyles.textSSemibold.copyWith(color: appTheme.black)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (!enabledAddCard)
|
||||
Positioned(
|
||||
top: 12,
|
||||
right: 12,
|
||||
child: GestureDetector(
|
||||
child: SvgPicture.asset('assets/images/img_close_blue_gray_900.svg'),
|
||||
onTap: () => context.read<UserCardsCubit>().deleteCard(id: cardId),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
70
lib/firebase_options.dart
Normal file
70
lib/firebase_options.dart
Normal file
@@ -0,0 +1,70 @@
|
||||
// File generated by FlutterFire CLI.
|
||||
// ignore_for_file: type=lint
|
||||
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
|
||||
import 'package:flutter/foundation.dart'
|
||||
show defaultTargetPlatform, kIsWeb, TargetPlatform;
|
||||
|
||||
/// Default [FirebaseOptions] for use with your Firebase apps.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// import 'firebase_options.dart';
|
||||
/// // ...
|
||||
/// await Firebase.initializeApp(
|
||||
/// options: DefaultFirebaseOptions.currentPlatform,
|
||||
/// );
|
||||
/// ```
|
||||
class DefaultFirebaseOptions {
|
||||
static FirebaseOptions get currentPlatform {
|
||||
if (kIsWeb) {
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions have not been configured for web - '
|
||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||
);
|
||||
}
|
||||
switch (defaultTargetPlatform) {
|
||||
case TargetPlatform.android:
|
||||
return android;
|
||||
case TargetPlatform.iOS:
|
||||
return ios;
|
||||
case TargetPlatform.macOS:
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions have not been configured for macos - '
|
||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||
);
|
||||
case TargetPlatform.windows:
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions have not been configured for windows - '
|
||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||
);
|
||||
case TargetPlatform.linux:
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions have not been configured for linux - '
|
||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||
);
|
||||
default:
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions are not supported for this platform.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static const FirebaseOptions android = FirebaseOptions(
|
||||
apiKey: 'AIzaSyBUe1UqAAMfgMcd4hWdRoLF1Os-yn-HE9k',
|
||||
appId: '1:613980912922:android:4de95e5f2d50ba69ecc0a3',
|
||||
messagingSenderId: '613980912922',
|
||||
projectId: 'eatvio-3467b',
|
||||
databaseURL: 'https://eatvio-3467b-default-rtdb.europe-west1.firebasedatabase.app',
|
||||
storageBucket: 'eatvio-3467b.firebasestorage.app',
|
||||
);
|
||||
|
||||
static const FirebaseOptions ios = FirebaseOptions(
|
||||
apiKey: 'AIzaSyBnE8tQjL9_1HuXrTnqNy6qbXpoenO36yQ',
|
||||
appId: '1:613980912922:ios:c557d140a1212e63ecc0a3',
|
||||
messagingSenderId: '613980912922',
|
||||
projectId: 'eatvio-3467b',
|
||||
databaseURL: 'https://eatvio-3467b-default-rtdb.europe-west1.firebasedatabase.app',
|
||||
storageBucket: 'eatvio-3467b.firebasestorage.app',
|
||||
iosBundleId: 'com.eatvioapp.app',
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:eatvio_app/core/app_router.dart';
|
||||
import 'package:eatvio_app/core/firebase_api.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart' as di;
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
@@ -8,7 +9,9 @@ import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/prof
|
||||
import 'package:eatvio_app/features/chat/presentation/bloc/chats_cubit/chats_cubit.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart';
|
||||
import 'package:eatvio_app/features/internet_connection_checker/internet_connection_checker_screen.dart';
|
||||
import 'package:eatvio_app/firebase_options.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
@@ -20,14 +23,12 @@ import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
Future<void> main() async {
|
||||
WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
|
||||
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
|
||||
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
||||
await di.init();
|
||||
await SentryFlutter.init(
|
||||
(options) {
|
||||
options.dsn = 'http://ba7f2653067776f3034e6e89850e85a1@sentry.md-desk.ru:9000/2';
|
||||
options.tracesSampleRate = 1.0;
|
||||
},
|
||||
appRunner: () => runApp(MyApp()),
|
||||
);
|
||||
await SentryFlutter.init((options) {
|
||||
options.dsn = 'http://ba7f2653067776f3034e6e89850e85a1@sentry.md-desk.ru:9000/2';
|
||||
options.tracesSampleRate = 1.0;
|
||||
}, appRunner: () => runApp(MyApp()));
|
||||
FlutterNativeSplash.remove();
|
||||
}
|
||||
|
||||
@@ -55,9 +56,7 @@ class MyApp extends StatelessWidget {
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: [
|
||||
Locale.fromSubtags(languageCode: 'ru'),
|
||||
],
|
||||
supportedLocales: [Locale.fromSubtags(languageCode: 'ru')],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: eatvio_app
|
||||
description: A new Flutter project.
|
||||
version: 1.0.48+48
|
||||
version: 1.0.50+2
|
||||
publish_to: 'none'
|
||||
|
||||
environment:
|
||||
@@ -62,6 +62,8 @@ dependencies:
|
||||
sentry_flutter: ^8.13.0
|
||||
connectivity_plus: ^6.1.3
|
||||
visibility_detector: ^0.4.0+2
|
||||
firebase_core: ^3.12.1
|
||||
firebase_messaging: ^15.2.4
|
||||
|
||||
dependency_overrides:
|
||||
intl: ^0.19.0
|
||||
|
||||
Reference in New Issue
Block a user