diff --git a/android/app/build.gradle b/android/app/build.gradle index 72ccbae..bf2c53c 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -38,7 +38,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.muhammadali.telegramchatapp" + applicationId "com.just2click.hivechatapp" minSdkVersion 16 targetSdkVersion 28 multiDexEnabled true diff --git a/android/app/google-services.json b/android/app/google-services.json index a6b534b..48a7efe 100644 --- a/android/app/google-services.json +++ b/android/app/google-services.json @@ -1,50 +1,43 @@ { "project_info": { - "project_number": "569979142021", - "firebase_url": "https://chat-app-2021.firebaseio.com", - "project_id": "chat-app-2021", - "storage_bucket": "chat-app-2021.appspot.com" + "project_number": "883352695161", + "firebase_url": "https://hive1-a9048.firebaseio.com", + "project_id": "hive1-a9048", + "storage_bucket": "hive1-a9048.appspot.com" }, "client": [ { "client_info": { - "mobilesdk_app_id": "1:569979142021:android:5bb02f0cb46e9c40898eec", + "mobilesdk_app_id": "1:883352695161:android:210561df6b8743f5953fa8", "android_client_info": { - "package_name": "com.muhammadali.telegramchatapp" + "package_name": "com.just2click.hivechatapp" } }, "oauth_client": [ { - "client_id": "569979142021-7j4rr51i9fo2hdivauv7ee1ahkmifmog.apps.googleusercontent.com", + "client_id": "883352695161-a97nhr56cssjvl69vq1nv55lcofagvkf.apps.googleusercontent.com", "client_type": 1, "android_info": { - "package_name": "com.muhammadali.telegramchatapp", - "certificate_hash": "3498fdb3a7b76c02a72d33027ff1bab13b7330a1" + "package_name": "com.just2click.hivechatapp", + "certificate_hash": "a2e05f984204ad69f5126c19b5a1d18070bdf753" } }, { - "client_id": "569979142021-7tnf70vgh3dl2u6pgsdf3a6lp2ej9nkc.apps.googleusercontent.com", + "client_id": "883352695161-5qi6vdbp7rf5aklvku2gkoj4icrm6kup.apps.googleusercontent.com", "client_type": 3 } ], "api_key": [ { - "current_key": "AIzaSyDiNucLksbgNIEFWuDsTXrxBHe1SKfNcKY" + "current_key": "AIzaSyAgdyYvoBH0SNB_TKbIfi84a9UeOSBesBo" } ], "services": { "appinvite_service": { "other_platform_oauth_client": [ { - "client_id": "569979142021-7tnf70vgh3dl2u6pgsdf3a6lp2ej9nkc.apps.googleusercontent.com", + "client_id": "883352695161-5qi6vdbp7rf5aklvku2gkoj4icrm6kup.apps.googleusercontent.com", "client_type": 3 - }, - { - "client_id": "569979142021-odjemf6j2jv9c1k999rv82v6ootii0r6.apps.googleusercontent.com", - "client_type": 2, - "ios_info": { - "bundle_id": "com.muhammadali.telegramchatapp" - } } ] } diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml index 825aed0..c6cda4c 100644 --- a/android/app/src/debug/AndroidManifest.xml +++ b/android/app/src/debug/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="com.just2click.hivechatapp"> diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index ca8fb5a..3840d56 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="com.just2click.hivechatapp"> diff --git a/assets/images/Friend 1@2x.png b/assets/images/Friend 1@2x.png new file mode 100644 index 0000000..0dfd9ed Binary files /dev/null and b/assets/images/Friend 1@2x.png differ diff --git a/assets/images/Friend 2@2x.png b/assets/images/Friend 2@2x.png new file mode 100644 index 0000000..46fcc25 Binary files /dev/null and b/assets/images/Friend 2@2x.png differ diff --git a/assets/images/Friend 3@2x.png b/assets/images/Friend 3@2x.png new file mode 100644 index 0000000..cfe0942 Binary files /dev/null and b/assets/images/Friend 3@2x.png differ diff --git a/assets/images/Friend 4@2x.png b/assets/images/Friend 4@2x.png new file mode 100644 index 0000000..e6840b5 Binary files /dev/null and b/assets/images/Friend 4@2x.png differ diff --git a/assets/images/Friend 5@2x.png b/assets/images/Friend 5@2x.png new file mode 100644 index 0000000..880e616 Binary files /dev/null and b/assets/images/Friend 5@2x.png differ diff --git a/assets/images/Friend_1@2x_100.jpg b/assets/images/Friend_1@2x_100.jpg new file mode 100644 index 0000000..e604902 Binary files /dev/null and b/assets/images/Friend_1@2x_100.jpg differ diff --git a/assets/images/Friend_2@2x_100.jpg b/assets/images/Friend_2@2x_100.jpg new file mode 100644 index 0000000..0d8caa7 Binary files /dev/null and b/assets/images/Friend_2@2x_100.jpg differ diff --git a/assets/images/Friend_3@2x_100.jpg b/assets/images/Friend_3@2x_100.jpg new file mode 100644 index 0000000..dd09882 Binary files /dev/null and b/assets/images/Friend_3@2x_100.jpg differ diff --git a/assets/images/Friend_4@2x_100.jpg b/assets/images/Friend_4@2x_100.jpg new file mode 100644 index 0000000..9247ec6 Binary files /dev/null and b/assets/images/Friend_4@2x_100.jpg differ diff --git a/assets/images/Friend_5@2x_100.jpg b/assets/images/Friend_5@2x_100.jpg new file mode 100644 index 0000000..5470159 Binary files /dev/null and b/assets/images/Friend_5@2x_100.jpg differ diff --git a/assets/images/Icon Black@2x-100.jpg b/assets/images/Icon Black@2x-100.jpg new file mode 100644 index 0000000..28e0d91 Binary files /dev/null and b/assets/images/Icon Black@2x-100.jpg differ diff --git a/assets/images/Icon Black@2x.png b/assets/images/Icon Black@2x.png new file mode 100644 index 0000000..a995bf1 Binary files /dev/null and b/assets/images/Icon Black@2x.png differ diff --git a/assets/images/Icon White@2x-100.jpg b/assets/images/Icon White@2x-100.jpg new file mode 100644 index 0000000..f13e619 Binary files /dev/null and b/assets/images/Icon White@2x-100.jpg differ diff --git a/assets/images/Icon White@2x.png b/assets/images/Icon White@2x.png new file mode 100644 index 0000000..e2ea18f Binary files /dev/null and b/assets/images/Icon White@2x.png differ diff --git a/assets/images/Icon Yellow@2x-100.jpg b/assets/images/Icon Yellow@2x-100.jpg new file mode 100644 index 0000000..36fa12e Binary files /dev/null and b/assets/images/Icon Yellow@2x-100.jpg differ diff --git a/assets/images/Icon Yellow@2x.png b/assets/images/Icon Yellow@2x.png new file mode 100644 index 0000000..3989992 Binary files /dev/null and b/assets/images/Icon Yellow@2x.png differ diff --git a/assets/images/Icon fill@2x-100.jpg b/assets/images/Icon fill@2x-100.jpg new file mode 100644 index 0000000..d021f59 Binary files /dev/null and b/assets/images/Icon fill@2x-100.jpg differ diff --git a/assets/images/Icon fill@2x.png b/assets/images/Icon fill@2x.png new file mode 100644 index 0000000..f7cf84c Binary files /dev/null and b/assets/images/Icon fill@2x.png differ diff --git a/assets/images/Icon-Yellow.jpeg b/assets/images/Icon-Yellow.jpeg new file mode 100644 index 0000000..c6ceade Binary files /dev/null and b/assets/images/Icon-Yellow.jpeg differ diff --git a/assets/images/Icon-fill.svg b/assets/images/Icon-fill.svg new file mode 100644 index 0000000..8bcf3d4 --- /dev/null +++ b/assets/images/Icon-fill.svg @@ -0,0 +1 @@ +Icon fill \ No newline at end of file diff --git a/assets/images/Iconfill.png b/assets/images/Iconfill.png new file mode 100644 index 0000000..9ebf804 Binary files /dev/null and b/assets/images/Iconfill.png differ diff --git a/assets/images/Logo Black@2x-100.jpg b/assets/images/Logo Black@2x-100.jpg new file mode 100644 index 0000000..7053915 Binary files /dev/null and b/assets/images/Logo Black@2x-100.jpg differ diff --git a/assets/images/Logo Black@2x.png b/assets/images/Logo Black@2x.png new file mode 100644 index 0000000..6c1ecb3 Binary files /dev/null and b/assets/images/Logo Black@2x.png differ diff --git a/assets/images/Logo Color@2x-100.jpg b/assets/images/Logo Color@2x-100.jpg new file mode 100644 index 0000000..d68f733 Binary files /dev/null and b/assets/images/Logo Color@2x-100.jpg differ diff --git a/assets/images/Logo Color@2x.png b/assets/images/Logo Color@2x.png new file mode 100644 index 0000000..ae9f8b9 Binary files /dev/null and b/assets/images/Logo Color@2x.png differ diff --git a/assets/images/Logo White@2x-100.jpg b/assets/images/Logo White@2x-100.jpg new file mode 100644 index 0000000..7dca6bf Binary files /dev/null and b/assets/images/Logo White@2x-100.jpg differ diff --git a/assets/images/Logo White@2x.png b/assets/images/Logo White@2x.png new file mode 100644 index 0000000..c14f3a4 Binary files /dev/null and b/assets/images/Logo White@2x.png differ diff --git a/assets/images/Logo Yellow@2x-100.jpg b/assets/images/Logo Yellow@2x-100.jpg new file mode 100644 index 0000000..8f1c060 Binary files /dev/null and b/assets/images/Logo Yellow@2x-100.jpg differ diff --git a/assets/images/Logo Yellow@2x.png b/assets/images/Logo Yellow@2x.png new file mode 100644 index 0000000..7694212 Binary files /dev/null and b/assets/images/Logo Yellow@2x.png differ diff --git a/assets/images/Typo Black@2x-100.jpg b/assets/images/Typo Black@2x-100.jpg new file mode 100644 index 0000000..eb07f73 Binary files /dev/null and b/assets/images/Typo Black@2x-100.jpg differ diff --git a/assets/images/Typo Black@2x.png b/assets/images/Typo Black@2x.png new file mode 100644 index 0000000..5292f00 Binary files /dev/null and b/assets/images/Typo Black@2x.png differ diff --git a/assets/images/Typo White@2x-100.jpg b/assets/images/Typo White@2x-100.jpg new file mode 100644 index 0000000..0c768be Binary files /dev/null and b/assets/images/Typo White@2x-100.jpg differ diff --git a/assets/images/Typo White@2x.png b/assets/images/Typo White@2x.png new file mode 100644 index 0000000..af5c0a6 Binary files /dev/null and b/assets/images/Typo White@2x.png differ diff --git a/assets/images/Typo Yellow@2x-100.jpg b/assets/images/Typo Yellow@2x-100.jpg new file mode 100644 index 0000000..996c556 Binary files /dev/null and b/assets/images/Typo Yellow@2x-100.jpg differ diff --git a/assets/images/Typo Yellow@2x.png b/assets/images/Typo Yellow@2x.png new file mode 100644 index 0000000..2e03aa0 Binary files /dev/null and b/assets/images/Typo Yellow@2x.png differ diff --git a/assets/images/camera.png b/assets/images/camera.png new file mode 100644 index 0000000..d3b20f6 Binary files /dev/null and b/assets/images/camera.png differ diff --git a/assets/images/gallery.png b/assets/images/gallery.png new file mode 100644 index 0000000..7a0aecc Binary files /dev/null and b/assets/images/gallery.png differ diff --git a/assets/images/hive-bee-on-boarding.png b/assets/images/hive-bee-on-boarding.png new file mode 100644 index 0000000..ec81a8d Binary files /dev/null and b/assets/images/hive-bee-on-boarding.png differ diff --git a/assets/images/iconyellow.png b/assets/images/iconyellow.png new file mode 100644 index 0000000..3989992 Binary files /dev/null and b/assets/images/iconyellow.png differ diff --git a/assets/images/logo1.jpg b/assets/images/logo1.jpg new file mode 100644 index 0000000..d021f59 Binary files /dev/null and b/assets/images/logo1.jpg differ diff --git a/assets/images/people.jpeg b/assets/images/people.jpeg new file mode 100644 index 0000000..68c9d23 Binary files /dev/null and b/assets/images/people.jpeg differ diff --git a/ios/Flutter/.last_build_id b/ios/Flutter/.last_build_id new file mode 100644 index 0000000..8fc0fd9 --- /dev/null +++ b/ios/Flutter/.last_build_id @@ -0,0 +1 @@ +30dcd1506d550883b561a48d41d2ad13 \ No newline at end of file diff --git a/ios/GoogleService-Info.plist b/ios/GoogleService-Info.plist index 0de63e6..6634cf9 100644 --- a/ios/GoogleService-Info.plist +++ b/ios/GoogleService-Info.plist @@ -3,23 +3,23 @@ CLIENT_ID - 569979142021-odjemf6j2jv9c1k999rv82v6ootii0r6.apps.googleusercontent.com + 883352695161-d416o0l8rpp8vo35mc5orj3f9shj7m15.apps.googleusercontent.com REVERSED_CLIENT_ID - Paste your Key Here + com.googleusercontent.apps.883352695161-d416o0l8rpp8vo35mc5orj3f9shj7m15 ANDROID_CLIENT_ID - 569979142021-7j4rr51i9fo2hdivauv7ee1ahkmifmog.apps.googleusercontent.com + 883352695161-a97nhr56cssjvl69vq1nv55lcofagvkf.apps.googleusercontent.com API_KEY - AIzaSyD1beeMpBpbazkf5ZEgNSbOsTA6qKakgIk + AIzaSyDf-ZdPh0YbokRjSdr9ZlpNiu6lEQihbfo GCM_SENDER_ID - 569979142021 + 883352695161 PLIST_VERSION 1 BUNDLE_ID - com.muhammadali.telegramchatapp + com.just2click.hivechatapp PROJECT_ID - chat-app-2021 + hive1-a9048 STORAGE_BUCKET - chat-app-2021.appspot.com + hive1-a9048.appspot.com IS_ADS_ENABLED IS_ANALYTICS_ENABLED @@ -31,8 +31,8 @@ IS_SIGNIN_ENABLED GOOGLE_APP_ID - 1:569979142021:ios:884d5cae87f2742f898eec + 1:883352695161:ios:34089d9e884693b6953fa8 DATABASE_URL - https://chat-app-2021.firebaseio.com + https://hive1-a9048.firebaseio.com \ No newline at end of file diff --git a/ios/Podfile b/ios/Podfile index b30a428..36eb230 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -10,81 +10,34 @@ project 'Runner', { 'Release' => :release, } -def parse_KV_file(file, separator='=') - file_abs_path = File.expand_path(file) - if !File.exists? file_abs_path - return []; +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" end - generated_key_values = {} - skip_line_start_symbols = ["#", "/"] - File.foreach(file_abs_path) do |line| - next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } - plugin = line.split(pattern=separator) - if plugin.length == 2 - podname = plugin[0].strip() - path = plugin[1].strip() - podpath = File.expand_path("#{path}", file_abs_path) - generated_key_values[podname] = podpath - else - puts "Invalid plugin specification: #{line}" - end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches end - generated_key_values + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" end +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + target 'Runner' do use_frameworks! use_modular_headers! - - # Flutter Pod - - copied_flutter_dir = File.join(__dir__, 'Flutter') - copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework') - copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec') - unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path) - # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet. - # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration. - # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist. - - generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig') - unless File.exist?(generated_xcode_build_settings_path) - raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first" - end - generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path) - cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR']; - - unless File.exist?(copied_framework_path) - FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir) - end - unless File.exist?(copied_podspec_path) - FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir) - end - end - - # Keep pod path relative so it can be checked into Podfile.lock. - pod 'Flutter', :path => 'Flutter' - - # Plugin Pods - # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock - # referring to absolute paths on developers' machines. - system('rm -rf .symlinks') - system('mkdir -p .symlinks/plugins') - plugin_pods = parse_KV_file('../.flutter-plugins') - plugin_pods.each do |name, path| - symlink = File.join('.symlinks', 'plugins', name) - File.symlink(path, symlink) - pod name, :path => File.join(symlink, 'ios') - end + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) end -# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. -install! 'cocoapods', :disable_input_output_paths => true - post_install do |installer| installer.pods_project.targets.each do |target| - target.build_configurations.each do |config| - config.build_settings['ENABLE_BITCODE'] = 'NO' - end + flutter_additional_ios_build_settings(target) end end + +pod 'Firebase/Auth' \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index a5550fa..2836e1b 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -251,8 +251,6 @@ PODS: - firebase_core (0.0.1): - Firebase/Core - Flutter - - firebase_core_web (0.1.0): - - Flutter - firebase_messaging (0.0.1): - Firebase/Core - Firebase/Messaging @@ -328,8 +326,6 @@ PODS: - Flutter (1.0.0) - flutter_local_notifications (0.0.1): - Flutter - - flutter_plugin_android_lifecycle (0.0.1): - - Flutter - fluttertoast (0.0.2): - Flutter - FMDB (2.7.5): @@ -338,8 +334,6 @@ PODS: - google_sign_in (0.0.1): - Flutter - GoogleSignIn (~> 5.0) - - google_sign_in_web (0.8.1): - - Flutter - GoogleAppMeasurement (6.6.0): - GoogleUtilities/AppDelegateSwizzler (~> 6.0) - GoogleUtilities/MethodSwizzler (~> 6.0) @@ -408,6 +402,10 @@ PODS: - image_picker (0.0.1): - Flutter - leveldb-library (1.22) + - libphonenumber (0.0.1): + - Flutter + - libPhoneNumber-iOS + - libPhoneNumber-iOS (0.9.15) - nanopb (1.30905.0): - nanopb/decode (= 1.30905.0) - nanopb/encode (= 1.30905.0) @@ -415,42 +413,29 @@ PODS: - nanopb/encode (1.30905.0) - path_provider (0.0.1): - Flutter - - path_provider_linux (0.0.1): - - Flutter - - path_provider_macos (0.0.1): - - Flutter - PromisesObjC (1.2.9) - Protobuf (3.12.0) - shared_preferences (0.0.1): - Flutter - - shared_preferences_macos (0.0.1): - - Flutter - - shared_preferences_web (0.0.1): - - Flutter - - sqflite (0.0.1): + - sqflite (0.0.2): - Flutter - - FMDB (~> 2.7.2) + - FMDB (>= 2.7.5) DEPENDENCIES: - cloud_firestore (from `.symlinks/plugins/cloud_firestore/ios`) + - Firebase/Auth - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`) - - firebase_core_web (from `.symlinks/plugins/firebase_core_web/ios`) - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) - firebase_storage (from `.symlinks/plugins/firebase_storage/ios`) - Flutter (from `Flutter`) - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) - - flutter_plugin_android_lifecycle (from `.symlinks/plugins/flutter_plugin_android_lifecycle/ios`) - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) - google_sign_in (from `.symlinks/plugins/google_sign_in/ios`) - - google_sign_in_web (from `.symlinks/plugins/google_sign_in_web/ios`) - image_picker (from `.symlinks/plugins/image_picker/ios`) + - libphonenumber (from `.symlinks/plugins/libphonenumber/ios`) - path_provider (from `.symlinks/plugins/path_provider/ios`) - - path_provider_linux (from `.symlinks/plugins/path_provider_linux/ios`) - - path_provider_macos (from `.symlinks/plugins/path_provider_macos/ios`) - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) - - shared_preferences_macos (from `.symlinks/plugins/shared_preferences_macos/ios`) - - shared_preferences_web (from `.symlinks/plugins/shared_preferences_web/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`) SPEC REPOS: @@ -482,6 +467,7 @@ SPEC REPOS: - GTMAppAuth - GTMSessionFetcher - leveldb-library + - libPhoneNumber-iOS - nanopb - PromisesObjC - Protobuf @@ -493,8 +479,6 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/firebase_auth/ios" firebase_core: :path: ".symlinks/plugins/firebase_core/ios" - firebase_core_web: - :path: ".symlinks/plugins/firebase_core_web/ios" firebase_messaging: :path: ".symlinks/plugins/firebase_messaging/ios" firebase_storage: @@ -503,28 +487,18 @@ EXTERNAL SOURCES: :path: Flutter flutter_local_notifications: :path: ".symlinks/plugins/flutter_local_notifications/ios" - flutter_plugin_android_lifecycle: - :path: ".symlinks/plugins/flutter_plugin_android_lifecycle/ios" fluttertoast: :path: ".symlinks/plugins/fluttertoast/ios" google_sign_in: :path: ".symlinks/plugins/google_sign_in/ios" - google_sign_in_web: - :path: ".symlinks/plugins/google_sign_in_web/ios" image_picker: :path: ".symlinks/plugins/image_picker/ios" + libphonenumber: + :path: ".symlinks/plugins/libphonenumber/ios" path_provider: :path: ".symlinks/plugins/path_provider/ios" - path_provider_linux: - :path: ".symlinks/plugins/path_provider_linux/ios" - path_provider_macos: - :path: ".symlinks/plugins/path_provider_macos/ios" shared_preferences: :path: ".symlinks/plugins/shared_preferences/ios" - shared_preferences_macos: - :path: ".symlinks/plugins/shared_preferences_macos/ios" - shared_preferences_web: - :path: ".symlinks/plugins/shared_preferences_web/ios" sqflite: :path: ".symlinks/plugins/sqflite/ios" @@ -536,7 +510,6 @@ SPEC CHECKSUMS: Firebase: 7cf5f9c67f03cb3b606d1d6535286e1080e57eb6 firebase_auth: d99b993c1405096e66c58211b1cd956c23eed1c5 firebase_core: 335c02abd48672b7c83c683df833d0488a72e73e - firebase_core_web: d501d8b946b60c8af265428ce483b0fff5ad52d1 firebase_messaging: cffb57ce40958c6204f03fb0c81713e4cd1e240c firebase_storage: 22966fce4aa6e8848cbaa017df62107cee29f327 FirebaseAnalytics: 96634d356482d4f3af8fe459a0ebf19a99c71b75 @@ -553,11 +526,9 @@ SPEC CHECKSUMS: FirebaseStorage: f4f39ae834a7145963b913f54e2f24a9db1d8fac Flutter: 0e3d915762c693b495b44d77113d4970485de6ec flutter_local_notifications: 9e4738ce2471c5af910d961a6b7eadcf57c50186 - flutter_plugin_android_lifecycle: dc0b544e129eebb77a6bfb1239d4d1c673a60a35 fluttertoast: b644586ef3b16f67fae9a1f8754cef6b2d6b634b FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a google_sign_in: 6bd214b9c154f881422f5fe27b66aaa7bbd580cc - google_sign_in_web: 52deb24929ac0992baff65c57956031c44ed44c3 GoogleAppMeasurement: 67458367830514fb20fd9e233496f1eef9d90185 GoogleDataTransport: 9a8a16f79feffc7f42096743de2a7c4815e84020 GoogleDataTransportCCTSupport: d70a561f7d236af529fee598835caad5e25f6d3d @@ -567,19 +538,17 @@ SPEC CHECKSUMS: gRPC-Core: 4afa11bfbedf7cdecd04de535a9e046893404ed5 GTMAppAuth: 4deac854479704f348309e7b66189e604cf5e01e GTMSessionFetcher: 6f5c8abbab8a9bce4bb3f057e317728ec6182b10 - image_picker: 66aa71bc96850a90590a35d4c4a2907b0d823109 + image_picker: 9c3312491f862b28d21ecd8fdf0ee14e601b3f09 leveldb-library: 55d93ee664b4007aac644a782d11da33fba316f7 + libphonenumber: bd14d5abfc5939ab0b0767234e2d8e2593af258e + libPhoneNumber-iOS: 0a32a9525cf8744fe02c5206eb30d571e38f7d75 nanopb: c43f40fadfe79e8b8db116583945847910cbabc9 path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c - path_provider_linux: 4d630dc393e1f20364f3e3b4a2ff41d9674a84e4 - path_provider_macos: f760a3c5b04357c380e2fddb6f9db6f3015897e0 PromisesObjC: b48e0338dbbac2207e611750777895f7a5811b75 Protobuf: 2793fcd0622a00b546c60e7cbbcc493e043e9bb9 shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d - shared_preferences_macos: f3f29b71ccbb56bf40c9dd6396c9acf15e214087 - shared_preferences_web: 141cce0c3ed1a1c5bf2a0e44f52d31eeb66e5ea9 - sqflite: 4001a31ff81d210346b500c55b17f4d6c7589dd0 + sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 -PODFILE CHECKSUM: 1b66dae606f75376c5f2135a8290850eeb09ae83 +PODFILE CHECKSUM: 917a7ace996498e09e5a9c71cccc0820c8d5f1d0 -COCOAPODS: 1.9.3 +COCOAPODS: 1.10.0 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 72c905b..b8c442d 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -7,8 +7,8 @@ objects = { /* Begin PBXBuildFile section */ - 13BBA5E42494306000CD03FA /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 13BBA5E32494306000CD03FA /* GoogleService-Info.plist */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3A8D80B12556F3E40050D12F /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3A8D80B02556F3E40050D12F /* GoogleService-Info.plist */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 696DA2173320B5CA05C8F35A /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 959B03597723A75D9E0DCE82 /* Pods_Runner.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; @@ -32,9 +32,9 @@ /* Begin PBXFileReference section */ 0C675E7CFD4944ADC3B93E9D /* 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 = ""; }; - 13BBA5E32494306000CD03FA /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 3A8D80B02556F3E40050D12F /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -85,7 +85,7 @@ 97C146E51CF9000F007C117D = { isa = PBXGroup; children = ( - 13BBA5E32494306000CD03FA /* GoogleService-Info.plist */, + 3A8D80B02556F3E40050D12F /* GoogleService-Info.plist */, 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, @@ -132,7 +132,6 @@ 9D21B34EA45FAA2DA7F5CE53 /* Pods-Runner.release.xcconfig */, 0C675E7CFD4944ADC3B93E9D /* Pods-Runner.profile.xcconfig */, ); - name = Pods; path = Pods; sourceTree = ""; }; @@ -202,7 +201,7 @@ files = ( 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 13BBA5E42494306000CD03FA /* GoogleService-Info.plist in Resources */, + 3A8D80B12556F3E40050D12F /* GoogleService-Info.plist in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, ); @@ -253,9 +252,12 @@ files = ( ); inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", + "${PODS_ROOT}/GoogleSignIn/Resources/GoogleSignIn.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleSignIn.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -282,9 +284,54 @@ files = ( ); inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/AppAuth/AppAuth.framework", + "${BUILT_PRODUCTS_DIR}/BoringSSL-GRPC/openssl_grpc.framework", + "${BUILT_PRODUCTS_DIR}/FMDB/FMDB.framework", + "${PODS_ROOT}/../Flutter/Flutter.framework", + "${BUILT_PRODUCTS_DIR}/GTMAppAuth/GTMAppAuth.framework", + "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework", + "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", + "${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework", + "${BUILT_PRODUCTS_DIR}/Protobuf/Protobuf.framework", + "${BUILT_PRODUCTS_DIR}/abseil/absl.framework", + "${BUILT_PRODUCTS_DIR}/flutter_local_notifications/flutter_local_notifications.framework", + "${BUILT_PRODUCTS_DIR}/fluttertoast/fluttertoast.framework", + "${BUILT_PRODUCTS_DIR}/gRPC-C++/grpcpp.framework", + "${BUILT_PRODUCTS_DIR}/gRPC-Core/grpc.framework", + "${BUILT_PRODUCTS_DIR}/image_picker/image_picker.framework", + "${BUILT_PRODUCTS_DIR}/leveldb-library/leveldb.framework", + "${BUILT_PRODUCTS_DIR}/libPhoneNumber-iOS/libPhoneNumber_iOS.framework", + "${BUILT_PRODUCTS_DIR}/libphonenumber/libphonenumber.framework", + "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", + "${BUILT_PRODUCTS_DIR}/path_provider/path_provider.framework", + "${BUILT_PRODUCTS_DIR}/shared_preferences/shared_preferences.framework", + "${BUILT_PRODUCTS_DIR}/sqflite/sqflite.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AppAuth.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/openssl_grpc.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FMDB.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMAppAuth.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Protobuf.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/absl.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_local_notifications.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/fluttertoast.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/grpcpp.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/grpc.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/image_picker.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/leveldb.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libPhoneNumber_iOS.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libphonenumber.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqflite.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -327,7 +374,6 @@ /* Begin XCBuildConfiguration section */ 249021D3217E4FDB00AE95B9 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -394,17 +440,17 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = com.muhammadali.telegramchatapp; + PRODUCT_BUNDLE_IDENTIFIER = com.just2click.hivechatapp; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; }; name = Profile; }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -460,7 +506,6 @@ }; 97C147041CF9000F007C117D /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -528,11 +573,12 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = com.muhammadali.telegramchatapp; + PRODUCT_BUNDLE_IDENTIFIER = com.just2click.hivechatapp; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; @@ -555,10 +601,11 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = com.muhammadali.telegramchatapp; + PRODUCT_BUNDLE_IDENTIFIER = com.just2click.hivechatapp; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index e9821e1..f90c0f7 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -12,7 +12,7 @@ - Paste here + com.googleusercontent.apps.883352695161-d416o0l8rpp8vo35mc5orj3f9shj7m15 diff --git a/lib/AppLocalizations.dart b/lib/AppLocalizations.dart new file mode 100644 index 0000000..3b3b3b8 --- /dev/null +++ b/lib/AppLocalizations.dart @@ -0,0 +1,690 @@ +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'l10n/messages_all.dart'; + +class AppLocalizations { + AppLocalizations(this.localeName); + + static Future load(Locale locale) { + final String name = locale.countryCode.isEmpty ? locale.languageCode : locale.toString(); + final String localeName = Intl.canonicalizedLocale(name); + return initializeMessages(localeName).then((_) { + return AppLocalizations(localeName); + }); + } + + static AppLocalizations of(BuildContext context) { + return Localizations.of(context, AppLocalizations); + } + + final String localeName; + + String get welcomeToHive { + return Intl.message( + 'Welcome to Hive', + name: 'welcomeToHive', + desc: 'A description onBoarding Title', + locale: localeName, + ); + } + + String get signInLabel { + return Intl.message( + 'Sign in', + name: 'signInLabel', + desc: 'Sign In Label', + locale: localeName, + ); + } + + String get noHiveAccountYetLabel { + return Intl.message( + 'Don\'t have a Hive account yet?', + name: 'signInLabel', + desc: 'Sign In Label', + locale: localeName, + ); + } + + String get registerLabel { + return Intl.message( + 'Register here', + name: 'registerLabel', + desc: 'Register Label', + locale: localeName, + ); + } + + String get welcomeToHiveMessage { + return Intl.message( + 'Here you can connect to other communities and users near you', + name: 'welcomeToHiveMessage', + desc: 'A description onBoarding Description', + locale: localeName, + ); + } + + String get createHive { + return Intl.message( + 'Let\'s create my own hive', + name: 'createHive', + desc: 'A description onBoarding main CTA', + locale: localeName, + ); + } + + String get alreadyHaveHive { + return Intl.message( + 'I already have an account', + name: 'alreadyHaveHive', + desc: 'A description onBoarding link CTA', + locale: localeName, + ); + } + + String get verificationTitle { + return Intl.message( + 'Verification', + name: 'verificationTitle', + desc: 'Verification title', + locale: localeName, + ); + } + + String get enterYourPhoneTitle { + return Intl.message( + 'Enter your phone number', + name: 'enterYourPhoneTitle', + desc: 'Enter your phone number title', + locale: localeName + ); + } + + String get regionTitle { + return Intl.message( + 'Region', + name: 'regionTitle', + desc: 'Region title', + locale: localeName, + ); + } + + String get sendButton { + return Intl.message( + 'Send', + name: 'sendButton', + desc: 'Send button', + locale: localeName, + ); + } + + String get verifyTokenTitle { + return Intl.message( + 'Verify token', + name: 'verifyTokenTitle', + desc: 'Verify token, title', + locale: localeName, + ); + } + + String get verifyTokenInstructionTitle { + return Intl.message( + 'Please type the token you received', + name: 'verifyTokenInstructionTitle', + desc: 'Verify token instructions, title', + locale: localeName, + ); + } + + String get verificationCodeHint { + return Intl.message( + 'Verification code', + name: 'verificationCodeHint', + desc: 'Verification code, hint', + locale: localeName, + ); + } + + String verifyTokenResendTitle(time) => Intl.message( + 'You can request a new verification token in $time seconds.', + name: 'verifyTokenInstructionTitle', + args:[time], + desc: 'Verify token instructions, title', + locale: localeName, + ); + + String get resendTokenButton { + return Intl.message( + 'Resend token', + name: 'resendTokenButton', + desc: 'Resend token, button', + locale: localeName, + ); + } + + String get wrongTokenWarningSnackbar { + return Intl.message( + 'The token is wrong, please try again', + name: 'wrongTokenWarningSnackbar', + desc: 'Wrong token warning snackbar', + locale: localeName, + ); + } + + String get generalWarningSnackbar { + return Intl.message( + 'Some of the fields are not valid', + name: 'generalWarningSnackbar', + desc: 'General warning snackbar', + locale: localeName, + ); + } + + String get okContinueButton { + return Intl.message( + 'Ok, continue', + name: 'okContinueButton', + desc: 'Ok, continue button', + locale: localeName, + ); + } + + String get backButton { + return Intl.message( + 'Back', + name: 'backButton', + desc: 'Back button', + locale: localeName, + ); + } + + String get nextButton { + return Intl.message( + 'Next', + name: 'nextButton', + desc: 'Next button', + locale: localeName, + ); + } + + String get createYourNewHiveTitle { + return Intl.message( + 'Create your new hive', + name: 'createYourNewHiveTitle', + desc: 'Create your new hive title', + locale: localeName, + ); + } + + String get passwordLabel { + return Intl.message( + 'Password', + name: 'passwordLabel', + desc: 'Password label', + locale: localeName, + ); + } + + String get passwordHint { + return Intl.message( + 'Password (8 - 32 characters)', + name: 'passwordHint', + desc: 'Password hint', + locale: localeName, + ); + } + + String get confirmPasswordHint { + return Intl.message( + 'Confirm password', + name: 'confirmPasswordHint', + desc: 'Confirm password hint', + locale: localeName, + ); + } + + String get passwordsMismatchHint { + return Intl.message( + 'Passwords mismatch', + name: 'passwordsMismatchHint', + desc: 'Passwords mismatch hint', + locale: localeName, + ); + } + + String get confirmPasswordInstructions { + return Intl.message( + 'Your password must be between 8 to 32 characters', + name: 'confirmPasswordInstructions', + desc: 'Confirm password instructions', + locale: localeName, + ); + } + + String get addEmailHint { + return Intl.message( + 'Add email', + name: 'addEmailHint', + desc: 'Add email hint', + locale: localeName, + ); + } + + String get emailLabel { + return Intl.message( + 'Email', + name: 'emailLabel', + desc: 'Email', + locale: localeName, + ); + } + + String get invalidEmailWarning { + return Intl.message( + 'Please enter a valid email', + name: 'invalidEmailWarning', + desc: 'Invalid email warning', + locale: localeName, + ); + } + + String get nameHint { + return Intl.message( + 'Name (required)', + name: 'nameHint', + desc: 'Name hint', + locale: localeName, + ); + } + + String get fieldRequiredWarning { + return Intl.message( + 'This field is required', + name: 'fieldRequiredWarning', + desc: 'Field required warning', + locale: localeName, + ); + } + + String get nameRuleHint { + return Intl.message( + 'Name (3 - 22 characters)', + name: 'nameRuleHint', + desc: 'Name rule hint', + locale: localeName, + ); + } + + String get dobHint { + return Intl.message( + 'Date of birth (required)', + name: 'dobHint', + desc: 'DOB hint', + locale: localeName, + ); + } + + String get genderHint { + return Intl.message( + 'Gender (required)', + name: 'genderHint', + desc: 'Gender hint', + locale: localeName, + ); + } + + String get maleDropdownItem { + return Intl.message( + 'Male', + name: 'genderMaleDropdownItem', + desc: 'Male drop down item', + locale: localeName, + ); + } + + String get femaleDropdownItem { + return Intl.message( + 'Female', + name: 'genderFemaleDropdownItem', + desc: 'Female drop down item', + locale: localeName, + ); + } + + String get otherDropdownItem { + return Intl.message( + 'Other', + name: 'otherDropdownItem', + desc: 'Other drop down item', + locale: localeName, + ); + } + + String get customGenderHint { + return Intl.message( + 'Add your own gender description', + name: 'customGenderHint', + desc: 'Custom gender hint', + locale: localeName, + ); + } + + String get calendarDone { + return Intl.message( + 'Done', + name: 'calendarDone', + desc: 'Calendar done', + locale: localeName, + ); + } + + String get syncContactsLabel { + return Intl.message( + 'Sync contacts', + name: 'syncContactsLabel', + desc: 'Sync contacts label', + locale: localeName, + ); + } + + String get processingDataSnackBar { + return Intl.message( + 'Processing data ...', + name: 'processingDataSnackBar', + desc: 'Processing data snack bar', + locale: localeName, + ); + } + + String get categoriesTitle { + return Intl.message( + 'Categories', + name: 'categoriesTitle', + desc: 'Categories title', + locale: localeName, + ); + } + + String get categoriesSubTitle { + return Intl.message( + 'Which topics would you like to converse about?', + name: 'categoriesSubTitle', + desc: 'Categories sub title', + locale: localeName, + ); + } + + String get categoriesDoneButton { + return Intl.message( + 'That\'s it, we are good to start', + name: 'categoriesDoneButton', + desc: 'Categories Done Button', + locale: localeName, + ); + } + + String get categoriesKeyCooking { + return Intl.message( + 'Cooking', + name: 'categoriesKeyCooking', + desc: 'Categories key cooking', + locale: localeName, + ); + } + + String get categoriesKeySports { + return Intl.message( + 'Sports', + name: 'categoriesKeySports', + desc: 'Categories key sports', + locale: localeName, + ); + } + + String get categoriesKeyAnimals { + return Intl.message( + 'Animals', + name: 'categoriesKeyAnimals', + desc: 'Categories key animals', + locale: localeName, + ); + } + + String get categoriesKeyParenting { + return Intl.message( + 'Parenting', + name: 'categoriesKeyParenting', + desc: 'Categories key parenting', + locale: localeName, + ); + } + + String get categoriesKeyLoveAndDating { + return Intl.message( + 'Love & Dating', + name: 'categoriesKeyLoveAndDating', + desc: 'Categories key love and dating', + locale: localeName, + ); + } + + String get categoriesKeyGaming { + return Intl.message( + 'Gaming', + name: 'categoriesKeyGaming', + desc: 'Categories key gaming', + locale: localeName, + ); + } + + String get pleaseTryAgainWarningSnackbar { + return Intl.message( + 'Please try again', + name: 'pleaseTryAgainWarningSnackbar', + desc: 'Please try again warning snackbar', + locale: localeName, + ); + } + + String get loginSuccessMessageSnackbar { + return Intl.message( + 'Logged in ...', + name: 'loginSuccessMessageSnackbar', + desc: 'Login success message snackbar', + locale: localeName, + ); + } + + String get logoutButton { + return Intl.message( + 'Log out', + name: 'logoutButton', + desc: 'Logout Button', + locale: localeName, + ); + } + + String get noOtherPeopleFoundMessage { + return Intl.message( + 'No other people found', + name: 'noOtherPeopleFoundMessage', + desc: 'No other people found Message', + locale: localeName, + ); + } + + String get searchHereHint { + return Intl.message( + 'Search here ....', + name: 'searchHereHint', + desc: 'Search here hint', + locale: localeName, + ); + } + + String get sendAMessageHint { + return Intl.message( + 'Send a message ...', + name: 'sendAMessageHint', + desc: 'Send A Message Hint', + locale: localeName, + ); + } + + String get adminLabel { + return Intl.message( + 'Admin', + name: 'adminLabel', + desc: 'Admin label', + locale: localeName, + ); + } + + String get joinLabel { + return Intl.message( + 'Join', + name: 'joinLabel', + desc: 'Join label', + locale: localeName, + ); + } + + String get accountSettingsLabel { + return Intl.message( + 'Account Settings', + name: 'accountSettingsLabel', + desc: 'Account Settings label', + locale: localeName, + ); + } + + String get joinGroupHint { + return Intl.message( + 'You\'ve not joined any group, tap on the \'add\' icon to create a group or search for groups by tapping on the search button below.', + name: 'joinGroupHint', + desc: 'Join Group Hint', + locale: localeName, + ); + } + + String get groupLabel { + return Intl.message( + 'Group', + name: 'groupLabel', + desc: 'Group Label', + locale: localeName, + ); + } + + String get groupsLabel { + return Intl.message( + 'Groups', + name: 'groupsLabel', + desc: 'Groups Label', + locale: localeName, + ); + } + + String get cancelLabel { + return Intl.message( + 'Cancel', + name: 'cancelLabel', + desc: 'Cancel Label', + locale: localeName, + ); + } + + String get createLabel { + return Intl.message( + 'Create', + name: 'createLabel', + desc: 'Create Label', + locale: localeName, + ); + } + + String get createGroupLabel { + return Intl.message( + 'Create a group', + name: 'createGroupLabel', + desc: 'Create a group Label', + locale: localeName, + ); + } + + String get profileNameTitle { + return Intl.message( + 'Profile name', + name: 'profileNameTitle', + desc: 'Profile name title', + locale: localeName, + ); + } + + String get bioTitle { + return Intl.message( + 'Bio', + name: 'bioTitle', + desc: 'Bio title', + locale: localeName, + ); + } + + String get updatedTitle { + return Intl.message( + 'Updated', + name: 'updatedTitle', + desc: 'Updated title', + locale: localeName, + ); + } + + String get updatedSuccessfullyMessage { + return Intl.message( + 'Updated successfully', + name: 'updatedSuccessfullyMessage', + desc: 'Updated successfully message', + locale: localeName, + ); + } + + String get photoUpdatedSuccessfullyMessage { + return Intl.message( + 'Photo updated successfully', + name: 'photoUpdatedSuccessfullyMessage', + desc: 'Photo updated successfully message', + locale: localeName, + ); + } + + String get bioHint { + return Intl.message( + 'e.g About me', + name: 'bioHint', + desc: 'Bio hint', + locale: localeName, + ); + } + + String get joinedGroupHint { + return Intl.message( + 'Join the conversation as', + name: 'joinedGroupHint', + desc: 'Join group as hint', + locale: localeName, + ); + } +} + +class AppLocalizationsDelegate extends LocalizationsDelegate { + const AppLocalizationsDelegate(); + + @override + bool isSupported(Locale locale) => ['en', 'he'].contains(locale.languageCode); + + @override + Future load(Locale locale) => AppLocalizations.load(locale); + + @override + bool shouldReload(AppLocalizationsDelegate old) => false; +} \ No newline at end of file diff --git a/lib/Helpers/HelperFunctions.dart b/lib/Helpers/HelperFunctions.dart new file mode 100644 index 0000000..398cb82 --- /dev/null +++ b/lib/Helpers/HelperFunctions.dart @@ -0,0 +1,45 @@ +import 'package:shared_preferences/shared_preferences.dart'; + +class HelperFunctions { + + static String sharedPreferenceUserLoggedInKey = "ISLOGGEDIN"; + static String sharedPreferenceUserNameKey = "USERNAMEKEY"; + static String sharedPreferenceUserEmailKey = "USEREMAILKEY"; + + // Saving data to SharedPreferences + static Future saveUserLoggedInSharedPreference(bool isUserLoggedIn) async { + + SharedPreferences preferences = await SharedPreferences.getInstance(); + return await preferences.setBool(sharedPreferenceUserLoggedInKey, isUserLoggedIn); + } + + static Future saveUserNameSharedPreference(String userName) async{ + SharedPreferences preferences = await SharedPreferences.getInstance(); + return await preferences.setString(sharedPreferenceUserNameKey, userName); + } + + static Future saveUserEmailSharedPreference(String userEmail) async { + + SharedPreferences preferences = await SharedPreferences.getInstance(); + return await preferences.setString(sharedPreferenceUserEmailKey, userEmail); + } + + // fetching data from sharedPreference + static Future getUserLoggedInSharedPreference() async { + + SharedPreferences preferences = await SharedPreferences.getInstance(); + return await preferences.getBool(sharedPreferenceUserLoggedInKey); + } + + static Future getUserNameSharedPreference() async { + + SharedPreferences preferences = await SharedPreferences.getInstance(); + return await preferences.getString(sharedPreferenceUserNameKey); + } + + static Future getUserEmailSharedPreference() async { + + SharedPreferences preferences = await SharedPreferences.getInstance(); + return await preferences.getString(sharedPreferenceUserEmailKey); + } +} diff --git a/lib/Models/user.dart b/lib/Models/user.dart index 7724734..19123a7 100755 --- a/lib/Models/user.dart +++ b/lib/Models/user.dart @@ -1,13 +1,13 @@ import 'package:cloud_firestore/cloud_firestore.dart'; class User { - final String id; + final String uid; final String nickname; final String photoUrl; final String createdAt; User({ - this.id, + this.uid, this.nickname, this.photoUrl, this.createdAt, @@ -15,7 +15,7 @@ class User { factory User.fromDocument(DocumentSnapshot doc) { return User( - id: doc.documentID, + uid: doc.documentID, photoUrl: doc['photoUrl'], nickname: doc['nickname'], createdAt: doc['createdAt'], diff --git a/lib/Pages/AccountSettingsPage.dart b/lib/Pages/AccountSettingsPage.dart index 6a6fc66..ecaab04 100644 --- a/lib/Pages/AccountSettingsPage.dart +++ b/lib/Pages/AccountSettingsPage.dart @@ -4,6 +4,7 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_storage/firebase_storage.dart'; import 'package:flutter/material.dart'; +import 'package:telegramchatapp/AppLocalizations.dart'; import 'package:telegramchatapp/Widgets/ProgressWidget.dart'; import 'package:telegramchatapp/main.dart'; import 'package:fluttertoast/fluttertoast.dart'; @@ -17,21 +18,327 @@ import 'package:google_sign_in/google_sign_in.dart'; class Settings extends StatelessWidget { @override Widget build(BuildContext context) { - + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: Theme.of(context).backgroundColor, + ), + backgroundColor: Theme.of(context).primaryColor, + title: Text( + AppLocalizations.of(context).accountSettingsLabel, + style: TextStyle(color: Theme.of(context).backgroundColor, fontWeight: FontWeight.bold), + ), + centerTitle: true, + ), + body: SettingsScreen(), + ); } } - class SettingsScreen extends StatefulWidget { @override State createState() => SettingsScreenState(); } +class SettingsScreenState extends State { + BuildContext _buildContext; + TextEditingController nicknameTextEditingController = TextEditingController(); + TextEditingController aboutMeTextEditingController = TextEditingController(); + + SharedPreferences preferences; + String id = ""; + String nickname = ""; + String photoUrl = ""; + String aboutMe = ""; + File imageFileAvatar; + bool isLoading = false; + + final GoogleSignIn googleSignIn = GoogleSignIn(); + + final FocusNode nickNameFocusNode = FocusNode(); + final FocusNode aboutMeNameFocusNode = FocusNode(); + + @override + void initState() { + // TODO: implement initState + super.initState(); + + readDataFromLocal(); + } + + void readDataFromLocal () async { + preferences = await SharedPreferences.getInstance(); + + id = preferences.getString('id'); + nickname = preferences.getString('nickname'); + photoUrl = preferences.getString('photoUrl'); + aboutMe = preferences.getString('aboutMe'); + + nicknameTextEditingController = TextEditingController(text: nickname); + aboutMeTextEditingController = TextEditingController(text: aboutMe); + + setState(() { + + }); + } + + Future getImage() async { + File newImageFile = await ImagePicker.pickImage(source: ImageSource.gallery); + + if (newImageFile != null) { + setState(() { + this.imageFileAvatar = newImageFile; + isLoading = true; + }); + } + } + + Future uploadImageToFireStoreAndStorage () async { + String mFilename = id; + StorageReference storageReference = FirebaseStorage.instance.ref().child(mFilename); + StorageUploadTask storageUploadTask = storageReference.putFile(imageFileAvatar); + StorageTaskSnapshot storageTaskSnapshot; + storageUploadTask.onComplete.then((value) { + if (value.error == null) { + storageTaskSnapshot = value; + storageTaskSnapshot.ref.getDownloadURL().then((newPhotoDownloadUrl) { + photoUrl = newPhotoDownloadUrl; + + Firestore.instance.collection('users').document(id).updateData({ + "photoUrl": photoUrl, + "aboutMe": aboutMe, + "nickname": nickname, + }).then((data) async { + await preferences.setString("photoUrl", photoUrl); + setState(() { + isLoading = false; + }); + + Fluttertoast.showToast(msg: "Photo updated successfully"); + }); + }, onError: (errorMsg) { + setState(() { + isLoading = false; + }); + Fluttertoast.showToast(msg: 'Error occurred while getting download url'); + }); + } + }, onError: (errorMsg) { + setState(() { + isLoading = false; + }); + Fluttertoast.showToast(msg: errorMsg.toString()); + }); + } + + void updateDate () { + nickNameFocusNode.unfocus(); + aboutMeNameFocusNode.unfocus(); + + setState(() { + isLoading = false; + }); + + Firestore.instance.collection('users').document(id).updateData({ + "photoUrl": photoUrl, + "aboutMe": aboutMe, + "nickname": nickname, + }).then((data) async { + await preferences.setString("photoUrl", photoUrl); + await preferences.setString("aboutMe", aboutMe); + await preferences.setString("nickname", nickname); + + setState(() { + isLoading = false; + }); + + Fluttertoast.showToast(msg: "Updated successfully"); + }); + } + + Future logoutUser () async { + await FirebaseAuth.instance.signOut(); + await googleSignIn.disconnect(); + await googleSignIn.signOut(); + + this.setState(() { + isLoading = false; + }); + + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute(builder: (context) => MyApp()), ( + Route route) => false); + } -class SettingsScreenState extends State { @override Widget build(BuildContext context) { + return Stack( + children: [ + SingleChildScrollView( + child: Column( + children: [ + // Avatar + Container( + child: Center( + child: Stack( + children: [ + (imageFileAvatar == null) + ? (photoUrl != '') + ? Material( + // Display the current/old image file + child: CachedNetworkImage( + placeholder: (context, url) => Container( + child: CircularProgressIndicator( + strokeWidth: 2.0, + valueColor: AlwaysStoppedAnimation(Theme.of(context).accentColor), + ), + width: 200.0, + height: 200.0, + padding: EdgeInsets.all(20.0), + ), + imageUrl: photoUrl, + width: 200.0, + height: 200.0, + fit: BoxFit.cover, + ), + borderRadius: BorderRadius.all(Radius.circular(125.0)), + clipBehavior: Clip.hardEdge, + ) + : Icon(Icons.account_circle, size: 90.0, color: Theme.of(context).dividerColor,) + : Material( + // Display new updated image + child: Image.file( + imageFileAvatar, + width: 200.0, + height: 200.0, + fit: BoxFit.cover, + ), + borderRadius: BorderRadius.all(Radius.circular(125.0)), + clipBehavior: Clip.hardEdge, + ), + IconButton( + icon: Icon( + Icons.camera_alt, + size: 100.0, + color: Colors.white54.withOpacity(0.3), + ), + onPressed: getImage, + padding: EdgeInsets.all(0.0), + splashColor: Colors.transparent, + highlightColor: Theme.of(context).dividerColor, + iconSize: 200.0, + ), + ], + ), + ), + width: double.infinity, + margin: EdgeInsets.all(20.0), + ), + // Input fields + Column( + children: [ + Padding(padding: EdgeInsets.all(1.0), child: isLoading ? circularProgress() : Container(),), + + // User name + Container( + child: Text( + AppLocalizations.of(context).profileNameTitle + ':', + style: TextStyle( + fontStyle: FontStyle.italic, + fontWeight: FontWeight.bold, + color: Theme.of(context).primaryColor + ), + ), + margin: EdgeInsets.only(left: 10.0, bottom: 5.0, top: 10.0), + ), + Container( + child: Theme( + data: Theme.of(context).copyWith(primaryColor: Theme.of(context).primaryColor), + child: TextField( + decoration: InputDecoration( + hintText: "e.g John Doe", + contentPadding: EdgeInsets.all(5.0), + hintStyle: TextStyle(color: Colors.grey), + ), + controller: nicknameTextEditingController, + onChanged: (value) { + nickname = value; + }, + focusNode: nickNameFocusNode, + ), + ), + margin: EdgeInsets.only(left: 30.0, right: 30.0), + + ), + // About me - bio + Container( + child: Text( + AppLocalizations.of(context).bioTitle + ':', + style: TextStyle( + fontStyle: FontStyle.italic, + fontWeight: FontWeight.bold, + color: Theme.of(context).primaryColor + ), + ), + margin: EdgeInsets.only(left: 10.0, bottom: 5.0, top: 30.0), + ), + Container( + child: Theme( + data: Theme.of(context).copyWith(primaryColor: Theme.of(context).accentColor), + child: TextField( + decoration: InputDecoration( + hintText: AppLocalizations.of(context).bioHint, + contentPadding: EdgeInsets.all(5.0), + hintStyle: TextStyle(color: Theme.of(context).dividerColor), + ), + controller: aboutMeTextEditingController, + onChanged: (value) { + aboutMe = value; + }, + focusNode: aboutMeNameFocusNode, + ), + ), + margin: EdgeInsets.only(left: 30.0, right: 30.0), + + ), + ], + crossAxisAlignment: CrossAxisAlignment.start, + ), + + // Buttons + Container( + child: FlatButton( + onPressed: updateDate, + child: Text( + AppLocalizations.of(context).updatedTitle, style: TextStyle(fontSize: 16.0, ) + ), + color: Theme.of(context).primaryColor, + highlightColor: Theme.of(context).dividerColor, + splashColor: Colors.transparent, + textColor: Theme.of(context).backgroundColor, + padding: EdgeInsets.fromLTRB(30.0, 10.0, 30.0, 10.0), + ), + margin: EdgeInsets.only(top: 50.0, bottom: 1.0), + ), + // Logout button + Padding( + padding: EdgeInsets.only(left: 50.0, right: 50.0,), + child: RaisedButton( + color: Theme.of(context).errorColor, + onPressed: logoutUser, + child: Text( + AppLocalizations.of(context).logoutButton, + style: TextStyle(color: Theme.of(context).backgroundColor, fontSize: 14.0), + ), + ), + ), + ], + ), + padding: EdgeInsets.only(left: 15.0, right: 15.0), + ) + ], + ); } } diff --git a/lib/Pages/AddGroupPage.dart b/lib/Pages/AddGroupPage.dart new file mode 100644 index 0000000..b44c6b2 --- /dev/null +++ b/lib/Pages/AddGroupPage.dart @@ -0,0 +1,233 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:telegramchatapp/AppLocalizations.dart'; +import 'package:telegramchatapp/Helpers/HelperFunctions.dart'; +import 'package:telegramchatapp/pages/LoginPage.dart'; +import 'package:telegramchatapp/Pages/GroupChatPage.dart'; +import 'package:telegramchatapp/Pages/AccountSettingsPage.dart'; +import 'package:telegramchatapp/Pages/GroupSearchPage.dart'; +import 'package:telegramchatapp/Services/AuthService.dart'; +import 'package:telegramchatapp/Services/DatabaseService.dart'; +import 'package:telegramchatapp/Widgets/GroupTile.dart'; + +class AddGroupPage extends StatefulWidget { + @override + _AddGroupPageState createState() => _AddGroupPageState(); +} + +class _AddGroupPageState extends State { + + // data + final AuthService _auth = AuthService(); + FirebaseUser _user; + String _groupName; + String _email = ''; + Stream _groups; + + SharedPreferences preferences; + String id = ""; + String nickname = ""; + String photoUrl = ""; + String aboutMe = ""; + bool isLoading = false; + + @override + void initState() { + // TODO: implement initState + super.initState(); + + readDataFromLocal(); + _getUserAuthAndJoinedGroups(); + } + + void readDataFromLocal () async { + preferences = await SharedPreferences.getInstance(); + + id = preferences.getString('id'); + nickname = preferences.getString('nickname'); + photoUrl = preferences.getString('photoUrl'); + aboutMe = preferences.getString('aboutMe'); + + print(nickname); + } + + + // widgets + Widget noGroupWidget() { + return Container( + padding: EdgeInsets.symmetric(horizontal: 25.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + GestureDetector( + onTap: () { + _popupDialog(context); + }, + child: Icon(Icons.add_circle, color: Theme.of(context).dividerColor, size: 75.0) + ), + SizedBox(height: 20.0), + Text(AppLocalizations.of(context).joinGroupHint), + ], + ) + ); + } + + + + Widget groupsList() { + return StreamBuilder( + stream: _groups, + builder: (context, snapshot) { + if(snapshot.hasData) { + if(snapshot.data['groups'] != null) { + // print(snapshot.data['groups'].length); + if(snapshot.data['groups'].length != 0) { + return ListView.builder( + itemCount: snapshot.data['groups'].length, + shrinkWrap: true, + itemBuilder: (context, index) { + int reqIndex = snapshot.data['groups'].length - index - 1; + return GroupTile(userName: snapshot.data['nickname'], + groupId: _destructureId(snapshot.data['groups'][reqIndex]), + groupName: _destructureName(snapshot.data['groups'][reqIndex])); + } + ); + } + else { + return noGroupWidget(); + } + } + else { + return noGroupWidget(); + } + } + else { + return Center( + child: CircularProgressIndicator() + ); + } + }, + ); + } + + + // functions + _getUserAuthAndJoinedGroups() async { + preferences = await SharedPreferences.getInstance(); + + print(preferences); + + _user = await FirebaseAuth.instance.currentUser(); + await HelperFunctions.getUserNameSharedPreference().then((value) { + setState(() { + nickname = value; + }); + }); + print(nickname); + DatabaseService(uid: _user.uid).getUserGroups().then((snapshots) { + // print(snapshots); + setState(() { + _groups = snapshots; + }); + }); + await HelperFunctions.getUserEmailSharedPreference().then((value) { + setState(() { + _email = value; + }); + }); + } + + + String _destructureId(String res) { + // print(res.substring(0, res.indexOf('_'))); + return res.substring(0, res.indexOf('_')); + } + + + String _destructureName(String res) { + // print(res.substring(res.indexOf('_') + 1)); + return res.substring(res.indexOf('_') + 1); + } + + + void _popupDialog(BuildContext context) { + Widget cancelButton = FlatButton( + child: Text(AppLocalizations.of(context).cancelLabel), + onPressed: () { + Navigator.of(context).pop(); + }, + ); + Widget createButton = FlatButton( + child: Text(AppLocalizations.of(context).createLabel), + onPressed: () async { + if(_groupName != null) { + await DatabaseService().getUserNameByEmail(_user.email).then((val) { + DatabaseService(uid: _user.uid).createGroup(val, _groupName); + }); + + Navigator.of(context).pop(); + } + }, + ); + + AlertDialog alert = AlertDialog( + title: Text(AppLocalizations.of(context).createGroupLabel), + content: TextField( + onChanged: (val) { + _groupName = val; + }, + style: TextStyle( + fontSize: 15.0, + height: 2.0, + color: Theme.of(context).primaryColor + ) + ), + actions: [ + cancelButton, + createButton, + ], + ); + + showDialog( + context: context, + builder: (BuildContext context) { + return alert; + }, + ); + } + + + // Building the HomePage widget + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(AppLocalizations.of(context).groupsLabel, style: TextStyle(color: Theme.of(context).backgroundColor, fontWeight: FontWeight.bold)), + backgroundColor: Theme.of(context).primaryColor, + elevation: 0.0, + actions: [ + IconButton( + padding: EdgeInsets.symmetric(horizontal: 20.0), + icon: Icon(Icons.search, color: Theme.of(context).backgroundColor, size: 25.0), + onPressed: () { + Navigator.of(context).push(MaterialPageRoute(builder: (context) => GroupSearchPage())); + } + ) + ], + ), + + body: groupsList(), + floatingActionButton: FloatingActionButton( + onPressed: () { + _popupDialog(context); + }, + child: Icon(Icons.add, color: Theme.of(context).backgroundColor, size: 30.0), + backgroundColor: Theme.of(context).dividerColor, + elevation: 0.0, + ), + ); + } +} \ No newline at end of file diff --git a/lib/Pages/ChattingPage.dart b/lib/Pages/ChattingPage.dart index 60d6977..b276898 100644 --- a/lib/Pages/ChattingPage.dart +++ b/lib/Pages/ChattingPage.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_storage/firebase_storage.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:telegramchatapp/Widgets/FullImageWidget.dart'; import 'package:telegramchatapp/Widgets/ProgressWidget.dart'; @@ -13,24 +14,636 @@ import 'package:intl/intl.dart'; import 'package:shared_preferences/shared_preferences.dart'; class Chat extends StatelessWidget { + final String receiverId; + final String receiverAvatar; + final String receiverName; + + Chat({ + Key key, + @required this.receiverId, @required this.receiverAvatar, @required this.receiverName, + }); + @override Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: Colors.lightBlue, + actions: [ + Padding( + padding: EdgeInsets.all(8.0), + child: CircleAvatar( + backgroundColor: Theme.of(context).primaryColor, + backgroundImage: CachedNetworkImageProvider(receiverAvatar), + ), + ) + ], + iconTheme: IconThemeData( + color: Colors.white, + ), + title: Text( + receiverName, + style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold), + ), + centerTitle: true, + ), + body: ChatScreen(receiverId: receiverId, receiverAvatar: receiverAvatar), + ); } } class ChatScreen extends StatefulWidget { + final String receiverId; + final String receiverAvatar; + + ChatScreen({ + Key key, + @required this.receiverId, + @required this.receiverAvatar, + }) : super(key: key); @override - State createState() => ChatScreenState(); + State createState() => ChatScreenState(receiverId: receiverId, receiverAvatar: receiverAvatar); } +class ChatScreenState extends State { + final String receiverId; + final String receiverAvatar; + ChatScreenState({ + Key key, + @required this.receiverId, + @required this.receiverAvatar, + }); + final ScrollController listScrollController = ScrollController(); + final TextEditingController textEditingController = TextEditingController(); + final FocusNode focusNode = FocusNode(); + bool isDisplaySticker; + bool isLoading; + + File cameraImageFile; + File galleryImageFile; + String imageUrl; + + String chatId; + SharedPreferences preferences; + String id; + var listMessage; + + @override + void initState() { + // TODO: implement initState + super.initState(); + focusNode.addListener(onFocusChange); + + isDisplaySticker = false; + isLoading = false; + + chatId = ""; + + readLocal(); + } + + readLocal () async { + preferences = await SharedPreferences.getInstance(); + id = preferences.getString("id") ?? ""; + + if (id.hashCode <= receiverId.hashCode) { + chatId = '$id-$receiverId'; + } else { + chatId = '$receiverId-$id'; + } + + Firestore.instance.collection("users").document(id).updateData({'chattingWith': receiverId}); + + setState(() { + + }); + } + + onFocusChange () { + if (focusNode.hasFocus) { + // Hide stickers when ever soft keybaord + setState(() { + isDisplaySticker = false; + }); + } + } -class ChatScreenState extends State { @override Widget build(BuildContext context) { + return WillPopScope( + child: Stack( + children: [ + Column( + children: [ + // Create List of messages + createMessagesList(), + + // Show imoji stickers + (isDisplaySticker ? createStickers() : Container()), + + // Input Controllers + createInput(), + ], + ), + createLoading(), + ], + ), + onWillPop: onBackPress, + ); + } + + createLoading () { + return Positioned( + child: isLoading ? CircularProgressIndicator() : Container()); + } + + Future onBackPress () { + if (isDisplaySticker) { + setState(() { + isDisplaySticker = false; + }); + } else { + Navigator.pop(context); + } + + return Future.value(false); + } + + createStickers () { + return Container( + child: Column ( + children: [ + Row ( + children: [ + FlatButton ( + onPressed: () => onSendMessage("mimi1", 2), + child: Image.asset ( + "images/mimi1.gif", + width: 50.0, + height: 50.0, + fit: BoxFit.cover, + ), + ), + FlatButton ( + onPressed: () => onSendMessage("mimi2", 2), + child: Image.asset ( + "images/mimi2.gif", + width: 50.0, + height: 50.0, + fit: BoxFit.cover, + ), + ), + FlatButton ( + onPressed: () => onSendMessage("mimi3", 2), + child: Image.asset ( + "images/mimi3.gif", + width: 50.0, + height: 50.0, + fit: BoxFit.cover, + ), + ), + ], + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + ), + Row ( + children: [ + FlatButton ( + onPressed: () => onSendMessage("mimi4", 2), + child: Image.asset ( + "images/mimi4.gif", + width: 50.0, + height: 50.0, + fit: BoxFit.cover, + ), + ), + FlatButton ( + onPressed: () => onSendMessage("mimi5", 2), + child: Image.asset ( + "images/mimi5.gif", + width: 50.0, + height: 50.0, + fit: BoxFit.cover, + ), + ), + FlatButton ( + onPressed: () => onSendMessage("mimi6", 2), + child: Image.asset ( + "images/mimi6.gif", + width: 50.0, + height: 50.0, + fit: BoxFit.cover, + ), + ), + ], + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + ), + Row ( + children: [ + FlatButton ( + onPressed: () => onSendMessage("mimi7", 2), + child: Image.asset ( + "images/mimi7.gif", + width: 50.0, + height: 50.0, + fit: BoxFit.cover, + ), + ), + FlatButton ( + onPressed: () => onSendMessage("mimi8", 2), + child: Image.asset ( + "images/mimi8.gif", + width: 50.0, + height: 50.0, + fit: BoxFit.cover, + ), + ), + FlatButton ( + onPressed: () => onSendMessage("mimi9", 2), + child: Image.asset ( + "images/mimi9.gif", + width: 50.0, + height: 50.0, + fit: BoxFit.cover, + ), + ) + ], + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + ) + ], + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + ), + decoration: BoxDecoration( + border: Border (top: BorderSide(color: Colors.grey, width: 0.5),), + color: Colors.white, + ), + padding: EdgeInsets.all(5.0), + height: 180.0, + ); + } + + void getSticker () { + focusNode.unfocus(); + setState(() { + isDisplaySticker = !isDisplaySticker; + }); + } + + createMessagesList () { + return Flexible( + child: chatId == "" + ? Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation(Colors.lightBlueAccent),), + ) + : StreamBuilder( + stream: Firestore.instance.collection("messages") + .document(chatId) + .collection(chatId).orderBy("timestamp", descending: true) + .limit(20).snapshots(), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation(Colors.lightBlueAccent),), + ); + } else { + listMessage = snapshot.data.documents; + return ListView.builder( + padding: EdgeInsets.all(10.0), + itemBuilder: (context, index) => createItem(index, snapshot.data.documents[index]), + itemCount: snapshot.data.documents.length, + reverse: true, + controller: listScrollController, + ); + } + } + ) + ); + } + + bool isLastMessageRight (int index) { + if ((index > 0 && listMessage != null && listMessage[index - 1]["idFrom"] != id) || index == 0) { + return true; + } + + return false; + } + + + bool isLastMessageLeft (int index) { + if ((index > 0 && listMessage != null && listMessage[index - 1]["idFrom"] == id) || index == 0) { + return true; + } + + return false; + } + + Widget displayTextMessage (int index, DocumentSnapshot document, String type) { + var textColor = type == 'me' ? Colors.white : Colors.black; + return Container( + child: Text( + document["content"], + style: TextStyle( + color: textColor, + fontWeight: FontWeight.w500 + ) + ), + padding: EdgeInsets.fromLTRB(15.0, 10.0, 15.0, 10.0), + width: 200.0, + decoration: BoxDecoration ( + color: Colors.lightBlueAccent, + borderRadius: BorderRadius.circular(8.0), + ), + margin: EdgeInsets.only(bottom: isLastMessageRight(index) ? 20.0 : 10.0), + ); + } + + Widget displayImage (int index, DocumentSnapshot document, String type) { + return Container ( + child: FlatButton( + child: Material( + child: CachedNetworkImage ( + placeholder: (context, url) => Container( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation(Colors.lightBlueAccent), + ), + width: 200.0, + height: 200.0, + padding: EdgeInsets.all(70.0), + decoration: BoxDecoration( + color: Colors.grey, + borderRadius: BorderRadius.all(Radius.circular(8)), + ), + ), + errorWidget: (context, url, error) => Material ( + child: Image.asset("images/img_not_available.jpeg", width: 200.0, height: 200.0, fit: BoxFit.cover), + borderRadius: BorderRadius.all(Radius.circular(8)), + clipBehavior: Clip.hardEdge, + ), + imageUrl: document["content"], + width: 200.0, + height: 200.0, + fit: BoxFit.cover, + ), + borderRadius: BorderRadius.all(Radius.circular(8)), + clipBehavior: Clip.hardEdge, + ), + onPressed: () { + Navigator.push(context, MaterialPageRoute( + builder: (context) => FullPhoto(url: document["content"]) + )); + }, + ), + margin: EdgeInsets.only(bottom: isLastMessageRight(index) ? 20.0 : 10.0), + ); + } + + Widget displaySticker (int index, DocumentSnapshot document, String type) { + return Container( + child: Image.asset( + "images/${document['content']}.gif", + width: 200.0, + height: 200.0, + fit: BoxFit.cover, + ), + margin: EdgeInsets.only(bottom: isLastMessageRight(index) ? 20.0 : 10.0), + ); + } + + Widget createItem (int index, DocumentSnapshot document) { + // Sender messages - right side + if (document["idFrom"] == id) { + return Row ( + children: [ + // Text message + document["type"] == 0 + ? displayTextMessage(index, document, 'me') + : document["type"] == 1 + ? displayImage(index, document, 'me') + // Sticker .gif message + : displaySticker(index, document, 'me'), + ], + mainAxisAlignment: MainAxisAlignment.end, + ); + } else { // Receiver side - left side + return Container ( + child: Column ( + children: [ + Row ( + children: [ + isLastMessageLeft(index) + ? Material ( + child: CachedNetworkImage( + placeholder: (context, url) => Container( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation(Colors.lightBlueAccent), + ), + width: 35.0, + height: 35.0, + padding: EdgeInsets.all(10.0), + ), + imageUrl: receiverAvatar, + width: 35.0, + height: 35.0, + fit: BoxFit.cover, + ), + borderRadius: BorderRadius.all( + Radius.circular(18.0), + ), + clipBehavior: Clip.hardEdge, + ) + : Container (width: 35.0), + // Display messages + document["type"] == 0 + ? displayTextMessage(index, document, null) + : document["type"] == 1 + ? displayImage(index, document, null) + // Sticker .gif message + : displaySticker(index, document, null), + ], + mainAxisAlignment: MainAxisAlignment.start, + ), + + // Message time + isLastMessageLeft(index) + ? Container ( + child: Text( + DateFormat("dd MMMM, hh:mm:aa") + .format(DateTime.fromMillisecondsSinceEpoch(int.parse(document["timestamp"]))), + style: TextStyle (color: Colors.grey, fontSize: 12.0, fontStyle: FontStyle.italic), + ), + margin: EdgeInsets.only(left: 50.0, top: 50.0, bottom: 5.0), + ) + : Container () + ], + crossAxisAlignment: CrossAxisAlignment.start, + ), + margin: EdgeInsets.only(bottom: 10.0), + ); + } + } + + createInput () { + return Container( + child: Row( + children: [ + // Pick image icon button - TODO: extend to have selection between gallery and camera + Material( + child: Container( + margin: EdgeInsets.symmetric(horizontal: 1.0), + child: IconButton( + icon: Icon(Icons.image), + color: Colors.lightBlueAccent, + onPressed: getGalleryImage, + ), + ), + color: Colors.white, + ), + Material( + child: Container( + margin: EdgeInsets.symmetric(horizontal: 1.0), + child: IconButton( + icon: Icon(Icons.camera_alt), + color: Colors.lightBlueAccent, + onPressed: getCameraImage, + ), + ), + color: Colors.white, + ), + // Emoji button + Material( + child: Container( + margin: EdgeInsets.symmetric(horizontal: 1.0), + child: IconButton( + icon: Icon(Icons.face), + color: Colors.lightBlueAccent, + onPressed: getSticker, + ), + ), + color: Colors.white, + ), + // Text field - input + Flexible( + child: Container( + child: TextField( + style: TextStyle( + color: Colors.black, + fontSize: 15.0, + ), + controller: textEditingController, + decoration: InputDecoration.collapsed( + hintText: "Write here...", + hintStyle: TextStyle ( + color: Colors.grey, + ) + ), + focusNode: focusNode, + ) + ), + ), + + // Send message icon button + Material( + child: Container ( + margin: EdgeInsets.symmetric(horizontal: 8.0), + child: IconButton ( + icon: Icon(Icons.send), + color: Colors.lightBlueAccent, + onPressed: () => onSendMessage(textEditingController.text, 0), + ), + ), + color: Colors.white, + ) + ], + ), + width: double.infinity, + height: 50.0, + decoration: BoxDecoration ( + border: Border ( + top: BorderSide ( + color: Colors.grey, + width: 0.5 + ) + ), + color: Colors.white, + ), + ); + } + + Future getGalleryImage () async { + final imagePicker = ImagePicker(); + final pickedFile = await imagePicker.getImage(source: ImageSource.gallery); + setState(() { + if (pickedFile != null) { + galleryImageFile = File(pickedFile.path); + isLoading = true; + } + + uploadImageFile(galleryImageFile); + }); + } + + Future getCameraImage () async { + final imagePicker = ImagePicker(); + final pickedFile = await imagePicker.getImage(source: ImageSource.camera); + setState(() { + if (pickedFile != null) { + cameraImageFile = File(pickedFile.path); + isLoading = true; + } + + uploadImageFile(cameraImageFile); + }); + } + + Future uploadImageFile (imageFile) async { + String fileName = DateTime.now().millisecondsSinceEpoch.toString(); + StorageReference storageReference = FirebaseStorage.instance.ref().child("Chat Images").child(fileName); + + StorageUploadTask storageUploadTask = storageReference.putFile(imageFile); + StorageTaskSnapshot storageTaskSnapshot = await storageUploadTask.onComplete; + + storageTaskSnapshot.ref.getDownloadURL().then((downloadUrl) { + imageUrl = downloadUrl; + setState(() { + isLoading = false; + onSendMessage(imageUrl, 1); + }); + }, onError: (error) { + setState(() { + isLoading = false; + }); + Fluttertoast.showToast(msg: "Error: " + error); + }); } + void onSendMessage (String messageContent, int type) { + // type = 0 it's a text message + // type = 1 it's an image file + // type = 2 it's an Emoji + + if (messageContent != "") { + textEditingController.clear(); + + var docRef = Firestore.instance.collection("messages") + .document(chatId) + .collection(chatId).document(DateTime.now().millisecondsSinceEpoch.toString()); + + Firestore.instance.runTransaction((transaction) async { + await transaction.set(docRef, { + "idFrom": id, + "idTo": receiverId, + "timestamp": DateTime.now().millisecondsSinceEpoch.toString(), + "content": messageContent, + "type": type, + }, ); + }); + listScrollController.animateTo(0.0, duration: Duration(microseconds: 300), curve: Curves.easeOut); + } else { + Fluttertoast.showToast(msg: "Empty message, cannot be sent"); + } + } } diff --git a/lib/Pages/GroupChatPage.dart b/lib/Pages/GroupChatPage.dart new file mode 100644 index 0000000..6834d4f --- /dev/null +++ b/lib/Pages/GroupChatPage.dart @@ -0,0 +1,136 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:flutter/material.dart'; +import 'package:telegramchatapp/AppLocalizations.dart'; +import 'package:telegramchatapp/Services/DatabaseService.dart'; +import 'package:telegramchatapp/Widgets/MessageTileWidget.dart'; + +class GroupChatPage extends StatefulWidget { + final String groupId; + final String userName; + final String groupName; + + GroupChatPage ({ + this.groupId, + this.userName, + this.groupName + }); + + @override + _GroupChatPageState createState () => _GroupChatPageState(); +} + +class _GroupChatPageState extends State { + + Stream _chats; + TextEditingController messageEditingController = new TextEditingController(); + + Widget _chatMessage () { + return StreamBuilder ( + stream: _chats, + builder: (context, snapshot) { + return snapshot.hasData ? ListView.builder ( + itemCount: snapshot.data.documents.length, + itemBuilder: (context, index) { + return MessageTile ( + message: snapshot.data.documents[index].data["message"], + sender: snapshot.data.documents[index].data["sender"], + sentByMe: widget.userName == snapshot.data.documents[index].data["sender"], + ); + }, + ) + : + Container(); + }, + ); + } + + _sendMessage () { + if (messageEditingController.text.isNotEmpty) { + Map chatMessageMap = { + "message": messageEditingController.text, + "sender": widget.userName, + "time": DateTime.now().microsecondsSinceEpoch + }; + + DatabaseService().sendMessage(widget.groupId, chatMessageMap); + + setState(() { + messageEditingController.text = ""; + }); + } + } + + @override + void initState () { + super.initState(); + DatabaseService().getChats(widget.groupId).then((value) { + setState(() { + _chats = value; + }); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold ( + appBar: AppBar ( + title: Text(widget.groupName, style: TextStyle (color: Theme.of(context).backgroundColor)), + centerTitle: true, + backgroundColor: Theme.of(context).primaryColor, + elevation: 0.0, + ), + body: Container ( + child: Stack ( + children: [ + _chatMessage(), + Container ( + alignment: Alignment.bottomCenter, + width: MediaQuery.of(context).size.width, + child: Container ( + padding: EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0), + color: Theme.of(context).dividerColor, + child: Row ( + children: [ + Expanded ( + child: TextField ( + controller: messageEditingController, + style: TextStyle ( + color: Theme.of(context).backgroundColor + ), + decoration: InputDecoration ( + hintText: AppLocalizations.of(context).sendAMessageHint, + hintStyle: TextStyle ( + color: Theme.of(context).dividerColor, + fontSize: 16.0 + ), + border: InputBorder.none + ), + ), + ), + + SizedBox (width: 12.0,), + + GestureDetector ( + onTap: () { + _sendMessage(); + }, + child: Container ( + height: 50.0, + width: 50.0, + decoration: BoxDecoration ( + color: Theme.of(context).buttonColor, + borderRadius: BorderRadius.circular(50.0) + ), + child: Center(child: Icon(Icons.send, color: Theme.of(context).backgroundColor,),), + ), + ) + ] + ), + ), + ) + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/Pages/GroupSearchPage.dart b/lib/Pages/GroupSearchPage.dart new file mode 100644 index 0000000..829d1bd --- /dev/null +++ b/lib/Pages/GroupSearchPage.dart @@ -0,0 +1,219 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter/material.dart'; +import 'package:telegramchatapp/AppLocalizations.dart'; +import 'package:telegramchatapp/Helpers/HelperFunctions.dart'; +import 'package:telegramchatapp/Pages/GroupChatPage.dart'; +import 'package:telegramchatapp/Services/DatabaseService.dart'; + +class GroupSearchPage extends StatefulWidget { + @override + _GroupSearchPageState createState() => _GroupSearchPageState(); +} + +class _GroupSearchPageState extends State { + + // data + TextEditingController searchEditingController = new TextEditingController(); + QuerySnapshot searchResultSnapshot; + bool isLoading = false; + bool hasUserSearched = false; + bool _isJoined = false; + String nickname = ''; + FirebaseUser _user; + final GlobalKey _scaffoldKey = new GlobalKey(); + + + // initState() + @override + void initState() { + super.initState(); + _getCurrentUserNameAndUid(); + } + + + // functions + _getCurrentUserNameAndUid() async { + await HelperFunctions.getUserNameSharedPreference().then((value) { + nickname = value; + }); + _user = await FirebaseAuth.instance.currentUser(); + } + + + _initiateSearch() async { + if(searchEditingController.text.isNotEmpty){ + setState(() { + isLoading = true; + }); + await DatabaseService().searchByName(searchEditingController.text).then((snapshot) { + searchResultSnapshot = snapshot; + //print("$searchResultSnapshot"); + setState(() { + isLoading = false; + hasUserSearched = true; + }); + }); + } + } + + + void _showScaffold(String message) { + _scaffoldKey.currentState.showSnackBar( + SnackBar( + backgroundColor: Colors.blueAccent, + duration: Duration(milliseconds: 1500), + content: Text(message, textAlign: TextAlign.center, style: TextStyle(fontSize: 17.0)), + ) + ); + } + + + _joinValueInGroup(String userName, String groupId, String groupName, String admin) async { + bool value = await DatabaseService(uid: _user.uid).isUserJoined(groupId, groupName, userName); + setState(() { + _isJoined = value; + }); + } + + + // widgets + Widget groupList() { + return hasUserSearched ? ListView.builder( + shrinkWrap: true, + itemCount: searchResultSnapshot.documents.length, + itemBuilder: (context, index) { + return groupTile( + nickname, + searchResultSnapshot.documents[index].data["groupId"], + searchResultSnapshot.documents[index].data["groupName"], + searchResultSnapshot.documents[index].data["admin"], + ); + } + ) + : + Container(); + } + + + Widget groupTile(String userName, String groupId, String groupName, String admin){ + _joinValueInGroup(userName, groupId, groupName, admin); + return ListTile( + contentPadding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0), + leading: CircleAvatar( + radius: 30.0, + backgroundColor: Colors.blueAccent, + child: Text(groupName.substring(0, 1).toUpperCase(), style: TextStyle(color: Colors.white)) + ), + title: Text(groupName, style: TextStyle(fontWeight: FontWeight.bold)), + subtitle: Text("Admin: $admin"), + trailing: InkWell( + onTap: () async { + await DatabaseService(uid: _user.uid).togglingGroupJoin(groupId, groupName, userName); + if(_isJoined) { + setState(() { + _isJoined = !_isJoined; + }); + // await DatabaseService(uid: _user.uid).userJoinGroup(groupId, groupName, userName); + _showScaffold('Successfully joined the group "$groupName"'); + Future.delayed(Duration(milliseconds: 2000), () { + Navigator.of(context).push(MaterialPageRoute(builder: (context) => GroupChatPage(groupId: groupId, userName: userName, groupName: groupName))); + }); + } + else { + setState(() { + _isJoined = !_isJoined; + }); + _showScaffold('Left the group "$groupName"'); + } + }, + child: _isJoined ? Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10.0), + color: Colors.black87, + border: Border.all( + color: Colors.white, + width: 1.0 + ) + ), + padding: EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0), + child: Text('Joined', style: TextStyle(color: Colors.white)), + ) + : + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10.0), + color: Colors.blueAccent, + ), + padding: EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0), + child: Text(AppLocalizations.of(context).joinLabel, style: TextStyle(color: Colors.white)), + ), + ), + ); + } + + + // building the search page widget + @override + Widget build(BuildContext context) { + return Scaffold( + key: _scaffoldKey, + appBar: AppBar( + elevation: 0.0, + backgroundColor: Colors.black87, + title: Text('Search', style: TextStyle(fontSize: 27.0, fontWeight: FontWeight.bold, color: Colors.white)), + ), + body: // isLoading ? Container( + // child: Center( + // child: CircularProgressIndicator(), + // ), + // ) + // : + Container( + child: Column( + children: [ + Container( + padding: EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0), + color: Colors.grey[700], + child: Row( + children: [ + Expanded( + child: TextField( + controller: searchEditingController, + style: TextStyle( + color: Colors.white, + ), + decoration: InputDecoration( + hintText: "Search groups...", + hintStyle: TextStyle( + color: Colors.white38, + fontSize: 16, + ), + border: InputBorder.none + ), + ), + ), + GestureDetector( + onTap: (){ + _initiateSearch(); + }, + child: Container( + height: 40, + width: 40, + decoration: BoxDecoration( + color: Colors.blueAccent, + borderRadius: BorderRadius.circular(40) + ), + child: Icon(Icons.search, color: Colors.white) + ) + ) + ], + ), + ), + isLoading ? Container(child: Center(child: CircularProgressIndicator())) : groupList() + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/Pages/HomePage.dart b/lib/Pages/HomePage.dart index 2f6947a..c09d842 100644 --- a/lib/Pages/HomePage.dart +++ b/lib/Pages/HomePage.dart @@ -1,34 +1,203 @@ import 'dart:async'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; +import 'package:telegramchatapp/Models/user.dart'; +import 'package:telegramchatapp/main.dart'; import 'package:telegramchatapp/Pages/ChattingPage.dart'; -import 'package:telegramchatapp/models/user.dart'; +import 'package:telegramchatapp/Pages/AddGroupPage.dart'; import 'package:telegramchatapp/Pages/AccountSettingsPage.dart'; import 'package:telegramchatapp/Widgets/ProgressWidget.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:google_sign_in/google_sign_in.dart'; +import 'package:telegramchatapp/AppLocalizations.dart'; class HomeScreen extends StatefulWidget { + + final String currentUserId; + + HomeScreen({ Key key, @required this.currentUserId }) : super (key: key); + @override - State createState() => HomeScreenState(); + State createState() => HomeScreenState(currentUserId: currentUserId); } class HomeScreenState extends State { + BuildContext _buildContext; + + HomeScreenState({Key key, @required this.currentUserId}); + + TextEditingController searchTextEditingController = TextEditingController(); + Future futureSearchResults; + final String currentUserId; + + final GoogleSignIn googleSignIn = GoogleSignIn(); + + homePageHeader () { + return AppBar ( + automaticallyImplyLeading: false, + actions: [ + IconButton(icon: Icon(Icons.settings_applications, size: 30.0, color: Theme.of(context).backgroundColor,), + onPressed: () { + Navigator.push(context, MaterialPageRoute(builder: (context) => Settings())); + }), + IconButton(icon: Icon(Icons.group_add, size: 30.0, color: Theme.of(context).backgroundColor,), + onPressed: () { + Navigator.push(context, MaterialPageRoute(builder: (context) => AddGroupPage())); + }) + ], + backgroundColor: Theme.of(context).primaryColor, + title: Container( + margin: new EdgeInsets.only(bottom: 4.0), + child: TextFormField( + style: TextStyle(fontSize: 18.0, color: Theme.of(context).backgroundColor), + controller: searchTextEditingController, + decoration: InputDecoration( + hintText: AppLocalizations.of(context).searchHereHint, + hintStyle: TextStyle(color: Theme.of(context).backgroundColor), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Theme.of(context).disabledColor), + ), + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Theme.of(context).backgroundColor), + ), + filled: true, + prefixIcon: Icon(Icons.person_pin, color: Theme.of(context).backgroundColor, size: 30.0,), + suffixIcon: IconButton( + icon: Icon(Icons.clear, color: Theme.of(context).backgroundColor,), + onPressed: emptyTextFormField(), + ) + ), + onFieldSubmitted: controlSearch, + ), + ), + ); + } + + emptyTextFormField () { + searchTextEditingController.clear(); + } + + controlSearch (String username) { + Future allFoundUsers = Firestore.instance.collection('users') + .where('nickname', isGreaterThanOrEqualTo: username).getDocuments(); + + setState(() { + futureSearchResults = allFoundUsers; + }); + } + @override Widget build(BuildContext context) { + return Scaffold( + appBar: homePageHeader(), + body: futureSearchResults == null ? displayNoResultsScreen(): displayUsersFoundScreen() + ); + } + displayUsersFoundScreen() { + return FutureBuilder( + future: futureSearchResults, + builder: (context, dataSnapshot) { + if (!dataSnapshot.hasData) { + return circularProgress(); + } + + List searchUserResult = []; + dataSnapshot.data.documents.forEach((document) { + User eachUser = User.fromDocument(document); + UserResult userResult = UserResult(eachUser); + + if (currentUserId != document['id']) { + searchUserResult.add(userResult); + } + }); + return ListView(children: searchUserResult); + } + ); } -} + displayNoResultsScreen() { + final Orientation orientation = MediaQuery.of(context).orientation; + + return Container( + child: Center ( + child: ListView( + shrinkWrap: true, + children: [ + Icon(Icons.group, color: Theme.of(context).primaryColor, size: 200.0), + Text( + AppLocalizations.of(context).noOtherPeopleFoundMessage, + textAlign: TextAlign.center, + style: TextStyle(color: Theme.of(context).primaryColor, fontSize: 50.0, fontWeight: FontWeight.w500), + ) + ], + ) + ) + ); + } +} class UserResult extends StatelessWidget { + final User eachUser; + UserResult(this.eachUser); + @override Widget build(BuildContext context) { + return Padding( + padding: EdgeInsets.all(4.0), + child: Container( + color: Theme.of(context).backgroundColor, + child: Column( + children: [ + GestureDetector( + onTap: () => sendUserToChatPage(context), + child: ListTile( + leading: CircleAvatar( + backgroundColor: Theme.of(context).buttonColor, + backgroundImage: CachedNetworkImageProvider(eachUser.photoUrl), + + ), + title: Text( + eachUser.nickname, + style: TextStyle( + color: Theme.of(context).buttonColor, + fontSize: 16.0, + fontWeight: FontWeight.bold + ), + ), + subtitle: Text( + "joined: " + DateFormat("dd MMMM, yyyy") + .format(DateTime.fromMillisecondsSinceEpoch((int.parse(eachUser.createdAt) / 1000).floor())), + style: TextStyle( + color: Colors.grey, + fontSize: 14.0, + fontStyle: FontStyle.italic, + ), + ), + ), + ) + ], + ), + ), + ); + } + sendUserToChatPage (BuildContext context) { + Future.delayed(Duration.zero, () { + Navigator.push(context, MaterialPageRoute( + builder: (context) => + Chat( + receiverId: eachUser.uid, + receiverAvatar: eachUser.photoUrl, + receiverName: eachUser.nickname, + ) + )); + }); } } diff --git a/lib/Pages/LoginPage.dart b/lib/Pages/LoginPage.dart index 83a2412..6ca7a00 100644 --- a/lib/Pages/LoginPage.dart +++ b/lib/Pages/LoginPage.dart @@ -1,25 +1,334 @@ import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:flutter/gestures.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; -import 'package:flutter/material.dart'; -import 'package:telegramchatapp/Pages/HomePage.dart'; -import 'package:telegramchatapp/Widgets/ProgressWidget.dart'; + import 'package:fluttertoast/fluttertoast.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:google_sign_in/google_sign_in.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:telegramchatapp/Pages/HomePage.dart'; +import 'package:telegramchatapp/Shared/constants.dart'; +import 'package:telegramchatapp/Widgets/ProgressWidget.dart'; +import 'package:telegramchatapp/Helpers/HelperFunctions.dart'; +import 'package:telegramchatapp/Services/DatabaseService.dart'; + +import 'package:telegramchatapp/Services/AuthService.dart'; +import 'package:telegramchatapp/Pages/PhoneLoginPage.dart'; + +import 'package:telegramchatapp/AppLocalizations.dart'; class LoginScreen extends StatefulWidget { + LoginScreen ({Key key}): super(key: key); @override LoginScreenState createState() => LoginScreenState(); } class LoginScreenState extends State { + BuildContext _buildContext; + + double _deviceHeight; + double _deviceWidth; + + final _formKey = GlobalKey(); + + final TextEditingController _emailController = TextEditingController(); + final TextEditingController _passwordController = TextEditingController(); + + final GoogleSignIn googleSignIn = GoogleSignIn(); + final FirebaseAuth firebaseAuth = FirebaseAuth.instance; + + SharedPreferences preferences; + + bool isLoggedIn = false; + bool isLoading = false; + FirebaseUser currentUser; + + final AuthService _auth = AuthService(); + bool _isLoading = false; + + // text field state + String email = ''; + String password = ''; + String error = ''; + + @override + void initState() { + // TODO: implement initState + super.initState(); + + isSingedIn(); + } + + void isSingedIn() async { + this.setState(() { + isLoggedIn = true; + }); + + preferences = await SharedPreferences.getInstance(); + + isLoggedIn = await googleSignIn.isSignedIn(); + if (isLoggedIn) { + Navigator.push(context, MaterialPageRoute(builder: (context) => HomeScreen(currentUserId: preferences.getString("id")))); + } + + this.setState(() { + isLoading = false; + }); + } + + _onSignIn() async { + if (_formKey.currentState.validate()) { + setState(() { + _isLoading = true; + }); + + await _auth.signInWithEmailAndPassword(email, password).then((result) async { + if (result != null) { + QuerySnapshot userInfoSnapshot = await DatabaseService().getUserData(email); + + + await HelperFunctions.saveUserLoggedInSharedPreference(true); + await HelperFunctions.saveUserEmailSharedPreference(email); + await HelperFunctions.saveUserNameSharedPreference( + userInfoSnapshot.documents[0].data['nickname'] + ); + + print("Signed In"); + await HelperFunctions.getUserLoggedInSharedPreference().then((value) { + print("Logged in: $value"); + }); + await HelperFunctions.getUserEmailSharedPreference().then((value) { + print("Email: $value"); + }); + await HelperFunctions.getUserNameSharedPreference().then((value) { + print("Full Name: $value"); + }); + + Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (context) => HomeScreen(currentUserId: userInfoSnapshot.documents[0].data['uid']))); + } + else { + setState(() { + error = 'Error signing in!'; + _isLoading = false; + }); + } + }); + } + } + @override Widget build(BuildContext context) { + _buildContext = context; + + _deviceHeight = MediaQuery + .of(context) + .size + .height; + _deviceWidth = MediaQuery + .of(context) + .size + .width; + return Scaffold( + backgroundColor: Theme + .of(context) + .backgroundColor, + body: Container( + alignment: Alignment.center, + padding: EdgeInsets.symmetric(horizontal: 40.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + AppLocalizations.of(_buildContext).welcomeToHive, + style: GoogleFonts.montserrat( + textStyle: TextStyle( + fontSize: 24.0, + fontWeight: FontWeight.w700, + color: Theme.of(_buildContext).buttonColor + ) + ), + textAlign: TextAlign.center, + ), + _backgroundImageWidget(), + // Text(AppLocalizations.of(context).signInLabel, + // style: GoogleFonts.montserrat( + // textStyle: TextStyle( + // fontSize: 24.0, + // fontWeight: FontWeight.w700, + // color: Theme.of(_buildContext).buttonColor + // ) + // ), + // textAlign: TextAlign.center, + // ), + TextFormField( + style: TextStyle(color: Theme.of(_buildContext).buttonColor, + fontSize: 16.0, + fontWeight: FontWeight.w700, + ), + decoration: textInputDecoration.copyWith(labelText: AppLocalizations.of(context).emailLabel), + validator: (val) { + return RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+").hasMatch(val) ? null : AppLocalizations.of(context).invalidEmailWarning; + }, + + onChanged: (val) { + setState(() { + email = val; + }); + }, + ), + TextFormField( + style: TextStyle(color: Theme.of(_buildContext).buttonColor, + fontSize: 16.0, + fontWeight: FontWeight.w700, + ), + decoration: textInputDecoration.copyWith(labelText: AppLocalizations.of(context).passwordLabel), + validator: (val) => val.length < 6 ? AppLocalizations.of(context).passwordHint : null, + obscureText: true, + onChanged: (val) { + setState(() { + password = val; + }); + }, + ), + SizedBox( + width: double.infinity, + height: 50.0, + child: RaisedButton( + elevation: 0.0, + color: Theme.of(context).primaryColor, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30.0)), + child: Text(AppLocalizations.of(context).signInLabel, style: TextStyle(color: Theme.of(context).buttonColor, fontSize: 16.0)), + onPressed: () { + _onSignIn(); + } + ), + ), + Text.rich( + TextSpan( + text: AppLocalizations.of(context).noHiveAccountYetLabel + ' ', + style: TextStyle(color: Theme.of(context).buttonColor, fontSize: 14.0), + children: [ + TextSpan( + text: AppLocalizations.of(context).registerLabel, + style: TextStyle( + color: Theme.of(context).buttonColor, + decoration: TextDecoration.underline + ), + recognizer: TapGestureRecognizer()..onTap = () { + // widget.toggleView(); + }, + ), + ], + ), + ), + GestureDetector( + onTap: controlSignInWithGoogle, + child: Center( + child: Column( + children: [ + Container( + width: 270.0, + height: 65.0, + decoration: BoxDecoration( + image: DecorationImage ( + image: AssetImage ("assets/images/google_signin_button.png"), + fit: BoxFit.cover + ), + )), + Padding( + padding: EdgeInsets.all(1.0), + child: isLoading ? circularProgress() : Container(),)], + ), + ), + ) + ], + ), + ), + ); } + Future controlSignInWithGoogle () async { + var preferences = await SharedPreferences.getInstance(); + + this.setState(() { + isLoading = true; + }); + + GoogleSignInAccount googleUser = await googleSignIn.signIn(); + GoogleSignInAuthentication googleSignInAuthentication = await googleUser.authentication; + + final AuthCredential credential = GoogleAuthProvider.getCredential( + idToken: googleSignInAuthentication.idToken, + accessToken: googleSignInAuthentication.accessToken); + + FirebaseUser firebaseUser = (await firebaseAuth.signInWithCredential(credential)).user; + + // Sign in success + if (firebaseUser != null) { + // Check if the user has already signed up + final QuerySnapshot resultQuery = await Firestore.instance + .collection("users").where("id", isEqualTo: firebaseUser.uid).getDocuments(); + + final List documentSnapshot = resultQuery.documents; + + if (documentSnapshot.length == 0) { + // This is a new user and we should register it + Firestore.instance.collection("users").document(firebaseUser.uid).setData({ + "nickname": firebaseUser.displayName, + "photoUrl": firebaseUser.photoUrl, + "id": firebaseUser.uid, + "aboutMe": "I'm using Hive Chat", + "createdAt": DateTime.now().microsecondsSinceEpoch.toString(), + "chattingWith": null, + "email": googleUser.email + }); + + // Write data + currentUser = firebaseUser; + await preferences.setString("id", currentUser.uid); + await preferences.setString("nickname", currentUser.displayName); + await preferences.setString("photoUrl", currentUser.photoUrl); + await preferences.setString("email", documentSnapshot[0]["email"]); + } else { // User exists + currentUser = firebaseUser; + await preferences.setString("id", documentSnapshot[0]["id"]); + await preferences.setString("nickname", documentSnapshot[0]["nickname"]); + await preferences.setString("photoUrl", documentSnapshot[0]["photoUrl"]); + await preferences.setString("email", documentSnapshot[0]["email"]); + await preferences.setString("aboutMe", documentSnapshot[0]["aboutMe"]); + } + + Fluttertoast.showToast(msg: AppLocalizations.of(_buildContext).loginSuccessMessageSnackbar ,); + this.setState(() { + isLoading = false; + }); + + Navigator.push(context, MaterialPageRoute(builder: (context) => HomeScreen(currentUserId: firebaseUser.uid))); + + } else { // Sign in failed + Fluttertoast.showToast(msg: AppLocalizations.of(_buildContext).pleaseTryAgainWarningSnackbar ,); + this.setState(() { + isLoading = false; + }); + } + } + + Widget _backgroundImageWidget() { + return Container( + height: _deviceHeight * .25, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage("assets/images/hive-bee-on-boarding.png"), + fit: BoxFit.contain, + ),), + ); + } } diff --git a/lib/Pages/PhoneLoginPage.dart b/lib/Pages/PhoneLoginPage.dart new file mode 100644 index 0000000..f80e3cf --- /dev/null +++ b/lib/Pages/PhoneLoginPage.dart @@ -0,0 +1,53 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter/material.dart'; +import 'package:intl_phone_number_input/intl_phone_number_input.dart'; + + +class PhoneSignInScreen extends StatefulWidget { + @override + _PhoneSignInScreenState createState() => _PhoneSignInScreenState(); +} + +class _PhoneSignInScreenState extends State { + PhoneNumber _phoneNumber; + + String _message; + String _verificationId; + + bool _isSMSsent = false; + + final TextEditingController _smsController = TextEditingController(); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("Phone Sign In"), + ), + body: SingleChildScrollView( + child: AnimatedContainer( + duration: Duration(milliseconds: 500,), + width: MediaQuery.of(context).size.width, + margin: EdgeInsets.only( + top: 20, + ), + child: Column( + children: [ + Container( + margin: EdgeInsets.symmetric( + horizontal: 20, + vertical: 10, + ), + child: InternationalPhoneNumberInput( + onInputChanged: (phoneNumberTxt) { + _phoneNumber = phoneNumberTxt; + }, + inputBorder: OutlineInputBorder(), + initialCountry2LetterCode: 'US', + )), + ]) + )) + ); + } +} \ No newline at end of file diff --git a/lib/Services/AuthService.dart b/lib/Services/AuthService.dart new file mode 100644 index 0000000..d86f938 --- /dev/null +++ b/lib/Services/AuthService.dart @@ -0,0 +1,66 @@ +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:telegramchatapp/Helpers/HelperFunctions.dart'; +import 'package:telegramchatapp/Models/user.dart'; +import 'package:telegramchatapp/Services/DatabaseService.dart'; + +class AuthService { + final FirebaseAuth _auth = FirebaseAuth.instance; + + // create user object based on FirebaseUser + User _userFromFirebaseUser (FirebaseUser user) { + return (user != null) ? User(uid: user.uid) : null; + } + + // Sign in with email and password + Future signInWithEmailAndPassword (String email, String password) async { + try { + AuthResult result = await _auth.signInWithEmailAndPassword(email: email, password: password); + FirebaseUser user = result.user; + + return _userFromFirebaseUser(user); + } catch (e) { + print(e.toString()); + return null; + } + } + + // Register with email and password + Future registerWithEmailAndPassword (String nickname, String email, String password) async { + try { + AuthResult result = await _auth.signInWithEmailAndPassword(email: email, password: password); + FirebaseUser user = result.user; + + // Create a new document for user with uid + await DatabaseService(uid: user.uid).updateUserData(nickname, email, password); + return _userFromFirebaseUser(user); + } catch (e) { + print(e.toString()); + return null; + } + } + + // Sign out + Future signOut () async { + try { + await HelperFunctions.saveUserLoggedInSharedPreference(false); + await HelperFunctions.saveUserEmailSharedPreference(''); + await HelperFunctions.saveUserNameSharedPreference(''); + + return await _auth.signOut().whenComplete(() async { + print("Logged out"); + await HelperFunctions.getUserLoggedInSharedPreference().then((value) { + print("Logged in $value"); + }); + await HelperFunctions.getUserEmailSharedPreference().then((value) { + print("Email $value"); + }); + await HelperFunctions.getUserNameSharedPreference().then((value) { + print("Full name $value"); + }); + }); + } catch (e) { + print(e.toString()); + return null; + } + } +} diff --git a/lib/Services/DatabaseService.dart b/lib/Services/DatabaseService.dart new file mode 100644 index 0000000..50179bc --- /dev/null +++ b/lib/Services/DatabaseService.dart @@ -0,0 +1,133 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; + +class DatabaseService { + + final String uid; + DatabaseService({ + this.uid + }); + + // Collection reference + final CollectionReference userCollection = Firestore.instance.collection('users'); + final CollectionReference groupCollection = Firestore.instance.collection('groups'); + + // Update userdata + Future updateUserData(String nickname, String email, String password) async { + return await userCollection.document(uid).setData({ + 'nickname': nickname, + 'email': email, + 'password': password, + 'groups': [], + 'photoUrl': '' + }); + } + + // Create group + Future createGroup(String userName, String groupName) async { + DocumentReference groupDocRef = await groupCollection.add({ + 'groupName': groupName, + 'groupIcon': '', + 'admin': userName, + // 'messages': , + 'groupId': '', + 'recentMessage': '', + 'recentMessageSender': '' + }); + + print(userName); + await groupDocRef.updateData({ + 'members': FieldValue.arrayUnion([uid + '_' + userName]), + 'groupId': groupDocRef.documentID + }); + + DocumentReference userDocRef = userCollection.document(uid); + return await userDocRef.updateData({ + 'groups': FieldValue.arrayUnion([groupDocRef.documentID + '_' + groupName]) + }); + } + +// Toggling the user group join + Future togglingGroupJoin(String groupId, String groupName, String userName) async { + + DocumentReference userDocRef = userCollection.document(uid); + DocumentSnapshot userDocSnapshot = await userDocRef.get(); + + DocumentReference groupDocRef = groupCollection.document(groupId); + + List groups = await userDocSnapshot.data['groups']; + + if (groups.contains(groupId + '_' + groupName)) { + // print('hey'); + await userDocRef.updateData({ + 'groups': FieldValue.arrayRemove(([groupId + '_' + groupName])), + }); + + await groupDocRef.updateData({ + 'members': FieldValue.arrayRemove([uid + '_' + userName]), + }); + } else { + // print ('nay'); + await userDocRef.updateData({ + 'groups': FieldValue.arrayUnion([groupId + '_' + groupName]) + }); + + await groupDocRef.updateData({ + 'members': FieldValue.arrayUnion([uid + '_' + userName]) + }); + } + } + + // has user joined the group + Future isUserJoined(String groupId, String groupName, String userName) async { + DocumentReference userDocRef = userCollection.document(uid); + DocumentSnapshot userDocSnapshot = await userDocRef.get(); + + List groups = await userDocSnapshot.data['groups']; + + if (groups.contains(groupId + '_' + groupName)) { + // print('he'); + return true; + } + return false; + } + + // Get user data + Future getUserData(String email) async { + QuerySnapshot snapshot = await userCollection.where('email', isEqualTo: email).getDocuments(); + return snapshot; + } + + Future getUserNameByEmail(String email) async { + QuerySnapshot snapshot = await userCollection.where('email', isEqualTo: email).getDocuments(); + if (snapshot != null) { + return snapshot.documents[0].data['nickname']; + } + + return ''; + } + + // Get user groups + getUserGroups () async { + return Firestore.instance.collection('users').document(uid).snapshots(); + } + + // Send message + sendMessage (String groupId, chatMessageData) { + Firestore.instance.collection('groups').document(groupId).collection('messages').add(chatMessageData); + Firestore.instance.collection('groups').document(groupId).updateData({ + 'recentMessage': chatMessageData['message'], + 'recentMessageSender': chatMessageData['sender'], + 'recentMessageTime': chatMessageData['time'].toString() + }); + } + + // Get chats of a particular group + getChats (String groupId) async { + return Firestore.instance.collection('groups').document(groupId).collection('messages').orderBy('time').snapshots(); + } + + // search groups + searchByName (String groupName) { + return Firestore.instance.collection('groups').where('groupName', isEqualTo: groupName).getDocuments(); + } +} diff --git a/lib/Shared/constants.dart b/lib/Shared/constants.dart new file mode 100644 index 0000000..d635663 --- /dev/null +++ b/lib/Shared/constants.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; + +// TODO: Add context so we can use themes + +const textInputDecoration = InputDecoration( + labelStyle: TextStyle(color: Color.fromRGBO(28, 28, 28, 1)), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Color.fromRGBO(28, 28, 28, 1), width: 2.0) + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Color(0xFFFF0889B6), width: 2.0) + ), +); \ No newline at end of file diff --git a/lib/Widgets/FullImageWidget.dart b/lib/Widgets/FullImageWidget.dart index dfb9250..f19e7f7 100644 --- a/lib/Widgets/FullImageWidget.dart +++ b/lib/Widgets/FullImageWidget.dart @@ -3,18 +3,48 @@ import 'package:photo_view/photo_view.dart'; class FullPhoto extends StatelessWidget { + final String url; + + FullPhoto({ Key key, @required this.url }) : super(key: key); + @override Widget build(BuildContext context) { + return Scaffold ( + appBar: AppBar ( + backgroundColor: Colors.lightBlue, + iconTheme: IconThemeData ( + color: Colors.white, + ), + title: Text ( + "Full Imgae", + style: TextStyle ( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + centerTitle: true, + ), + body: FullPhotoScreen (url: url), + ); } } class FullPhotoScreen extends StatefulWidget { + + final String url; + + FullPhotoScreen({ Key key, @required this.url }) : super(key: key); + @override - State createState() => FullPhotoScreenState(); + State createState() => FullPhotoScreenState(url: url); } class FullPhotoScreenState extends State { + final String url; + + FullPhotoScreenState({ Key key, @required this.url }); + @override void initState() { super.initState(); @@ -22,6 +52,8 @@ class FullPhotoScreenState extends State { @override Widget build(BuildContext context) { - + return Container ( + child: PhotoView (imageProvider: NetworkImage(url),) + ); } } diff --git a/lib/Widgets/GroupTile.dart b/lib/Widgets/GroupTile.dart new file mode 100644 index 0000000..0790c4a --- /dev/null +++ b/lib/Widgets/GroupTile.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:telegramchatapp/Pages/GroupChatPage.dart'; + +import 'package:telegramchatapp/AppLocalizations.dart'; + +class GroupTile extends StatelessWidget { + final String userName; + final String groupId; + final String groupName; + + GroupTile({ this.userName, this.groupId, this.groupName }); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + Navigator.push(context, MaterialPageRoute(builder: (context) => GroupChatPage(groupId: groupId, userName: userName, groupName: groupName ))); + }, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 5.0, vertical: 10.0), + child: ListTile( + leading: CircleAvatar( + radius: 30.0, + backgroundColor: Theme.of(context).primaryColor, + child: Text( + groupName.substring(0, 1).toUpperCase(), + textAlign: TextAlign.center, + style: TextStyle(color: Theme.of(context).buttonColor),), + ), + title: Text(groupName, style: TextStyle(fontWeight: FontWeight.bold),), + subtitle: Text(AppLocalizations.of(context).joinedGroupHint + "$userName", style: TextStyle(fontSize: 13.0),), + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/Widgets/MessageTileWidget.dart b/lib/Widgets/MessageTileWidget.dart new file mode 100644 index 0000000..8b98c8e --- /dev/null +++ b/lib/Widgets/MessageTileWidget.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + +class MessageTile extends StatelessWidget { + final String message; + final String sender; + final bool sentByMe; + final DateTime timeStamp = DateTime.now(); + + MessageTile({ this.message, this.sender, this.sentByMe }); + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.only(top: 4.0, bottom: 4.0, left: sentByMe ? 0 : 24, right: sentByMe ? 0 : 24), + alignment: sentByMe ? Alignment.centerRight : Alignment.centerLeft , + child: Container ( + margin: sentByMe ? EdgeInsets.only(left: 20.0) : EdgeInsets.only(right: 30), + padding: EdgeInsets.only(top: 8.0, bottom: 12.0, left: 20.0, right: 20.0), + decoration: BoxDecoration( + borderRadius: sentByMe ? BorderRadius.only( + topLeft: Radius.circular(12.0), + topRight: Radius.circular(12.0), + bottomLeft: Radius.circular(12.0), + ) + : + BorderRadius.only( + topLeft: Radius.circular(12.0), + topRight: Radius.circular(12.0), + bottomRight: Radius.circular(12.0), + ), + color: sentByMe ? Theme.of(context).primaryColor : Theme.of(context).dividerColor, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Text(sender.toUpperCase(), textAlign: TextAlign.start, + // style: TextStyle(fontSize: 13.0, fontWeight: FontWeight.bold, color: Colors.black, letterSpacing: -0.5),), + SizedBox(height: 7.0), + Text(message, textAlign: TextAlign.start, style: TextStyle(fontSize: 15.0, color: Theme.of(context).buttonColor),), + Text( + DateFormat("HH:mm").format(timeStamp), + textAlign: TextAlign.justify, + style: TextStyle(fontSize: 12.0, + color: Theme.of(context).buttonColor),), + ], + ), + ), + ); + } +} diff --git a/lib/Widgets/ProgressWidget.dart b/lib/Widgets/ProgressWidget.dart index 9782b15..cc77645 100644 --- a/lib/Widgets/ProgressWidget.dart +++ b/lib/Widgets/ProgressWidget.dart @@ -2,9 +2,18 @@ import 'package:flutter/material.dart'; circularProgress() { + return Container( + alignment: Alignment.center, + padding: EdgeInsets.only(top: 12.0), + child: CircularProgressIndicator(valueColor: AlwaysStoppedAnimation(Colors.lightBlueAccent),), + ); } linearProgress() { - + return Container( + alignment: Alignment.center, + padding: EdgeInsets.only(top: 12.0), + child: LinearProgressIndicator(valueColor: AlwaysStoppedAnimation(Colors.lightBlueAccent),), + ); } diff --git a/lib/l10n/messages_all.dart b/lib/l10n/messages_all.dart new file mode 100644 index 0000000..f8aad9e --- /dev/null +++ b/lib/l10n/messages_all.dart @@ -0,0 +1,63 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that looks up messages for specific locales by +// delegating to the appropriate library. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:implementation_imports, file_names, unnecessary_new +// ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering +// ignore_for_file:argument_type_not_assignable, invalid_assignment +// ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases +// ignore_for_file:comment_references + +import 'dart:async'; + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; +import 'package:intl/src/intl_helpers.dart'; + +import 'messages_messages.dart' as messages_messages; + +typedef Future LibraryLoader(); +Map _deferredLibraries = { + 'messages': () => new Future.value(null), +}; + +MessageLookupByLibrary _findExact(String localeName) { + switch (localeName) { + case 'messages': + return messages_messages.messages; + default: + return null; + } +} + +/// User programs should call this before using [localeName] for messages. +Future initializeMessages(String localeName) async { + var availableLocale = Intl.verifiedLocale( + localeName, + (locale) => _deferredLibraries[locale] != null, + onFailure: (_) => null); + if (availableLocale == null) { + return new Future.value(false); + } + var lib = _deferredLibraries[availableLocale]; + await (lib == null ? new Future.value(false) : lib()); + initializeInternalMessageLookup(() => new CompositeMessageLookup()); + messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor); + return new Future.value(true); +} + +bool _messagesExistFor(String locale) { + try { + return _findExact(locale) != null; + } catch (e) { + return false; + } +} + +MessageLookupByLibrary _findGeneratedMessagesFor(String locale) { + var actualLocale = Intl.verifiedLocale(locale, _messagesExistFor, + onFailure: (_) => null); + if (actualLocale == null) return null; + return _findExact(actualLocale); +} diff --git a/lib/l10n/messages_messages.dart b/lib/l10n/messages_messages.dart new file mode 100644 index 0000000..94a161d --- /dev/null +++ b/lib/l10n/messages_messages.dart @@ -0,0 +1,29 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a messages locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'messages'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static _notInlinedMessages(_) => { + "alreadyHaveHive" : MessageLookupByLibrary.simpleMessage("I already have an account"), + "createHive" : MessageLookupByLibrary.simpleMessage("Let\'s create my own hive"), + "welcomeToHive" : MessageLookupByLibrary.simpleMessage("Welcome to Hive"), + "welcomeToHiveMessage" : MessageLookupByLibrary.simpleMessage("Here you can connect to other communities and users near you") + }; +} diff --git a/lib/main.dart b/lib/main.dart index a03b5cb..9935fa7 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,25 +1,41 @@ import 'package:flutter/material.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; import 'Pages/LoginPage.dart'; +import 'package:telegramchatapp/AppLocalizations.dart'; + void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( - title: 'Telegram Clone', + localizationsDelegates: [ + const AppLocalizationsDelegate(), + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate + ], + supportedLocales: [ + const Locale('en', 'US'), + const Locale('he', 'IL'), + ], + debugShowCheckedModeBanner: false, theme: ThemeData( - primaryColor: Colors.lightBlueAccent, + brightness: Brightness.dark, + primaryColor: Color.fromRGBO(255,193,58, 1), + accentColor: Color.fromRGBO(255,164,1, 1), + buttonColor: Color.fromRGBO(28, 28, 28, 1), + dividerColor: Color.fromRGBO(196, 196, 196, 1), + backgroundColor: Color.fromRGBO(244, 244, 244, 1), + errorColor: Color.fromRGBO(255,69,0, 1), + disabledColor: Color.fromRGBO(196, 196, 196, 1), + hintColor: Color.fromARGB(75, 75, 74, 1), + visualDensity: VisualDensity.adaptivePlatformDensity, + fontFamily: 'Montserrat' ), - home: Scaffold( - appBar: AppBar( - title: Text('Coding Cafe', style: TextStyle(fontSize: 26.0, color: Colors.white, fontWeight: FontWeight.bold),), - ), - body: Center( - child: Text('Welcome to Telegram Clone App', style: TextStyle(fontSize: 20.0, color: Colors.blueAccent),), - ), - ), - debugShowCheckedModeBanner: false, + title: 'Telegram Clone', + home: LoginScreen(), ); } } diff --git a/pubspec.lock b/pubspec.lock index f36f478..2fa3c70 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,13 +1,20 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: - archive: + _fe_analyzer_shared: dependency: transitive description: - name: archive + name: _fe_analyzer_shared url: "https://pub.dartlang.org" source: hosted - version: "2.0.13" + version: "7.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + url: "https://pub.dartlang.org" + source: hosted + version: "0.39.17" args: dependency: transitive description: @@ -21,14 +28,14 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.4.1" + version: "2.5.0-nullsafety.1" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.0-nullsafety.1" cached_network_image: dependency: "direct main" description: @@ -36,20 +43,34 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.0-rc.1" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.3" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.1.3" + version: "1.2.0-nullsafety.1" + cli_util: + dependency: transitive + description: + name: cli_util + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.0.1" + version: "1.1.0-nullsafety.1" cloud_firestore: dependency: "direct main" description: @@ -63,7 +84,7 @@ packages: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.14.12" + version: "1.15.0-nullsafety.3" convert: dependency: transitive description: @@ -77,7 +98,14 @@ packages: name: crypto url: "https://pub.dartlang.org" source: hosted - version: "2.1.4" + version: "2.1.5" + csslib: + dependency: transitive + description: + name: csslib + url: "https://pub.dartlang.org" + source: hosted + version: "0.16.2" cupertino_icons: dependency: "direct main" description: @@ -85,20 +113,41 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.1.3" + dart_style: + dependency: transitive + description: + name: dart_style + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.6" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" file: dependency: transitive description: name: file url: "https://pub.dartlang.org" source: hosted - version: "5.2.0" + version: "5.2.1" firebase: dependency: transitive description: name: firebase url: "https://pub.dartlang.org" source: hosted - version: "7.3.0" + version: "7.3.3" firebase_auth: dependency: "direct main" description: @@ -152,7 +201,7 @@ packages: name: flutter_cache_manager url: "https://pub.dartlang.org" source: hosted - version: "1.4.1" + version: "1.4.2" flutter_local_notifications: dependency: "direct main" description: @@ -167,13 +216,18 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.1" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" flutter_plugin_android_lifecycle: dependency: transitive description: name: flutter_plugin_android_lifecycle url: "https://pub.dartlang.org" source: hosted - version: "1.0.8" + version: "1.0.11" flutter_test: dependency: "direct dev" description: flutter @@ -191,13 +245,34 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.1.3" + font_awesome_flutter: + dependency: "direct main" + description: + name: font_awesome_flutter + url: "https://pub.dartlang.org" + source: hosted + version: "8.11.0" + glob: + dependency: transitive + description: + name: glob + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + google_fonts: + dependency: "direct main" + description: + name: google_fonts + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.2" google_sign_in: dependency: "direct main" description: name: google_sign_in url: "https://pub.dartlang.org" source: hosted - version: "4.5.1" + version: "4.5.9" google_sign_in_platform_interface: dependency: transitive description: @@ -211,14 +286,21 @@ packages: name: google_sign_in_web url: "https://pub.dartlang.org" source: hosted - version: "0.9.1+1" + version: "0.9.2" + html: + dependency: transitive + description: + name: html + url: "https://pub.dartlang.org" + source: hosted + version: "0.14.0+4" http: dependency: transitive description: name: http url: "https://pub.dartlang.org" source: hosted - version: "0.12.1" + version: "0.12.2" http_parser: dependency: transitive description: @@ -226,34 +308,41 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.1.4" - image: - dependency: transitive - description: - name: image - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.12" image_picker: dependency: "direct main" description: name: image_picker url: "https://pub.dartlang.org" source: hosted - version: "0.6.7+1" + version: "0.6.7+22" image_picker_platform_interface: dependency: transitive description: name: image_picker_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.6" intl: dependency: "direct main" description: name: intl url: "https://pub.dartlang.org" source: hosted - version: "0.15.8" + version: "0.16.1" + intl_phone_number_input: + dependency: "direct main" + description: + name: intl_phone_number_input + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.1" + intl_translation: + dependency: "direct main" + description: + name: intl_translation + url: "https://pub.dartlang.org" + source: hosted + version: "0.17.10+1" js: dependency: transitive description: @@ -261,69 +350,111 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.6.2" + libphonenumber: + dependency: transitive + description: + name: libphonenumber + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.6" + version: "0.12.10-nullsafety.1" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.1.8" + version: "1.3.0-nullsafety.3" + nested: + dependency: transitive + description: + name: nested + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.4" + node_interop: + dependency: transitive + description: + name: node_interop + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" + node_io: + dependency: transitive + description: + name: node_io + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + package_config: + dependency: transitive + description: + name: package_config + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.3" path: dependency: transitive description: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.6.4" + version: "1.8.0-nullsafety.1" path_provider: dependency: transitive description: name: path_provider url: "https://pub.dartlang.org" source: hosted - version: "1.6.10" + version: "1.6.27" path_provider_linux: dependency: transitive description: name: path_provider_linux url: "https://pub.dartlang.org" source: hosted - version: "0.0.1+1" + version: "0.0.1+2" path_provider_macos: dependency: transitive description: name: path_provider_macos url: "https://pub.dartlang.org" source: hosted - version: "0.0.4+3" + version: "0.0.4+8" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.2" + version: "1.0.4" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.4+3" pedantic: dependency: transitive description: name: pedantic url: "https://pub.dartlang.org" source: hosted - version: "1.9.0" + version: "1.9.2" petitparser: dependency: transitive description: name: petitparser url: "https://pub.dartlang.org" source: hosted - version: "2.4.0" + version: "3.1.0" photo_view: dependency: "direct main" description: @@ -344,7 +475,7 @@ packages: name: plugin_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.2" + version: "1.0.3" process: dependency: transitive description: @@ -352,13 +483,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.13" + provider: + dependency: transitive + description: + name: provider + url: "https://pub.dartlang.org" + source: hosted + version: "4.3.3" + pub_semver: + dependency: transitive + description: + name: pub_semver + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.4" quiver: dependency: transitive description: name: quiver url: "https://pub.dartlang.org" source: hosted - version: "2.1.3" + version: "2.1.5" rxdart: dependency: transitive description: @@ -372,14 +517,21 @@ packages: name: shared_preferences url: "https://pub.dartlang.org" source: hosted - version: "0.5.7+3" + version: "0.5.12+4" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.2+4" shared_preferences_macos: dependency: transitive description: name: shared_preferences_macos url: "https://pub.dartlang.org" source: hosted - version: "0.0.1+10" + version: "0.0.1+11" shared_preferences_platform_interface: dependency: transitive description: @@ -394,6 +546,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.1.2+7" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.2+3" sky_engine: dependency: transitive description: flutter @@ -405,98 +564,112 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "1.8.0-nullsafety.2" sqflite: dependency: transitive description: name: sqflite url: "https://pub.dartlang.org" source: hosted - version: "1.3.1" + version: "1.3.2+3" sqflite_common: dependency: transitive description: name: sqflite_common url: "https://pub.dartlang.org" source: hosted - version: "1.0.1" + version: "1.0.3+1" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.9.3" + version: "1.10.0-nullsafety.1" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.0-nullsafety.1" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.0.5" + version: "1.1.0-nullsafety.1" synchronized: dependency: transitive description: name: synchronized url: "https://pub.dartlang.org" source: hosted - version: "2.2.0" + version: "2.2.0+2" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0-nullsafety.1" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.15" + version: "0.2.19-nullsafety.2" typed_data: dependency: transitive description: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.1.6" + version: "1.3.0-nullsafety.3" uuid: dependency: transitive description: name: uuid url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.2.2" vector_math: dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.0.8" + version: "2.1.0-nullsafety.3" + watcher: + dependency: transitive + description: + name: watcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.9.7+15" + win32: + dependency: transitive + description: + name: win32 + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.4+1" xdg_directories: dependency: transitive description: name: xdg_directories url: "https://pub.dartlang.org" source: hosted - version: "0.1.0" - xml: + version: "0.1.2" + yaml: dependency: transitive description: - name: xml + name: yaml url: "https://pub.dartlang.org" source: hosted - version: "3.6.1" + version: "2.2.1" sdks: - dart: ">=2.7.0 <3.0.0" - flutter: ">=1.12.13+hotfix.5 <2.0.0" + dart: ">=2.10.2 <2.11.0" + flutter: ">=1.22.2 <2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 2b8f92b..8295d59 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,6 +19,10 @@ environment: dependencies: flutter: sdk: flutter + flutter_localizations: + sdk: flutter + + font_awesome_flutter: ^8.10.0 firebase_auth: ^0.14.0+5 google_sign_in: ^4.0.7 cloud_firestore: ^0.12.9+5 @@ -27,10 +31,13 @@ dependencies: shared_preferences: ^0.5.3+4 firebase_storage: ^3.0.6 cached_network_image: 2.0.0-rc.1 - intl: ^0.15.7 + intl: ^0.16.1 + intl_translation: ^0.17.10+1 + google_fonts: ^1.1.1 firebase_messaging: 6.0.13 flutter_local_notifications: 1.4.0 photo_view: ^0.5.0 + intl_phone_number_input: ^0.2.0 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. @@ -69,6 +76,18 @@ flutter: - images/mimi7.gif - images/mimi8.gif - images/mimi9.gif + - assets/images/hive-bee-on-boarding.png + - assets/Icon-Yellow.jpeg + - assets/images/iconfill.png + - assets/people.jpeg + - assets/Friend_1@2x_100.jpg + - assets/Friend_2@2x_100.jpg + - assets/Friend_3@2x_100.jpg + - assets/Friend_4@2x_100.jpg + - assets/Friend_5@2x_100.jpg + - assets/camera.png + - assets/gallery.png + - # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware.