diff --git a/.gitignore b/.gitignore index 6d0a9f2fe..f7243428c 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,5 @@ report.xml /crowdin.yml -.flutter-plugins-dependencies \ No newline at end of file +.flutter-plugins-dependencies +/ios/Flutter/.last_build_id diff --git a/assets/i18n/en/error.arb b/assets/i18n/en/error.arb index 742dd9ddf..b918664ca 100644 --- a/assets/i18n/en/error.arb +++ b/assets/i18n/en/error.arb @@ -4,6 +4,13 @@ "type": "text", "placeholders": {} }, + "receive_share_text_too_long": "Text is too long (limit: {limit} characters)", + "@receive_share_text_too_long": { + "type": "text", + "placeholders": { + "limit": {} + } + }, "receive_share_temp_write_failed": "Failed to copy shared file to temporary location", "@receive_share_temp_write_failed": { "type": "text", diff --git a/assets/i18n/en/post.arb b/assets/i18n/en/post.arb index f221acf6c..1d26b159b 100644 --- a/assets/i18n/en/post.arb +++ b/assets/i18n/en/post.arb @@ -231,6 +231,16 @@ "type": "text", "placeholders": {} }, + "create_media": "Media", + "@create_media": { + "type": "text", + "placeholders": {} + }, + "create_camera": "Camera", + "@create_camera": { + "type": "text", + "placeholders": {} + }, "create_photo": "Photo", "@create_photo": { "type": "text", diff --git a/assets/i18n/en/user.arb b/assets/i18n/en/user.arb index a8fa96600..c52c41f92 100644 --- a/assets/i18n/en/user.arb +++ b/assets/i18n/en/user.arb @@ -974,6 +974,11 @@ "type": "text", "placeholders": {} }, + "change_email_current_email_text": "Current email", + "@change_email_current_email_text": { + "type": "text", + "placeholders": {} + }, "change_email_hint_text": "Enter your new email", "@change_email_hint_text": { "type": "text", diff --git a/assets/images/icons/camera-icon.png b/assets/images/icons/camera-icon.png new file mode 100644 index 000000000..286b0d886 Binary files /dev/null and b/assets/images/icons/camera-icon.png differ diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 06ad38cc2..26caa5b21 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -2,10 +2,45 @@ PODS: - connectivity (0.0.1): - Flutter - Reachability + - connectivity_macos (0.0.1): + - Flutter - device_info (0.0.1): - Flutter + - DKImagePickerController/Core (4.2.2): + - DKImagePickerController/ImageDataManager + - DKImagePickerController/Resource + - DKImagePickerController/ImageDataManager (4.2.2) + - DKImagePickerController/PhotoGallery (4.2.2): + - DKImagePickerController/Core + - DKPhotoGallery + - DKImagePickerController/Resource (4.2.2) + - DKPhotoGallery (0.0.14): + - DKPhotoGallery/Core (= 0.0.14) + - DKPhotoGallery/Model (= 0.0.14) + - DKPhotoGallery/Preview (= 0.0.14) + - DKPhotoGallery/Resource (= 0.0.14) + - SDWebImage + - SDWebImageFLPlugin + - DKPhotoGallery/Core (0.0.14): + - DKPhotoGallery/Model + - DKPhotoGallery/Preview + - SDWebImage + - SDWebImageFLPlugin + - DKPhotoGallery/Model (0.0.14): + - SDWebImage + - SDWebImageFLPlugin + - DKPhotoGallery/Preview (0.0.14): + - DKPhotoGallery/Model + - DKPhotoGallery/Resource + - SDWebImage + - SDWebImageFLPlugin + - DKPhotoGallery/Resource (0.0.14): + - SDWebImage + - SDWebImageFLPlugin - file_picker (0.0.1): + - DKImagePickerController/PhotoGallery - Flutter + - FLAnimatedImage (1.0.12) - Flutter (1.0.0) - flutter_ffmpeg/full-gpl-lts (0.2.10): - Flutter @@ -14,6 +49,8 @@ PODS: - Flutter - Mantle - SDWebImageWebPCoder + - flutter_plugin_android_lifecycle (0.0.1): + - Flutter - flutter_secure_storage (3.3.1): - Flutter - FMDB (2.7.5): @@ -24,8 +61,8 @@ PODS: - TOCropViewController (~> 2.5.2) - image_picker (0.0.1): - Flutter - - Intercom (6.0.1) - - intercom_flutter (2.1.0): + - Intercom (6.0.2) + - intercom_flutter (2.1.1): - Flutter - Intercom (~> 6.0.1) - libwebp (1.1.0): @@ -41,22 +78,29 @@ PODS: - Mantle/extobjc (= 2.1.1) - Mantle/extobjc (2.1.1) - mobile-ffmpeg-full-gpl (4.3.1.LTS) - - OneSignal (2.12.6) - - onesignal_flutter (2.3.3): + - OneSignal (2.13.1) + - onesignal_flutter (2.4.1): - Flutter - - OneSignal (= 2.12.6) + - OneSignal (= 2.13.1) - package_info (0.0.1): - Flutter - path_provider (0.0.1): - Flutter + - path_provider_macos (0.0.1): + - Flutter - Reachability (3.2) - screen (0.0.1): - Flutter - - SDWebImage/Core (5.5.2) - - SDWebImageWebPCoder (0.5.0): + - SDWebImage (5.7.3): + - SDWebImage/Core (= 5.7.3) + - SDWebImage/Core (5.7.3) + - SDWebImageFLPlugin (0.4.0): + - FLAnimatedImage (>= 1.0.11) + - SDWebImage/Core (~> 5.6) + - SDWebImageWebPCoder (0.6.1): - libwebp (~> 1.0) - - SDWebImage/Core (~> 5.5) - - share (0.5.2): + - SDWebImage/Core (~> 5.7) + - share (0.0.1): - Flutter - shared_preferences (0.0.1): - Flutter @@ -84,11 +128,13 @@ PODS: DEPENDENCIES: - connectivity (from `.symlinks/plugins/connectivity/ios`) + - connectivity_macos (from `.symlinks/plugins/connectivity_macos/ios`) - device_info (from `.symlinks/plugins/device_info/ios`) - file_picker (from `.symlinks/plugins/file_picker/ios`) - Flutter (from `Flutter`) - flutter_ffmpeg/full-gpl-lts (from `.symlinks/plugins/flutter_ffmpeg/ios`) - flutter_image_compress (from `.symlinks/plugins/flutter_image_compress/ios`) + - flutter_plugin_android_lifecycle (from `.symlinks/plugins/flutter_plugin_android_lifecycle/ios`) - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) - image_cropper (from `.symlinks/plugins/image_cropper/ios`) - image_picker (from `.symlinks/plugins/image_picker/ios`) @@ -97,6 +143,7 @@ DEPENDENCIES: - onesignal_flutter (from `.symlinks/plugins/onesignal_flutter/ios`) - package_info (from `.symlinks/plugins/package_info/ios`) - path_provider (from `.symlinks/plugins/path_provider/ios`) + - path_provider_macos (from `.symlinks/plugins/path_provider_macos/ios`) - screen (from `.symlinks/plugins/screen/ios`) - share (from `.symlinks/plugins/share/ios`) - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) @@ -111,6 +158,9 @@ DEPENDENCIES: SPEC REPOS: trunk: + - DKImagePickerController + - DKPhotoGallery + - FLAnimatedImage - FMDB - Intercom - libwebp @@ -119,6 +169,7 @@ SPEC REPOS: - OneSignal - Reachability - SDWebImage + - SDWebImageFLPlugin - SDWebImageWebPCoder - TOCropViewController - VIMediaCache @@ -126,6 +177,8 @@ SPEC REPOS: EXTERNAL SOURCES: connectivity: :path: ".symlinks/plugins/connectivity/ios" + connectivity_macos: + :path: ".symlinks/plugins/connectivity_macos/ios" device_info: :path: ".symlinks/plugins/device_info/ios" file_picker: @@ -136,6 +189,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/flutter_ffmpeg/ios" flutter_image_compress: :path: ".symlinks/plugins/flutter_image_compress/ios" + flutter_plugin_android_lifecycle: + :path: ".symlinks/plugins/flutter_plugin_android_lifecycle/ios" flutter_secure_storage: :path: ".symlinks/plugins/flutter_secure_storage/ios" image_cropper: @@ -150,6 +205,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/package_info/ios" path_provider: :path: ".symlinks/plugins/path_provider/ios" + path_provider_macos: + :path: ".symlinks/plugins/path_provider_macos/ios" screen: :path: ".symlinks/plugins/screen/ios" share: @@ -175,30 +232,37 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: connectivity: 6e94255659cc86dcbef1d452ad3e0491bb1b3e75 - device_info: cbf09d2ec12aa7110e0b09fabe54b5bd6c8efe74 - file_picker: 408623be2125b79a4539cf703be3d4b3abe5e245 + connectivity_macos: e2e9731b6b22dda39eb1b128f6969d574460e191 + device_info: d7d233b645a32c40dfdc212de5cf646ca482f175 + DKImagePickerController: 4a3e7948a848c4348e600b3fe5ce41478835fa10 + DKPhotoGallery: 0290d32343574f06eaa4c26f8f2f8a1035e916be + file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1 + FLAnimatedImage: 4a0b56255d9b05f18b6dd7ee06871be5d3b89e31 Flutter: 0e3d915762c693b495b44d77113d4970485de6ec flutter_ffmpeg: cf0a6941ef67e88248c998cc3e34f8acb0b4e454 flutter_image_compress: 082f8daaf6c1b0c9fe798251c750ef0ecd98d7ae + flutter_plugin_android_lifecycle: dc0b544e129eebb77a6bfb1239d4d1c673a60a35 flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a image_cropper: 3c16d7651730ffe85897f5a1c4e2547e6b54989a image_picker: e3eacd46b94694dde7cf2705955cece853aa1a8f - Intercom: f63e30d10c658b4f5817f5693e5ee2fa070b5803 - intercom_flutter: 3c324ba9a75060f15c5bfb7854a9bb0a3524cb65 + Intercom: 523417d82ed1a8c635cc7f97b266eb8bd0bd9d85 + intercom_flutter: 45b06d70365ccae12227cbceeca36c6a23457f54 libwebp: 946cb3063cea9236285f7e9a8505d806d30e07f3 Mantle: 35238ae6f2e2b2d474fa7b67fee82a59fea71915 mobile-ffmpeg-full-gpl: b1e8e1540852bd911768774f1c11e8fc76102dc3 - OneSignal: a6df91542d1cf133b9dd19d89f2b533d8f2d3163 - onesignal_flutter: 0db2f05befedb1ef79399b571bff4c9ba5410f6a - package_info: 48b108e75b8802c2d5e126f208ef540561c98aef - path_provider: fb74bd0465e96b594bb3b5088ee4a4e7bb1f2a9d + OneSignal: fa98eaa90372bb367b6a4a1c1622ffb9832c7600 + onesignal_flutter: fc5794fdcfdbd3f17426cb63c78281a3f51c85ff + package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62 + path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c + path_provider_macos: f760a3c5b04357c380e2fddb6f9db6f3015897e0 Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96 screen: abd91ca7bf3426e1cc3646d27e9b2358d6bf07b0 - SDWebImage: 4d5c027c935438f341ed33dbac53ff9f479922ca - SDWebImageWebPCoder: e7ae855f058e3dcae99696920b6a5d134e9dcddf - share: bae0a282aab4483288913fc4dc0b935d4b491f2e - shared_preferences: 430726339841afefe5142b9c1f50cb6bd7793e01 + SDWebImage: 97351f6582ceca541ea294ba66a1fcb342a331c2 + SDWebImageFLPlugin: 6c2295fb1242d44467c6c87dc5db6b0a13228fd8 + SDWebImageWebPCoder: d0dac55073088d24b2ac1b191a71a8f8d0adac21 + share: 0b2c3e82132f5888bccca3351c504d0003b3b410 + shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d shared_preferences_macos: f3f29b71ccbb56bf40c9dd6396c9acf15e214087 shared_preferences_web: 141cce0c3ed1a1c5bf2a0e44f52d31eeb66e5ea9 sqflite: 4001a31ff81d210346b500c55b17f4d6c7589dd0 @@ -208,8 +272,8 @@ SPEC CHECKSUMS: video_player: fd8b6188941ec5a530d6ab0830a34f78faa0452e video_thumbnail: c4e2a3c539e247d4de13cd545344fd2d26ffafd1 VIMediaCache: aa650f82cb114c68a343beb4e67416cf5eb2f3a2 - wakelock: bd3dcc6a8bcf53a1c0309780ff192dcee67c6ccb + wakelock: 0d4a70faf8950410735e3f61fb15d517c8a6efc4 PODFILE CHECKSUM: 19fc3c8c5b57454c416c7e16986dc7dd8aa66d48 -COCOAPODS: 1.8.3 +COCOAPODS: 1.9.1 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index b959651cd..3e623a228 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -9,13 +9,8 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 8959D90F23F955D80091EB2B /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 898053AA22047AAF00E47AD9 /* Pods_Runner.framework */; }; 89ABAE522203425900049DFB /* NotificationService.m in Sources */ = {isa = PBXBuildFile; fileRef = 89ABAE512203425900049DFB /* NotificationService.m */; }; 89ABAE562203425900049DFB /* OneSignalNotificationServiceExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 89ABAE4E2203425900049DFB /* OneSignalNotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -76,6 +71,55 @@ remoteGlobalIDString = 603FA97A9EAECDA6694299BF06D80C42; remoteInfo = sqflite; }; + 89398C07245F1D5F00F7C8DE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8980538D22047AAE00E47AD9 /* Pods.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = E95F4135A781F1B0F90A544CD0398420; + remoteInfo = DKImagePickerController; + }; + 89398C09245F1D5F00F7C8DE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8980538D22047AAE00E47AD9 /* Pods.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 7247E064ECCE473BC39CE9C6B1E7C0B3; + remoteInfo = "DKImagePickerController-DKImagePickerController"; + }; + 89398C0B245F1D5F00F7C8DE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8980538D22047AAE00E47AD9 /* Pods.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = B1B247DE42D782218EFEA0CB0F6D6454; + remoteInfo = DKPhotoGallery; + }; + 89398C0D245F1D5F00F7C8DE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8980538D22047AAE00E47AD9 /* Pods.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = B85D4E7E4EAEF1DAD4C541A2D2E6F27D; + remoteInfo = "DKPhotoGallery-DKPhotoGallery"; + }; + 89398C0F245F1D5F00F7C8DE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8980538D22047AAE00E47AD9 /* Pods.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = FAA5F2D71B90788C908800A94534AA92; + remoteInfo = FLAnimatedImage; + }; + 89398C11245F1D5F00F7C8DE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8980538D22047AAE00E47AD9 /* Pods.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 84D8D6D3A0E58CD8A5EEB311D34E6BDB; + remoteInfo = flutter_plugin_android_lifecycle; + }; + 89398C13245F1D5F00F7C8DE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8980538D22047AAE00E47AD9 /* Pods.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 7FA68B460F5DC5F5BDB312C44D7C1963; + remoteInfo = SDWebImageFLPlugin; + }; 8958C896234B7D5E000280FF /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 8980538D22047AAE00E47AD9 /* Pods.xcodeproj */; @@ -278,8 +322,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -297,7 +339,6 @@ 37BDAB2A2170B9A900811BA5 /* development.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = development.plist; sourceTree = ""; }; 37BDAB2B2170B9AA00811BA5 /* production.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = production.plist; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 418B7159EB81FFD151CA68C6 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 75F711C5FC14A5361C4912AE /* Pods-OneSignalNotificationServiceExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OneSignalNotificationServiceExtension.release.xcconfig"; path = "Pods/Target Support Files/Pods-OneSignalNotificationServiceExtension/Pods-OneSignalNotificationServiceExtension.release.xcconfig"; sourceTree = ""; }; 77585F4F710D7AE9AB748E2F /* Pods-OneSignalNotificationServiceExtension.release-production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OneSignalNotificationServiceExtension.release-production.xcconfig"; path = "Pods/Target Support Files/Pods-OneSignalNotificationServiceExtension/Pods-OneSignalNotificationServiceExtension.release-production.xcconfig"; sourceTree = ""; }; @@ -321,7 +362,6 @@ 89DBC2DD2203430700F80685 /* OneSignalNotificationServiceExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = OneSignalNotificationServiceExtension.entitlements; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -346,7 +386,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 8959D90F23F955D80091EB2B /* Pods_Runner.framework in Frameworks */, B28D3FB500E916EAB5DDC3CB /* Pods_OneSignalNotificationServiceExtension.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -355,8 +394,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, D4D1718378280D051194AC59 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -414,9 +451,15 @@ children = ( 89FE1C6B23268E35007C6904 /* connectivity.framework */, 8925094A222F0E0900455D87 /* device_info.framework */, + 89398C08245F1D5F00F7C8DE /* DKImagePickerController.framework */, + 89398C0A245F1D5F00F7C8DE /* DKImagePickerController.bundle */, + 89398C0C245F1D5F00F7C8DE /* DKPhotoGallery.framework */, + 89398C0E245F1D5F00F7C8DE /* DKPhotoGallery.bundle */, 89E684862320235F00E0855B /* file_picker.framework */, + 89398C10245F1D5F00F7C8DE /* FLAnimatedImage.framework */, 89E684882320235F00E0855B /* flutter_ffmpeg.framework */, 89FE1BA52323BCA0007C6904 /* flutter_image_compress.framework */, + 89398C12245F1D5F00F7C8DE /* flutter_plugin_android_lifecycle.framework */, 8980539E22047AAF00E47AD9 /* flutter_secure_storage.framework */, 8928E31E22356450001DB32A /* FMDB.framework */, 898053A022047AAF00E47AD9 /* image_cropper.framework */, @@ -432,6 +475,7 @@ 89FE1C6D23268E35007C6904 /* Reachability.framework */, 89E6848C2320235F00E0855B /* screen.framework */, 8981C97823F82B3200DE0290 /* SDWebImage.framework */, + 89398C14245F1D5F00F7C8DE /* SDWebImageFLPlugin.framework */, 8981C97A23F82B3200DE0290 /* SDWebImageWebPCoder.framework */, 89EE3231227B56510094ACB0 /* share.framework */, 89EE3233227B56510094ACB0 /* shared_preferences.framework */, @@ -462,9 +506,7 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( - 3B80C3931E831B6300D905FE /* App.framework */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEBA1CF902C7004384FC /* Flutter.framework */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 9740EEB31CF90195004384FC /* Generated.xcconfig */, @@ -691,6 +733,55 @@ remoteRef = 8928E31F22356450001DB32A /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 89398C08245F1D5F00F7C8DE /* DKImagePickerController.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = DKImagePickerController.framework; + remoteRef = 89398C07245F1D5F00F7C8DE /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 89398C0A245F1D5F00F7C8DE /* DKImagePickerController.bundle */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = DKImagePickerController.bundle; + remoteRef = 89398C09245F1D5F00F7C8DE /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 89398C0C245F1D5F00F7C8DE /* DKPhotoGallery.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = DKPhotoGallery.framework; + remoteRef = 89398C0B245F1D5F00F7C8DE /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 89398C0E245F1D5F00F7C8DE /* DKPhotoGallery.bundle */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = DKPhotoGallery.bundle; + remoteRef = 89398C0D245F1D5F00F7C8DE /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 89398C10245F1D5F00F7C8DE /* FLAnimatedImage.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FLAnimatedImage.framework; + remoteRef = 89398C0F245F1D5F00F7C8DE /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 89398C12245F1D5F00F7C8DE /* flutter_plugin_android_lifecycle.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = flutter_plugin_android_lifecycle.framework; + remoteRef = 89398C11245F1D5F00F7C8DE /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 89398C14245F1D5F00F7C8DE /* SDWebImageFLPlugin.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = SDWebImageFLPlugin.framework; + remoteRef = 89398C13245F1D5F00F7C8DE /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 8958C897234B7D5E000280FF /* package_info.framework */ = { isa = PBXReferenceProxy; fileType = wrapper.framework; @@ -932,7 +1023,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; 8614A00078062C58F9149142 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; @@ -1099,7 +1190,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 65; + CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_TEAM = GAR7B57RXU; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1113,7 +1204,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 0.0.65; + MARKETING_VERSION = 0.0.66; PRODUCT_BUNDLE_IDENTIFIER = social.openbook.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner-Bridging-Header.h"; @@ -1188,7 +1279,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 65; + CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_TEAM = GAR7B57RXU; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1202,7 +1293,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 0.0.65; + MARKETING_VERSION = 0.0.66; PRODUCT_BUNDLE_IDENTIFIER = social.openbook.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner-Bridging-Header.h"; @@ -1272,7 +1363,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 65; + CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_TEAM = GAR7B57RXU; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1286,7 +1377,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 0.0.65; + MARKETING_VERSION = 0.0.66; PRODUCT_BUNDLE_IDENTIFIER = social.openbook.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner-Bridging-Header.h"; @@ -1361,7 +1452,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 65; + CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_TEAM = GAR7B57RXU; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1375,7 +1466,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 0.0.65; + MARKETING_VERSION = 0.0.66; PRODUCT_BUNDLE_IDENTIFIER = social.openbook.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner-Bridging-Header.h"; @@ -1445,7 +1536,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 65; + CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_TEAM = GAR7B57RXU; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1459,7 +1550,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 0.0.65; + MARKETING_VERSION = 0.0.66; PRODUCT_BUNDLE_IDENTIFIER = social.openbook.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner-Bridging-Header.h"; @@ -1482,7 +1573,7 @@ CODE_SIGN_ENTITLEMENTS = OneSignalNotificationServiceExtension/OneSignalNotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 65; + CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_TEAM = GAR7B57RXU; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1493,7 +1584,7 @@ INFOPLIST_FILE = OneSignalNotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 0.0.65; + MARKETING_VERSION = 0.0.66; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = social.openbook.app.OneSignalNotificationServiceExtension; @@ -1519,7 +1610,7 @@ CODE_SIGN_ENTITLEMENTS = OneSignalNotificationServiceExtension/OneSignalNotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 65; + CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_TEAM = GAR7B57RXU; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1530,7 +1621,7 @@ INFOPLIST_FILE = OneSignalNotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 0.0.65; + MARKETING_VERSION = 0.0.66; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = social.openbook.app.OneSignalNotificationServiceExtension; @@ -1556,7 +1647,7 @@ CODE_SIGN_ENTITLEMENTS = OneSignalNotificationServiceExtension/OneSignalNotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 65; + CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_TEAM = GAR7B57RXU; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1567,7 +1658,7 @@ INFOPLIST_FILE = OneSignalNotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 0.0.65; + MARKETING_VERSION = 0.0.66; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = social.openbook.app.OneSignalNotificationServiceExtension; @@ -1593,7 +1684,7 @@ CODE_SIGN_ENTITLEMENTS = OneSignalNotificationServiceExtension/OneSignalNotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 65; + CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_TEAM = GAR7B57RXU; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1604,7 +1695,7 @@ INFOPLIST_FILE = OneSignalNotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 0.0.65; + MARKETING_VERSION = 0.0.66; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = social.openbook.app.OneSignalNotificationServiceExtension; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1628,7 +1719,7 @@ CODE_SIGN_ENTITLEMENTS = OneSignalNotificationServiceExtension/OneSignalNotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 65; + CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_TEAM = GAR7B57RXU; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1639,7 +1730,7 @@ INFOPLIST_FILE = OneSignalNotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 0.0.65; + MARKETING_VERSION = 0.0.66; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = social.openbook.app.OneSignalNotificationServiceExtension; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1663,7 +1754,7 @@ CODE_SIGN_ENTITLEMENTS = OneSignalNotificationServiceExtension/OneSignalNotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 65; + CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_TEAM = GAR7B57RXU; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1674,7 +1765,7 @@ INFOPLIST_FILE = OneSignalNotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 0.0.65; + MARKETING_VERSION = 0.0.66; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = social.openbook.app.OneSignalNotificationServiceExtension; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1698,7 +1789,7 @@ CODE_SIGN_ENTITLEMENTS = OneSignalNotificationServiceExtension/OneSignalNotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 65; + CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_TEAM = GAR7B57RXU; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1709,7 +1800,7 @@ INFOPLIST_FILE = OneSignalNotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 0.0.65; + MARKETING_VERSION = 0.0.66; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = social.openbook.app.OneSignalNotificationServiceExtension; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1722,7 +1813,6 @@ }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; @@ -1779,7 +1869,6 @@ }; 97C147041CF9000F007C117D /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; @@ -1836,7 +1925,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 65; + CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_TEAM = GAR7B57RXU; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1850,7 +1939,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 0.0.65; + MARKETING_VERSION = 0.0.66; PRODUCT_BUNDLE_IDENTIFIER = social.openbook.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner-Bridging-Header.h"; @@ -1869,7 +1958,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 65; + CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_TEAM = GAR7B57RXU; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1883,7 +1972,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 0.0.65; + MARKETING_VERSION = 0.0.66; PRODUCT_BUNDLE_IDENTIFIER = social.openbook.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner-Bridging-Header.h"; diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100755 index 949b67898..000000000 --- a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - BuildSystemType - Original - - diff --git a/lib/locale/messages_en.dart b/lib/locale/messages_en.dart index f4f07933e..c69a33bbf 100644 --- a/lib/locale/messages_en.dart +++ b/lib/locale/messages_en.dart @@ -61,6 +61,8 @@ class MessageLookup extends MessageLookupByLibrary { static m20(currentUserLanguage) => "Language (${currentUserLanguage})"; + static m67(limit) => "Text is too long (limit: ${limit} characters)"; + static m21(limit) => "File too large (limit: ${limit} MB)"; static m22(resourceCount, resourceName) => "See all ${resourceCount} ${resourceName}"; @@ -93,7 +95,7 @@ class MessageLookup extends MessageLookupByLibrary { static m36(description) => "Failed to preview link with website error: ${description}"; - static m67(link) => "Invalid link: ${link}"; + static m68(link) => "Invalid link: ${link}"; static m37(maxLength) => "Circle name must be no longer than ${maxLength} characters."; @@ -500,6 +502,7 @@ class MessageLookup extends MessageLookupByLibrary { "error__receive_share_invalid_uri_scheme" : MessageLookupByLibrary.simpleMessage("Failed to receive share"), "error__receive_share_temp_write_denied" : MessageLookupByLibrary.simpleMessage("Denied permission to copy shared file to temporary location"), "error__receive_share_temp_write_failed" : MessageLookupByLibrary.simpleMessage("Failed to copy shared file to temporary location"), + "error__receive_share_text_too_long" : m67, "error__unknown_error" : MessageLookupByLibrary.simpleMessage("Unknown error"), "image_picker__error_too_large" : m21, "image_picker__from_camera" : MessageLookupByLibrary.simpleMessage("From camera"), @@ -677,7 +680,9 @@ class MessageLookup extends MessageLookupByLibrary { "post__comments_page_tap_to_retry_replies" : MessageLookupByLibrary.simpleMessage("Tap to retry loading replies."), "post__comments_page_title" : MessageLookupByLibrary.simpleMessage("Post comments"), "post__comments_view_all_comments" : m31, + "post__create_camera" : MessageLookupByLibrary.simpleMessage("Camera"), "post__create_hashtags_invalid" : m32, + "post__create_media" : MessageLookupByLibrary.simpleMessage("Media"), "post__create_new" : MessageLookupByLibrary.simpleMessage("New post"), "post__create_new_community_post_label" : MessageLookupByLibrary.simpleMessage("Create new communtiy post"), "post__create_new_post_label" : MessageLookupByLibrary.simpleMessage("Create new post"), @@ -752,7 +757,7 @@ class MessageLookup extends MessageLookupByLibrary { "post__you_shared_with" : MessageLookupByLibrary.simpleMessage("You shared with"), "post_body_link_preview__empty" : MessageLookupByLibrary.simpleMessage("This link could not be previewed"), "post_body_link_preview__error_with_description" : m36, - "post_body_link_preview__invalid" : m67, + "post_body_link_preview__invalid" : m68, "post_body_media__unsupported" : MessageLookupByLibrary.simpleMessage("Unsupported media type"), "post_uploader__cancelled" : MessageLookupByLibrary.simpleMessage("Cancelled!"), "post_uploader__cancelling" : MessageLookupByLibrary.simpleMessage("Cancelling"), @@ -781,6 +786,7 @@ class MessageLookup extends MessageLookupByLibrary { "user__billion_postfix" : MessageLookupByLibrary.simpleMessage("b"), "user__block_description" : MessageLookupByLibrary.simpleMessage("You will both dissapear from each other\'s social network experience, with the exception of communities which the person is a staff member of."), "user__block_user" : MessageLookupByLibrary.simpleMessage("Block user"), + "user__change_email_current_email_text" : MessageLookupByLibrary.simpleMessage("Current email"), "user__change_email_email_text" : MessageLookupByLibrary.simpleMessage("Email"), "user__change_email_error" : MessageLookupByLibrary.simpleMessage("Email is already registered"), "user__change_email_hint_text" : MessageLookupByLibrary.simpleMessage("Enter your new email"), diff --git a/lib/main.dart b/lib/main.dart index 1b203ef7c..a4537adc1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -29,8 +29,6 @@ import 'package:Okuna/services/universal_links/universal_links.dart'; import 'package:Okuna/widgets/toast.dart'; import 'package:Okuna/translation/constants.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/foundation.dart' - show debugDefaultTargetPlatformOverride; import 'package:flutter_advanced_networkimage/provider.dart'; import 'package:flutter\_localizations/flutter\_localizations.dart'; import 'package:sentry/sentry.dart'; @@ -252,91 +250,32 @@ class _MyAppState extends State { } } -void _setPlatformOverrideForDesktop() { - TargetPlatform targetPlatform; - if (Platform.isMacOS) { - targetPlatform = TargetPlatform.iOS; - } else if (Platform.isLinux || Platform.isWindows) { - targetPlatform = TargetPlatform.android; - } - if (targetPlatform != null) { - debugDefaultTargetPlatformOverride = targetPlatform; - } -} - Future main() async { - _setPlatformOverrideForDesktop(); - // This captures errors reported by the Flutter framework. - FlutterError.onError = (FlutterErrorDetails details) async { - if (isOnDesktop) { - // Report errors on Desktop to embedder - DesktopErrorReporting.reportError(details.exception, details.stack); - } else if (isInDebugMode) { - // In development mode simply print to console. - FlutterError.dumpErrorToConsole(details); - } else { - // In production mode report to the application zone to report to - // Sentry. - Zone.current.handleUncaughtError(details.exception, details.stack); - } - }; - - // This creates a [Zone] that contains the Flutter application and stablishes - // an error handler that captures errors and reports them. - // - // Using a zone makes sure that as many errors as possible are captured, - // including those thrown from [Timer]s, microtasks, I/O, and those forwarded - // from the `FlutterError` handler. - // - // More about zones: - // - // - https://api.dartlang.org/stable/1.24.2/dart-async/Zone-class.html - // - https://www.dartlang.org/articles/libraries/zones + MyApp app = MyApp(); - MyApp app; - // run the entire app with our own HttpieOverride - HttpOverrides.runWithHttpOverrides>( - () async { - app = MyApp(); - runApp(app); - }, - HttpieOverrides(), - onError: (error, stackTrace) async { - if (isOnDesktop) { - DesktopErrorReporting.reportError(error, stackTrace); - return; - } - SentryClient sentryClient = - app.openbookProviderKey.currentState.sentryClient; - await _reportError(error, stackTrace, sentryClient); - }); -} - -/// Reports [error] along with its [stackTrace] to Sentry.io. -Future _reportError( - dynamic error, dynamic stackTrace, SentryClient sentryClient) async { - print('Caught error: $error'); +// Run the whole app in a zone to capture all uncaught errors. + runZonedGuarded(() => runApp(app), (Object error, StackTrace stackTrace) { + if (isInDebugMode) { + print(error); + print(stackTrace); + print('In dev mode. Not sending report to Sentry.io.'); + return; + } - // Errors thrown in development mode are unlikely to be interesting. You can - // check if you are running in dev mode using an assertion and omit sending - // the report. - if (isInDebugMode) { - print(stackTrace); - print('In dev mode. Not sending report to Sentry.io.'); - return; - } + SentryClient sentryClient = + app.openbookProviderKey.currentState.sentryClient; - print('Reporting to Sentry.io...'); - final SentryResponse response = await sentryClient.captureException( - exception: error, - stackTrace: stackTrace, - ); - - if (response.isSuccessful) { - print('Success! Event ID: ${response.eventId}'); - } else { - print('Failed to report to Sentry.io: ${response.error}'); - } + try { + sentryClient.captureException( + exception: error, + stackTrace: stackTrace, + ); + print('Error sent to sentry.io: $error'); + } catch (e) { + print('Sending report to sentry.io failed: $e'); + print('Original error: $error'); + } + }); } bool get isInDebugMode { diff --git a/lib/pages/home/bottom_sheets/camera_picker.dart b/lib/pages/home/bottom_sheets/camera_picker.dart new file mode 100644 index 000000000..6c6210f17 --- /dev/null +++ b/lib/pages/home/bottom_sheets/camera_picker.dart @@ -0,0 +1,63 @@ +import 'dart:io'; +import 'package:Okuna/pages/home/bottom_sheets/rounded_bottom_sheet.dart'; +import 'package:Okuna/provider.dart'; +import 'package:Okuna/services/localization.dart'; +import 'package:Okuna/services/media/media.dart'; +import 'package:Okuna/services/media/models/media_file.dart'; +import 'package:Okuna/widgets/icon.dart'; +import 'package:Okuna/widgets/theming/text.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; + +class OBCameraPickerBottomSheet extends StatelessWidget { + const OBCameraPickerBottomSheet({Key key}) : super(key: key); + + @override + Widget build(BuildContext context) { + var provider = OpenbookProvider.of(context); + + LocalizationService localizationService = provider.localizationService; + + List cameraPickerActions = [ + ListTile( + leading: const OBIcon(OBIcons.camera), + title: OBText( + localizationService.post__create_photo, + ), + onTap: () async { + bool permissionGranted = await provider.permissionService + .requestStoragePermissions(context: context); + if (permissionGranted) { + File file = await ImagePicker.pickImage(source: ImageSource.camera); + Navigator.pop( + context, file != null ? MediaFile(file, FileType.image) : null); + } + }, + ), + ListTile( + leading: const OBIcon(OBIcons.video_camera), + title: OBText( + localizationService.post__create_video, + ), + onTap: () async { + bool permissionGranted = await provider.permissionService + .requestStoragePermissions(context: context); + if (permissionGranted) { + File file = await ImagePicker.pickVideo(source: ImageSource.camera); + Navigator.pop( + context, file != null ? MediaFile(file, FileType.video) : null); + } + }, + ), + ]; + + return OBRoundedBottomSheet( + child: Padding( + padding: EdgeInsets.only(bottom: 16), + child: Column( + children: cameraPickerActions, + mainAxisSize: MainAxisSize.min, + ), + )); + } +} diff --git a/lib/pages/home/bottom_sheets/image_picker.dart b/lib/pages/home/bottom_sheets/image_picker.dart index 24c3d8973..790eda3db 100644 --- a/lib/pages/home/bottom_sheets/image_picker.dart +++ b/lib/pages/home/bottom_sheets/image_picker.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:Okuna/pages/home/bottom_sheets/rounded_bottom_sheet.dart'; import 'package:Okuna/provider.dart'; import 'package:Okuna/services/localization.dart'; -import 'package:Okuna/services/media.dart'; +import 'package:Okuna/services/media/media.dart'; import 'package:Okuna/widgets/icon.dart'; import 'package:Okuna/widgets/theming/text.dart'; import 'package:file_picker/file_picker.dart'; @@ -28,7 +28,7 @@ class OBImagePickerBottomSheet extends StatelessWidget { bool permissionGranted = await provider.permissionService .requestStoragePermissions(context: context); if (permissionGranted) { - File file = await FilePicker.getFile(type: FileType.IMAGE); + File file = await FilePicker.getFile(type: FileType.image); Navigator.pop(context, file); } }, diff --git a/lib/pages/home/bottom_sheets/video_picker.dart b/lib/pages/home/bottom_sheets/video_picker.dart index d16911074..f79b3739a 100644 --- a/lib/pages/home/bottom_sheets/video_picker.dart +++ b/lib/pages/home/bottom_sheets/video_picker.dart @@ -2,7 +2,7 @@ import 'dart:io'; import 'package:Okuna/pages/home/bottom_sheets/rounded_bottom_sheet.dart'; import 'package:Okuna/provider.dart'; import 'package:Okuna/services/localization.dart'; -import 'package:Okuna/services/media.dart'; +import 'package:Okuna/services/media/media.dart'; import 'package:Okuna/widgets/icon.dart'; import 'package:Okuna/widgets/theming/text.dart'; import 'package:file_picker/file_picker.dart'; @@ -27,7 +27,7 @@ class OBVideoPickerBottomSheet extends StatelessWidget { bool permissionGranted = await provider.permissionService .requestStoragePermissions(context: context); if (permissionGranted) { - File file = await FilePicker.getFile(type: FileType.VIDEO); + File file = await FilePicker.getFile(type: FileType.video); Navigator.pop(context, file); } }, diff --git a/lib/pages/home/home.dart b/lib/pages/home/home.dart index d99a862bd..cbde4395a 100644 --- a/lib/pages/home/home.dart +++ b/lib/pages/home/home.dart @@ -4,7 +4,7 @@ import 'dart:io'; import 'package:Okuna/models/push_notification.dart'; import 'package:Okuna/pages/home/lib/poppable_page_controller.dart'; import 'package:Okuna/services/intercom.dart'; -import 'package:Okuna/services/media.dart'; +import 'package:Okuna/services/media/media.dart'; import 'package:Okuna/services/push_notifications/push_notifications.dart'; import 'package:Okuna/models/user.dart'; import 'package:Okuna/pages/home/pages/communities/communities.dart'; diff --git a/lib/pages/home/modals/save_community.dart b/lib/pages/home/modals/save_community.dart index 8675b6c6f..5c6c6a7d3 100644 --- a/lib/pages/home/modals/save_community.dart +++ b/lib/pages/home/modals/save_community.dart @@ -2,7 +2,7 @@ import 'dart:io'; import 'package:Okuna/models/category.dart'; import 'package:Okuna/models/community.dart'; -import 'package:Okuna/services/media.dart'; +import 'package:Okuna/services/media/media.dart'; import 'package:Okuna/services/localization.dart'; import 'package:Okuna/services/theme_value_parser.dart'; import 'package:Okuna/widgets/avatars/avatar.dart'; diff --git a/lib/pages/home/modals/save_post/create_post.dart b/lib/pages/home/modals/save_post/create_post.dart index 68b01b030..76f4a9d55 100644 --- a/lib/pages/home/modals/save_post/create_post.dart +++ b/lib/pages/home/modals/save_post/create_post.dart @@ -1,28 +1,29 @@ import 'dart:io'; + import 'package:Okuna/models/community.dart'; import 'package:Okuna/models/post.dart'; import 'package:Okuna/models/post_image.dart'; import 'package:Okuna/models/post_media.dart'; import 'package:Okuna/models/post_video.dart'; +import 'package:Okuna/pages/home/lib/draft_editing_controller.dart'; import 'package:Okuna/pages/home/modals/save_post/widgets/create_post_text.dart'; import 'package:Okuna/pages/home/modals/save_post/widgets/post_community_previewer.dart'; import 'package:Okuna/pages/home/modals/save_post/widgets/post_image_previewer.dart'; import 'package:Okuna/pages/home/modals/save_post/widgets/post_video_previewer.dart'; import 'package:Okuna/pages/home/modals/save_post/widgets/remaining_post_characters.dart'; -import 'package:Okuna/pages/home/lib/draft_editing_controller.dart'; import 'package:Okuna/provider.dart'; import 'package:Okuna/services/draft.dart'; import 'package:Okuna/services/httpie.dart'; import 'package:Okuna/services/link_preview.dart'; -import 'package:Okuna/services/media.dart'; import 'package:Okuna/services/localization.dart'; +import 'package:Okuna/services/media/media.dart'; import 'package:Okuna/services/navigation_service.dart'; import 'package:Okuna/services/share.dart'; import 'package:Okuna/services/toast.dart'; import 'package:Okuna/services/user.dart'; import 'package:Okuna/services/validation.dart'; -import 'package:Okuna/widgets/avatars/logged_in_user_avatar.dart'; import 'package:Okuna/widgets/avatars/avatar.dart'; +import 'package:Okuna/widgets/avatars/logged_in_user_avatar.dart'; import 'package:Okuna/widgets/buttons/button.dart'; import 'package:Okuna/widgets/buttons/pill_button.dart'; import 'package:Okuna/widgets/contextual_search_boxes/contextual_search_box_state.dart'; @@ -31,12 +32,13 @@ import 'package:Okuna/widgets/link_preview.dart'; import 'package:Okuna/widgets/nav_bars/themed_nav_bar.dart'; import 'package:Okuna/widgets/new_post_data_uploader.dart'; import 'package:Okuna/widgets/theming/primary_color_container.dart'; +import 'package:Okuna/widgets/theming/smart_text.dart'; import 'package:Okuna/widgets/theming/text.dart'; +import 'package:async/async.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:pigment/pigment.dart'; -import 'package:Okuna/widgets/theming/smart_text.dart'; -import 'package:async/async.dart'; class OBSavePostModal extends StatefulWidget { final Community community; @@ -400,24 +402,22 @@ class OBSavePostModalState extends OBContextualSearchBoxState { List _getImagePostActions() { return [ OBPillButton( - text: _localizationService.trans('post__create_photo'), + text: _localizationService.post__create_media, color: Pigment.fromString('#FCC14B'), icon: const OBIcon(OBIcons.photo), onPressed: () async { _unfocusTextField(); try { - File pickedPhoto = await _mediaService.pickImage( - imageType: OBImageType.post, - context: context, - flattenGifs: false); - if (pickedPhoto != null) { - bool photoIsGif = await _mediaService.isGif(pickedPhoto); - if (photoIsGif) { - File gifVideo = - await _mediaService.convertGifToVideo(pickedPhoto); - _setPostVideoFile(gifVideo); + var pickedMedia = await _mediaService.pickMedia( + context: context, + source: ImageSource.gallery, + flattenGifs: false, + ); + if (pickedMedia != null) { + if (pickedMedia.type == FileType.image) { + _setPostImageFile(pickedMedia.file); } else { - _setPostImageFile(pickedPhoto); + _setPostVideoFile(pickedMedia.file); } } } catch (error) { @@ -426,14 +426,21 @@ class OBSavePostModalState extends OBContextualSearchBoxState { }, ), OBPillButton( - text: _localizationService.post__create_video, + text: _localizationService.post__create_camera, color: Pigment.fromString('#50b1f2'), - icon: const OBIcon(OBIcons.video), + icon: const OBIcon(OBIcons.cameraCartoon), onPressed: () async { _unfocusTextField(); try { - File pickedVideo = await _mediaService.pickVideo(context: context); - if (pickedVideo != null) _setPostVideoFile(pickedVideo); + var pickedMedia = await _mediaService.pickMedia( + context: context, source: ImageSource.camera); + if (pickedMedia != null) { + if (pickedMedia.type == FileType.image) { + _setPostImageFile(pickedMedia.file); + } else { + _setPostVideoFile(pickedMedia.file); + } + } } catch (error) { _onError(error); } @@ -459,6 +466,10 @@ class OBSavePostModalState extends OBContextualSearchBoxState { } void _setPostImageFile(File image) { + if (!mounted) { + return; + } + setState(() { this._postImageFile = image; _hasImage = true; @@ -485,6 +496,10 @@ class OBSavePostModalState extends OBContextualSearchBoxState { } void _setPostVideoFile(File video) { + if (!mounted) { + return; + } + setState(() { this._postVideoFile = video; _hasVideo = true; @@ -527,7 +542,7 @@ class OBSavePostModalState extends OBContextualSearchBoxState { _addPostItemWidget(postImageWidget); } - Future _onShare({String text, File image, File video}) async { + Future _onShare({String text, File image, File video}) async { if (image != null || video != null) { if (_hasImage) { _removePostImageFile(); @@ -548,11 +563,6 @@ class OBSavePostModalState extends OBContextualSearchBoxState { } if (video != null) { - final isGif = await _mediaService.isGif(video); - if (isGif) { - video = await _mediaService.convertGifToVideo(video); - } - _setPostVideoFile(video); } diff --git a/lib/pages/home/pages/menu/pages/settings/about.dart b/lib/pages/home/pages/menu/pages/settings/about.dart index 26b70daf9..a12d669d6 100644 --- a/lib/pages/home/pages/menu/pages/settings/about.dart +++ b/lib/pages/home/pages/menu/pages/settings/about.dart @@ -48,7 +48,7 @@ class OBAboutPageState extends State { ListTile( leading: OBIcon(OBIcons.nativeInfo), title: OBText( - 'Okuna v0.0.65' + 'Okuna v0.0.66' ), ), ], diff --git a/lib/pages/home/pages/menu/pages/settings/pages/account_settings/modals/change_email/change_email.dart b/lib/pages/home/pages/menu/pages/settings/pages/account_settings/modals/change_email/change_email.dart index 0e72a4f6e..57338f503 100644 --- a/lib/pages/home/pages/menu/pages/settings/pages/account_settings/modals/change_email/change_email.dart +++ b/lib/pages/home/pages/menu/pages/settings/pages/account_settings/modals/change_email/change_email.dart @@ -36,6 +36,7 @@ class OBChangeEmailModalState extends State { bool _changedEmailTaken = false; bool _formValid = true; TextEditingController _emailController = TextEditingController(); + TextEditingController _currentEmailController; CancelableOperation _requestOperation; @override @@ -61,6 +62,9 @@ class OBChangeEmailModalState extends State { _toastService = openbookProvider.toastService; _userService = openbookProvider.userService; _localizationService = openbookProvider.localizationService; + + String currentUserEmail = _userService.getLoggedInUser().getEmail(); + _currentEmailController = TextEditingController(text: currentUserEmail); return OBCupertinoPageScaffold( navigationBar: _buildNavigationBar(), @@ -72,6 +76,15 @@ class OBChangeEmailModalState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + OBTextFormField( + size: OBTextFormFieldSize.large, + autofocus: false, + readOnly: true, + controller: _currentEmailController, + decoration: InputDecoration( + labelText:_localizationService.user__change_email_current_email_text, + ), + ), OBTextFormField( size: OBTextFormFieldSize.large, autofocus: true, diff --git a/lib/pages/home/pages/menu/pages/useful_links.dart b/lib/pages/home/pages/menu/pages/useful_links.dart index 5ef113b5d..0c49b553c 100644 --- a/lib/pages/home/pages/menu/pages/useful_links.dart +++ b/lib/pages/home/pages/menu/pages/useful_links.dart @@ -95,7 +95,7 @@ class OBUsefulLinksPage extends StatelessWidget { _localizationService.drawer__useful_links_slack_channel_desc), onTap: () { urlLauncherService.launchUrl( - 'https://join.slack.com/t/okuna/shared_invite/enQtNDI2NjI3MDM0MzA2LTYwM2E1Y2NhYWRmNTMzZjFhYWZlYmM2YTQ0MWEwYjYyMzcxMGI0MTFhNTIwYjU2ZDI1YjllYzlhOWZjZDc4ZWY'); + 'https://join.slack.com/t/okuna/shared_invite/zt-5fzmpygy-V5nbMzmNJnEg5Hiwx4LO~w'); }, ), ListTile( diff --git a/lib/pages/home/pages/profile/pages/edit_profile/modals/edit_profile.dart b/lib/pages/home/pages/profile/pages/edit_profile/modals/edit_profile.dart index 6add12225..db4efed7e 100644 --- a/lib/pages/home/pages/profile/pages/edit_profile/modals/edit_profile.dart +++ b/lib/pages/home/pages/profile/pages/edit_profile/modals/edit_profile.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:Okuna/models/user.dart'; import 'package:Okuna/provider.dart'; import 'package:Okuna/services/httpie.dart'; -import 'package:Okuna/services/media.dart'; +import 'package:Okuna/services/media/media.dart'; import 'package:Okuna/services/localization.dart'; import 'package:Okuna/services/toast.dart'; import 'package:Okuna/services/user.dart'; diff --git a/lib/provider.dart b/lib/provider.dart index 7d3d955a7..a9c265467 100644 --- a/lib/provider.dart +++ b/lib/provider.dart @@ -17,6 +17,7 @@ import 'package:Okuna/services/hashtags_api.dart'; import 'package:Okuna/services/intercom.dart'; import 'package:Okuna/services/link_preview.dart'; import 'package:Okuna/services/moderation_api.dart'; +import 'package:Okuna/services/media/media.dart'; import 'package:Okuna/services/notifications_api.dart'; import 'package:Okuna/services/permissions.dart'; import 'package:Okuna/services/push_notifications/push_notifications.dart'; @@ -28,7 +29,6 @@ import 'package:Okuna/services/emojis_api.dart'; import 'package:Okuna/services/environment_loader.dart'; import 'package:Okuna/services/follows_api.dart'; import 'package:Okuna/services/httpie.dart'; -import 'package:Okuna/services/media.dart'; import 'package:Okuna/services/follows_lists_api.dart'; import 'package:Okuna/services/localization.dart'; import 'package:Okuna/services/modal_service.dart'; @@ -193,6 +193,7 @@ class OpenbookProviderState extends State { dialogService.setThemeValueParserService(themeValueParserService); mediaService.setValidationService(validationService); mediaService.setBottomSheetService(bottomSheetService); + mediaService.setPermissionsService(permissionService); mediaService.setUtilsService(utilsService); documentsService.setHttpService(httpService); moderationApiService.setStringTemplateService(stringTemplateService); diff --git a/lib/services/bottom_sheet.dart b/lib/services/bottom_sheet.dart index 927feea1f..f444c17b0 100644 --- a/lib/services/bottom_sheet.dart +++ b/lib/services/bottom_sheet.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:io'; import 'package:Okuna/models/circle.dart'; @@ -9,31 +10,30 @@ import 'package:Okuna/models/post_comment.dart'; import 'package:Okuna/models/post_comment_reaction.dart'; import 'package:Okuna/models/post_reaction.dart'; import 'package:Okuna/models/user.dart'; +import 'package:Okuna/pages/home/bottom_sheets/camera_picker.dart'; import 'package:Okuna/pages/home/bottom_sheets/community_actions.dart'; import 'package:Okuna/pages/home/bottom_sheets/community_type_picker.dart'; import 'package:Okuna/pages/home/bottom_sheets/confirm_action.dart'; import 'package:Okuna/pages/home/bottom_sheets/connection_circles_picker.dart'; +import 'package:Okuna/pages/home/bottom_sheets/follows_lists_picker.dart'; import 'package:Okuna/pages/home/bottom_sheets/hashtag_actions.dart'; import 'package:Okuna/pages/home/bottom_sheets/hashtags_display_setting_picker.dart'; import 'package:Okuna/pages/home/bottom_sheets/image_picker.dart'; import 'package:Okuna/pages/home/bottom_sheets/link_previews_setting_picker.dart'; -import 'package:Okuna/pages/home/bottom_sheets/post_comment_more_actions.dart'; -import 'package:Okuna/pages/home/bottom_sheets/follows_lists_picker.dart'; import 'package:Okuna/pages/home/bottom_sheets/post_actions.dart'; -import 'package:Okuna/pages/home/bottom_sheets/user_actions/user_actions.dart'; -import 'package:Okuna/pages/home/bottom_sheets/video_picker.dart'; +import 'package:Okuna/pages/home/bottom_sheets/post_comment_more_actions.dart'; import 'package:Okuna/pages/home/bottom_sheets/react_to_post.dart'; import 'package:Okuna/pages/home/bottom_sheets/react_to_post_comment.dart'; +import 'package:Okuna/pages/home/bottom_sheets/user_actions/user_actions.dart'; +import 'package:Okuna/pages/home/bottom_sheets/video_picker.dart'; import 'package:Okuna/pages/home/bottom_sheets/videos_autoplay_setting_picker.dart'; import 'package:Okuna/pages/home/bottom_sheets/videos_sound_setting_picker.dart'; +import 'package:Okuna/services/media/models/media_file.dart'; import 'package:Okuna/services/user_preferences.dart'; import 'package:Okuna/widgets/post/post.dart'; import 'package:flutter/material.dart'; -import 'dart:async'; import 'package:meta/meta.dart'; -import 'media.dart'; - class BottomSheetService { bool hasActiveBottomSheet = false; @@ -238,6 +238,14 @@ class BottomSheetService { }); } + Future showCameraPicker({@required BuildContext context}) { + return _showModalBottomSheetApp( + context: context, + builder: (BuildContext context) { + return OBCameraPickerBottomSheet(); + }); + } + Future showVideoPicker({@required BuildContext context}) { return _showModalBottomSheetApp( context: context, diff --git a/lib/services/dialog.dart b/lib/services/dialog.dart index 799a8b4fd..c9254209a 100644 --- a/lib/services/dialog.dart +++ b/lib/services/dialog.dart @@ -35,11 +35,12 @@ class DialogService { return showAlert( content: SingleChildScrollView( child: ColorPicker( - enableAlpha: enableAlpha, - pickerColor: initialColor, - onColorChanged: onColorChanged, - pickerAreaHeightPercent: 0.8, - showLabel: false), + enableAlpha: enableAlpha, + pickerColor: initialColor, + onColorChanged: onColorChanged, + pickerAreaHeightPercent: 0.8, + showLabel: false, + ), ), context: context); } diff --git a/lib/services/localization.dart b/lib/services/localization.dart index 047084989..efabdfe6c 100644 --- a/lib/services/localization.dart +++ b/lib/services/localization.dart @@ -819,6 +819,11 @@ class LocalizationService { return Intl.message("Unknown error", name: 'error__unknown_error'); } + String error__receive_share_text_too_long(int limit) { + return Intl.message("Text is too long (limit: $limit characters)", + args: [limit], name: 'error__receive_share_text_too_long'); + } + String get error__receive_share_temp_write_failed { return Intl.message('Failed to copy shared file to temporary location', name: 'error__receive_share_temp_write_failed'); @@ -1918,6 +1923,14 @@ class LocalizationService { return Intl.message("Next", name: 'post__create_next'); } + String get post__create_media { + return Intl.message("Media", name: 'post__create_media'); + } + + String get post__create_camera { + return Intl.message("Camera", name: 'post__create_camera'); + } + String get post__create_photo { return Intl.message("Photo", name: 'post__create_photo'); } @@ -3209,6 +3222,10 @@ class LocalizationService { return Intl.message("Email", name: 'user__change_email_email_text'); } + String get user__change_email_current_email_text { + return Intl.message("Current email", name: 'user__change_email_current_email_text'); + } + String get user__change_email_hint_text { return Intl.message("Enter your new email", name: 'user__change_email_hint_text'); diff --git a/lib/services/media.dart b/lib/services/media/media.dart similarity index 51% rename from lib/services/media.dart rename to lib/services/media/media.dart index d96389e01..77b2f4e81 100644 --- a/lib/services/media.dart +++ b/lib/services/media/media.dart @@ -1,18 +1,27 @@ +import 'dart:async'; import 'dart:io'; + import 'package:Okuna/plugins/image_converter/image_converter.dart'; +import 'package:Okuna/services/bottom_sheet.dart'; import 'package:Okuna/services/localization.dart'; +import 'package:Okuna/services/media/models/media_file.dart'; +import 'package:Okuna/services/permissions.dart'; +import 'package:Okuna/services/toast.dart'; import 'package:Okuna/services/utils_service.dart'; +import 'package:Okuna/services/validation.dart'; +import 'package:async/async.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_ffmpeg/flutter_ffmpeg.dart'; import 'package:flutter_image_compress/flutter_image_compress.dart'; import 'package:image_cropper/image_cropper.dart'; +import 'package:image_picker/image_picker.dart'; import 'package:meta/meta.dart'; -import 'package:Okuna/services/validation.dart'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; import 'package:uuid/uuid.dart'; import 'package:video_thumbnail/video_thumbnail.dart'; -import 'package:flutter_ffmpeg/flutter_ffmpeg.dart'; -import 'bottom_sheet.dart'; + export 'package:image_picker/image_picker.dart'; class MediaService { @@ -28,6 +37,8 @@ class MediaService { ValidationService _validationService; BottomSheetService _bottomSheetService; LocalizationService _localizationService; + ToastService _toastService; + PermissionsService _permissionsService; UtilsService _utilsService; void setLocalizationService(LocalizationService localizationService) { @@ -46,87 +57,179 @@ class MediaService { _bottomSheetService = modalService; } + void setToastService(ToastService toastService) { + _toastService = toastService; + } + + void setPermissionsService(PermissionsService permissionsService) { + _permissionsService = permissionsService; + } + + /// Opens a bottom sheet with the options to pick either an image or a video. + /// The media is picked from the gallery or camera (determined by [source]). + /// + /// If a GIF is picked it will be converted to a video file before being returned. + /// + /// The returned file may be either an image or a video. Use [MediaFile.type] + /// to determine which one it is. + Future pickMedia( + {@required BuildContext context, + @required ImageSource source, + bool flattenGifs}) async { + MediaFile media; + + if (source == ImageSource.gallery) { + bool permissionGranted = + await _permissionsService.requestStoragePermissions(context: context); + if (permissionGranted) { + File file = await FilePicker.getFile(type: FileType.media); + + if (file != null) { + FileType type = await getMediaType(file); + media = MediaFile(file, type); + } + } + } else if (source == ImageSource.camera) { + media = await _bottomSheetService.showCameraPicker(context: context); + } else { + throw 'Unsupported media source: $source'; + } + + if (media == null) { + return null; + } + + media = await processMedia( + media: media, + context: context, + flattenGifs: flattenGifs, + ); + + return media; + } + + /// Opens a bottom sheet with the option to pick an image from gallery or snap + /// a new one with the camera. + /// + /// The returned file should always point to an image. If a GIF is picked it will + /// be flattened. Future pickImage( - {@required OBImageType imageType, - @required BuildContext context, - bool flattenGifs = true}) async { + {@required OBImageType imageType, @required BuildContext context}) async { File pickedImage = await _bottomSheetService.showImagePicker(context: context); if (pickedImage == null) return null; - pickedImage = await fixExifRotation(pickedImage); - final tempPath = await _getTempPath(); + var media = await processMedia( + media: MediaFile(pickedImage, FileType.image), + context: context, + flattenGifs: true, + imageType: imageType, + ); - final String processedImageUuid = _uuid.v4(); - String imageExtension = basename(pickedImage.path); + return media.file; + } - // The image picker gives us the real image, lets copy it into a temp path - pickedImage = - pickedImage.copySync('$tempPath/$processedImageUuid$imageExtension'); + /// Opens a bottom sheet with the option to pick a video from gallery or take + /// a new one with the camera. + /// + /// The returned file should always point to a video. + Future pickVideo({@required BuildContext context}) async { + File pickedVideo = + await _bottomSheetService.showVideoPicker(context: context); - File processedPickedImage; + if (pickedVideo == null) return null; - bool pickedImageIsGif = await isGif(pickedImage); + var media = await processMedia( + media: MediaFile(pickedVideo, FileType.video), + context: context, + ); + + return media.file; + } - if (!pickedImageIsGif || flattenGifs) { - String processedImageName = processedImageUuid + '.jpg'; - processedPickedImage = File('$tempPath/$processedImageName'); - List convertedImageData = - await ImageConverter.convertImage(pickedImage.readAsBytesSync()); - processedPickedImage.writeAsBytesSync(convertedImageData); + Future processMedia( + {@required MediaFile media, + @required BuildContext context, + bool flattenGifs = false, + OBImageType imageType = OBImageType.post}) async { + var mediaType = media.type; + MediaFile result; + + // Copy the media to a temporary location. + final tempPath = await _getTempPath(); + final String mediaUuid = _uuid.v4(); + String mediaExtension = basename(media.file.path); + var copiedFile = media.file.copySync('$tempPath/$mediaUuid$mediaExtension'); + + if (await isGif(media.file) && !flattenGifs) { + mediaType = FileType.video; + + Completer completer = Completer(); + convertGifToVideo(copiedFile).then((file) => completer.complete(file), + onError: (error, trace) { + print(error); + _toastService.error( + message: _localizationService.error__unknown_error, + context: context); + }); + copiedFile = await completer.future; + } + + MediaFile copiedMedia = MediaFile(copiedFile, mediaType); + if (mediaType == FileType.image) { + result = await _processImage(copiedMedia, tempPath, mediaUuid, imageType); + } else if (mediaType == FileType.video) { + result = await _processVideo(copiedMedia); } else { - String processedImageName = processedImageUuid + '.gif'; - processedPickedImage = - pickedImage.copySync('$tempPath/$processedImageName'); + throw 'Unsupported media type: ${media.type}'; } - // We now have a processed one - pickedImage.deleteSync(); + return result; + } + + Future _processImage(MediaFile media, String tempPath, + String mediaUuid, OBImageType imageType) async { + var image = await fixExifRotation(media.file, deleteOriginal: true); + String processedImageName = mediaUuid + '.jpg'; + File processedImage = File('$tempPath/$processedImageName'); + List convertedImageData = + await ImageConverter.convertImage(image.readAsBytesSync()); + processedImage.writeAsBytesSync(convertedImageData); + + // We have a new processed copy, so we can delete our first copy. + image.deleteSync(); if (!await _validationService.isImageAllowedSize( - processedPickedImage, imageType)) { + processedImage, imageType)) { throw FileTooLargeException( _validationService.getAllowedImageSize(imageType)); } + + MediaFile result; + if (imageType == OBImageType.post) { + result = MediaFile(processedImage, media.type); + } else { + double ratioX = IMAGE_RATIOS[imageType]['x']; + double ratioY = IMAGE_RATIOS[imageType]['y']; - processedPickedImage = !pickedImageIsGif || flattenGifs - ? await processImage(processedPickedImage) - : processedPickedImage; - - if (imageType == OBImageType.post) return processedPickedImage; - - double ratioX = IMAGE_RATIOS[imageType]['x']; - double ratioY = IMAGE_RATIOS[imageType]['y']; + File croppedFile = + await cropImage(processedImage, ratioX: ratioX, ratioY: ratioY); - File croppedFile = - await cropImage(processedPickedImage, ratioX: ratioX, ratioY: ratioY); + result = MediaFile(croppedFile, media.type); + } - return croppedFile; + return result; } - Future pickVideo({@required BuildContext context}) async { - File pickedVideo = - await _bottomSheetService.showVideoPicker(context: context); - - if (pickedVideo == null) return null; - - String videoExtension = basename(pickedVideo.path); - String tmpImageName = _uuid.v4() + videoExtension; - final path = await _getTempPath(); - final String pickedVideoCopyPath = '$path/$tmpImageName'; - File pickedVideoCopy = pickedVideo.copySync(pickedVideoCopyPath); - - if (!await _validationService.isVideoAllowedSize(pickedVideoCopy)) { + Future _processVideo(MediaFile media) async { + if (!await _validationService.isVideoAllowedSize(media.file)) { throw FileTooLargeException(_validationService.getAllowedVideoSize()); } - return pickedVideoCopy; + return media; } - Future processImage(File image) async { - return image; - } Future fixExifRotation(File image, {deleteOriginal: false}) async { List imageBytes = await image.readAsBytes(); @@ -143,13 +246,11 @@ class MediaService { await fixedImage.writeAsBytes(result); - if(deleteOriginal) await image.delete(); + if (deleteOriginal) await image.delete(); return fixedImage; } - - Future copyMediaFile(File mediaFile, {deleteOriginal: true}) async { final String processedImageUuid = _uuid.v4(); String imageExtension = basename(mediaFile.path); @@ -234,26 +335,39 @@ class MediaService { return file; } - Future convertGifToVideo(File gif) async { - File resultFile; - + CancelableOperation convertGifToVideo(File gif) { final FlutterFFmpeg _flutterFFmpeg = new FlutterFFmpeg(); + // Set a cancel flag which we can use if we need to cancel before the ffmpeg + // process is started (can happen for two gif shares with a short time between, + // since we have to wait for _getTempPath() before we can start ffmpeg. + var isCancelled = false; + var ffmpegFuture = + _convertGifToVideo(_flutterFFmpeg, gif, () => isCancelled); + return CancelableOperation.fromFuture(ffmpegFuture, onCancel: () { + isCancelled = true; + _flutterFFmpeg.cancel(); + }); + } + + Future _convertGifToVideo( + FlutterFFmpeg flutterFFmpeg, File gif, bool Function() doCancel) async { String resultFileName = _uuid.v4() + '.mp4'; final path = await _getTempPath(); final String sourceFilePath = gif.path; final String resultFilePath = '$path/$resultFileName'; - int exitCode = await _flutterFFmpeg.execute( - '-f gif -i $sourceFilePath -pix_fmt yuv420p -c:v libx264 -movflags +faststart -filter:v crop=\'floor(in_w/2)*2:floor(in_h/2)*2\' $resultFilePath'); + var exitCode; + if (!doCancel()) { + exitCode = await flutterFFmpeg.execute( + ' -loglevel debug -f gif -i $sourceFilePath -pix_fmt yuv420p -c:v libx264 -movflags +faststart -filter:v crop=\'floor(in_w/2)*2:floor(in_h/2)*2\' $resultFilePath'); + } if (exitCode == 0) { - resultFile = File(resultFilePath); + return File(resultFilePath); } else { - throw (Exception('Gif couldn\'t be converted to video')); + throw 'Gif couldn\'t be converted to video'; } - - return resultFile; } void clearThumbnailForFile(File videoFile) { @@ -273,6 +387,22 @@ class MediaService { return mediaMimeSubtype == 'gif'; } + Future getMediaType(File file) async { + String mediaMime = await _utilsService.getFileMimeType(file); + + String mediaMimeType = mediaMime.split('/')[0]; + + if (mediaMimeType == 'video') { + return FileType.video; + } else if (mediaMimeType == 'image') { + return FileType.image; + } else if (mediaMimeType == 'audio') { + return FileType.audio; + } else { + return null; + } + } + Future cropImage(File image, {double ratioX, double ratioY}) async { return ImageCropper.cropImage( sourcePath: image.path, diff --git a/lib/services/media/models/media_file.dart b/lib/services/media/models/media_file.dart new file mode 100644 index 000000000..636ff9d16 --- /dev/null +++ b/lib/services/media/models/media_file.dart @@ -0,0 +1,10 @@ +import 'dart:io'; + +import 'package:file_picker/file_picker.dart'; + +class MediaFile { + final File file; + final FileType type; + + const MediaFile(this.file, this.type); +} diff --git a/lib/services/share.dart b/lib/services/share.dart index 6862b5bda..c41a20c30 100644 --- a/lib/services/share.dart +++ b/lib/services/share.dart @@ -3,13 +3,15 @@ import 'dart:io'; import 'package:Okuna/plugins/share/share.dart'; import 'package:Okuna/services/localization.dart'; +import 'package:Okuna/services/media/media.dart'; +import 'package:Okuna/services/media/models/media_file.dart'; import 'package:Okuna/services/toast.dart'; import 'package:Okuna/services/validation.dart'; +import 'package:async/async.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'media.dart'; - class ShareService { static const _stream = const EventChannel('openbook.social/receive_share'); @@ -19,14 +21,18 @@ class ShareService { LocalizationService _localizationService; StreamSubscription _shareReceiveSubscription; - List _shareQueue; - List Function({String text, File image, File video})> _subscribers; + List Function({String text, File image, File video})> + _subscribers; + + Share _queuedShare; + bool _isProcessingShare = false; + Map _activeShares; BuildContext _context; ShareService() { - _shareQueue = []; _subscribers = []; + _activeShares = {}; if (Platform.isAndroid) { if (_shareReceiveSubscription == null) { @@ -58,99 +64,172 @@ class ShareService { /// Subscribe to share events. /// - /// [onShare] should return [true] if it consumes the share. If [false] is - /// returned, the next subscriber will be sent the share as well! - void subscribe(Future Function({String text, File image, File video}) onShare) { + /// [onShare] should return [true] if it consumes the share immediately, + /// a [CancelableOperation] if some amount of work has to be done first (like + /// gif to video conversion in OBSavePostModal), or [false] if the subscriber + /// did _not_ consume the share (the share will be passed on to the next subscriber). + /// + /// If a [CancelableOperation] is returned, it _must_ handle cancellation + /// properly. + void subscribe( + Future Function({String text, File image, File video}) onShare) { _subscribers.add(onShare); if (_subscribers.length == 1) { - _emptyQueue(); + _processQueuedShare(); } } - void unsubscribe(Future Function({String text, File image, File video}) subscriber) { + void unsubscribe( + Future Function({String text, File image, File video}) + subscriber) { _subscribers.remove(subscriber); } - Future _emptyQueue() async { - var consumed = []; - for (Share share in _shareQueue) { - if (await _onShare(share)) { - consumed.add(share); - } - } + void _onReceiveShare(dynamic shared) async { + _queuedShare = Share.fromReceived(shared); - consumed.forEach((e) => _shareQueue.remove(e)); + if (_subscribers.isNotEmpty && !_isProcessingShare) { + _processQueuedShare(); + } } - void _onReceiveShare(dynamic shared) async { - var share = Share.fromReceived(shared); + Future _processQueuedShare() async { + if (_queuedShare != null) { + // Schedule cancellation of existing share operations. We don't cancel + // immediately since that can cause concurrent modification of _activeShares. + _activeShares + .forEach((key, value) => Future.delayed(Duration(), value.cancel)); - if (_subscribers.isEmpty) { - _shareQueue.add(share); - } else { - await _onShare(share); + var share = _queuedShare; + _queuedShare = null; + + _isProcessingShare = true; + _activeShares[share] = ShareOperation(share, _onShare); + _activeShares[share].then(() => _activeShares.remove(share)); + _activeShares[share].start(); + _isProcessingShare = false; + + // Recurse since a new share might have came in while the last was being processed. + _processQueuedShare(); } } - Future _onShare(Share share) async { + Future _onShare(Share share) async { String text; File image; File video; + if (share.error != null) { _toastService.error( message: _localizationService.trans(share.error), context: _context); if (share.error.contains('uri_scheme')) { throw share.error; } - return true; + return; } if (share.image != null) { image = File.fromUri(Uri.parse(share.image)); - image = await _mediaService.fixExifRotation(image); - image = await _mediaService.processImage(image); - if (!await _validationService.isImageAllowedSize( - image, OBImageType.post)) { - _showFileTooLargeToast( - _validationService.getAllowedImageSize(OBImageType.post)); - return true; - } + var processedFile = await _mediaService.processMedia( + media: MediaFile(image, FileType.image), + context: _context, + ); + image = processedFile.file; } if (share.video != null) { video = File.fromUri(Uri.parse(share.video)); - if (!await _validationService.isVideoAllowedSize(video)) { - _showFileTooLargeToast(_validationService.getAllowedVideoSize()); - return true; - } + var processedFile = await _mediaService.processMedia( + media: MediaFile(image, FileType.video), + context: _context, + ); + + video = processedFile.file; } if (share.text != null) { text = share.text; if (!_validationService.isPostTextAllowedLength(text)) { - _toastService.error( - message: - 'Text too long (limit: ${ValidationService.POST_MAX_LENGTH} characters)', - context: _context); - return true; + String errorMessage = + _localizationService.error__receive_share_text_too_long( + ValidationService.POST_MAX_LENGTH); + _toastService.error(message: errorMessage, context: _context); + return; } } for (var sub in _subscribers.reversed) { - if (await sub(text: text, image: image, video: video)) { - return true; + if (_activeShares[share].isCancelled) { + break; + } + + var subResult = await sub(text: text, image: image, video: video); + + // Stop event propagation if we have a sub-result that is either true or + // a CancelableOperation. + if (subResult is CancelableOperation) { + _activeShares[share].setSubOperation(subResult); + break; + } else if (subResult == true) { + break; } } + } +} + +class ShareOperation { + final Future Function(Share) _shareFunction; + + Share share; + CancelableOperation shareOperation; + CancelableOperation subOperation; + bool isCancelled = false; - return false; + bool _shareComplete = false; + bool _subComplete = false; + FutureOr Function() _callback; + + ShareOperation(this.share, Future Function(Share) shareFunction) + : _shareFunction = shareFunction; + + void start() { + shareOperation = CancelableOperation.fromFuture(_shareFunction(share)); + shareOperation.then((_) { + _shareComplete = true; + _complete(); + }); } - Future _showFileTooLargeToast(int limitInBytes) async { - _toastService.error( - message: _localizationService.image_picker__error_too_large( - limitInBytes ~/ 1048576), - context: _context); + void setSubOperation(CancelableOperation operation) { + subOperation = operation; + subOperation.then((_) { + _subComplete = true; + _complete(); + }); + + shareOperation.then((_) { + if (shareOperation.isCanceled) { + subOperation.cancel(); + } + }); + } + + void cancel() { + isCancelled = true; + shareOperation?.cancel(); + subOperation?.cancel(); + } + + void then(FutureOr Function() callback) { + _callback = callback; + } + + void _complete() { + if ((subOperation == null || _subComplete) && + (shareOperation == null || _shareComplete)) { + _callback(); + } } -} \ No newline at end of file +} diff --git a/lib/services/validation.dart b/lib/services/validation.dart index 783a6045d..656920f6c 100644 --- a/lib/services/validation.dart +++ b/lib/services/validation.dart @@ -4,7 +4,7 @@ import 'package:Okuna/services/communities_api.dart'; import 'package:Okuna/services/connections_circles_api.dart'; import 'package:Okuna/services/follows_lists_api.dart'; import 'package:Okuna/services/httpie.dart'; -import 'package:Okuna/services/media.dart'; +import 'package:Okuna/services/media/media.dart'; import 'package:Okuna/services/utils_service.dart'; import 'package:validators/validators.dart' as validators; diff --git a/lib/widgets/fields/text_form_field.dart b/lib/widgets/fields/text_form_field.dart index b8e25c502..2b1d870d9 100644 --- a/lib/widgets/fields/text_form_field.dart +++ b/lib/widgets/fields/text_form_field.dart @@ -8,6 +8,7 @@ class OBTextFormField extends StatelessWidget { final TextEditingController controller; final FormFieldValidator validator; final bool autofocus; + final bool readOnly; final InputDecoration decoration; final int maxLines; final TextInputType keyboardType; @@ -25,6 +26,7 @@ class OBTextFormField extends StatelessWidget { {this.controller, this.validator, this.autofocus = false, + this.readOnly = false, this.keyboardType, this.inputFormatters, this.obscureText = false, @@ -85,6 +87,7 @@ class OBTextFormField extends StatelessWidget { textCapitalization: textCapitalization, textInputAction: textInputAction, autofocus: autofocus, + readOnly: readOnly, controller: controller, validator: validator, keyboardType: keyboardType, diff --git a/lib/widgets/icon.dart b/lib/widgets/icon.dart index 5411eec53..70c4dc169 100644 --- a/lib/widgets/icon.dart +++ b/lib/widgets/icon.dart @@ -194,7 +194,10 @@ class OBIcons { static const report = OBIconData(nativeIcon: Icons.flag); static const filter = OBIconData(nativeIcon: Icons.tune); static const gallery = OBIconData(nativeIcon: Icons.apps); + static const movie = OBIconData(nativeIcon: Icons.local_movies); + static const picture = OBIconData(nativeIcon: Icons.image); static const camera = OBIconData(nativeIcon: Icons.camera_alt); + static const video_camera = OBIconData(nativeIcon: Icons.videocam); static const privateCommunity = OBIconData(nativeIcon: Icons.lock); static const publicCommunity = OBIconData(nativeIcon: Icons.public); static const communityDescription = OBIconData(nativeIcon: Icons.book); @@ -269,6 +272,7 @@ class OBIcons { static const profile = OBIconData(filename: 'profile-icon.png'); static const photo = OBIconData(filename: 'photo-icon.png'); static const video = OBIconData(filename: 'video-icon.png'); + static const cameraCartoon = OBIconData(filename: 'camera-icon.png'); static const gif = OBIconData(filename: 'gif-icon.png'); static const audience = OBIconData(filename: 'audience-icon.png'); static const burner = OBIconData(filename: 'burner-icon.png'); diff --git a/lib/widgets/new_post_data_uploader.dart b/lib/widgets/new_post_data_uploader.dart index d3787c00d..c2362c591 100644 --- a/lib/widgets/new_post_data_uploader.dart +++ b/lib/widgets/new_post_data_uploader.dart @@ -6,7 +6,7 @@ import 'package:Okuna/models/community.dart'; import 'package:Okuna/models/post.dart'; import 'package:Okuna/provider.dart'; import 'package:Okuna/services/localization.dart'; -import 'package:Okuna/services/media.dart'; +import 'package:Okuna/services/media/media.dart'; import 'package:Okuna/services/user.dart'; import 'package:Okuna/widgets/theming/highlighted_box.dart'; import 'package:Okuna/widgets/theming/text.dart'; diff --git a/lib/widgets/routes/fadein_material_route.dart b/lib/widgets/routes/fadein_material_route.dart deleted file mode 100644 index 5e9795f5c..000000000 --- a/lib/widgets/routes/fadein_material_route.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:flutter/material.dart'; - -class OBFadeInMaterialPageRoute extends MaterialPageRoute { - OBFadeInMaterialPageRoute({ WidgetBuilder builder, RouteSettings settings, bool fullscreenDialog }) - : super(builder: builder, settings: settings, fullscreenDialog: fullscreenDialog); - - @override - Widget buildTransitions(BuildContext context, - Animation animation, - Animation secondaryAnimation, - Widget child) { - if (settings.isInitialRoute) return child; - return new FadeTransition(opacity: animation, child: child); - } -} \ No newline at end of file diff --git a/lib/widgets/video_player/widgets/chewie/chewie_player.dart b/lib/widgets/video_player/widgets/chewie/chewie_player.dart index a1d2bc26c..c9166bc49 100644 --- a/lib/widgets/video_player/widgets/chewie/chewie_player.dart +++ b/lib/widgets/video_player/widgets/chewie/chewie_player.dart @@ -1,12 +1,13 @@ import 'dart:async'; -import 'package:Okuna/widgets/video_player/widgets/chewie/chewie_progress_colors.dart'; import 'package:Okuna/widgets/video_player/widgets/chewie/player_with_controls.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; -import 'package:screen/screen.dart'; import 'package:video_player/video_player.dart'; +import 'package:wakelock/wakelock.dart'; + +import 'chewie_progress_colors.dart'; typedef Widget ChewieRoutePageBuilder( BuildContext context, @@ -69,7 +70,7 @@ class ChewieState extends State { _isFullScreen = true; await _pushFullScreenWidget(context); } else if (_isFullScreen) { - Navigator.of(context).pop(); + Navigator.of(context, rootNavigator: true).pop(); _isFullScreen = false; } } @@ -110,10 +111,10 @@ class ChewieState extends State { } Widget _fullScreenRoutePageBuilder( - BuildContext context, - Animation animation, - Animation secondaryAnimation, - ) { + BuildContext context, + Animation animation, + Animation secondaryAnimation, + ) { var controllerProvider = _ChewieControllerProvider( controller: widget.controller, child: PlayerWithControls(), @@ -130,7 +131,6 @@ class ChewieState extends State { Future _pushFullScreenWidget(BuildContext context) async { final isAndroid = Theme.of(context).platform == TargetPlatform.android; final TransitionRoute route = PageRouteBuilder( - settings: RouteSettings(isInitialRoute: false), pageBuilder: _fullScreenRoutePageBuilder, ); @@ -143,17 +143,16 @@ class ChewieState extends State { } if (!widget.controller.allowedScreenSleep) { - Screen.keepOn(true); + Wakelock.enable(); } - await Navigator.of(context).push(route); + await Navigator.of(context, rootNavigator: true).push(route); _isFullScreen = false; widget.controller.exitFullScreen(); - bool isKeptOn = await Screen.isKeptOn; - if (isKeptOn) { - Screen.keepOn(false); - } + // The wakelock plugins checks whether it needs to perform an action internally, + // so we do not need to check Wakelock.isEnabled. + Wakelock.disable(); SystemChrome.setEnabledSystemUIOverlays( widget.controller.systemOverlaysAfterFullScreen); @@ -202,7 +201,7 @@ class ChewieController extends ChangeNotifier { ], this.routePageBuilder = null, }) : assert(videoPlayerController != null, - 'You must provide a controller to play a video') { + 'You must provide a controller to play a video') { _initialize(); } @@ -282,8 +281,8 @@ class ChewieController extends ChangeNotifier { static ChewieController of(BuildContext context) { final chewieControllerProvider = - context.inheritFromWidgetOfExactType(_ChewieControllerProvider) - as _ChewieControllerProvider; + context.inheritFromWidgetOfExactType(_ChewieControllerProvider) + as _ChewieControllerProvider; return chewieControllerProvider.controller; } @@ -292,6 +291,8 @@ class ChewieController extends ChangeNotifier { bool get isFullScreen => _isFullScreen; + bool get isPlaying => videoPlayerController.value.isPlaying; + Future _initialize() async { await videoPlayerController.setLooping(looping); @@ -339,6 +340,10 @@ class ChewieController extends ChangeNotifier { notifyListeners(); } + void togglePause() { + isPlaying ? pause() : play(); + } + Future play() async { await videoPlayerController.play(); } @@ -374,4 +379,4 @@ class _ChewieControllerProvider extends InheritedWidget { @override bool updateShouldNotify(_ChewieControllerProvider old) => controller != old.controller; -} +} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 13e60c1eb..6e60e02d4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,42 +7,35 @@ packages: name: _fe_analyzer_shared url: "https://pub.dartlang.org" source: hosted - version: "1.0.3" - after_layout: - dependency: transitive - description: - name: after_layout - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.7+2" + version: "3.0.0" analyzer: dependency: transitive description: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "0.39.4" + version: "0.39.8" archive: dependency: transitive description: name: archive url: "https://pub.dartlang.org" source: hosted - version: "2.0.11" + version: "2.0.13" args: dependency: transitive description: name: args url: "https://pub.dartlang.org" source: hosted - version: "1.5.2" + version: "1.6.0" async: dependency: transitive description: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.4.0" + version: "2.4.1" back_button_interceptor: dependency: "direct main" description: @@ -56,7 +49,7 @@ packages: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "1.0.5" + version: "2.0.0" cached_network_image: dependency: "direct main" description: @@ -70,21 +63,42 @@ packages: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.1.2" + version: "1.1.3" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.14.11" + version: "1.14.12" connectivity: dependency: "direct main" description: name: connectivity url: "https://pub.dartlang.org" source: hosted - version: "0.4.6+2" + version: "0.4.8+2" + connectivity_macos: + dependency: transitive + description: + name: connectivity_macos + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0+2" + connectivity_platform_interface: + dependency: transitive + description: + name: connectivity_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.5" convert: dependency: transitive description: @@ -98,14 +112,14 @@ packages: name: coverage url: "https://pub.dartlang.org" source: hosted - version: "0.13.6" + version: "0.13.9" crypto: dependency: transitive description: name: crypto url: "https://pub.dartlang.org" source: hosted - version: "2.1.3" + version: "2.1.4" csslib: dependency: transitive description: @@ -126,7 +140,7 @@ packages: name: dart_style url: "https://pub.dartlang.org" source: hosted - version: "1.3.3" + version: "1.3.6" dcache: dependency: "direct main" description: @@ -140,7 +154,7 @@ packages: name: device_info url: "https://pub.dartlang.org" source: hosted - version: "0.4.1+4" + version: "0.4.2+2" exif: dependency: "direct main" description: @@ -155,13 +169,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.2.3" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" file_picker: dependency: "direct main" description: name: file_picker url: "https://pub.dartlang.org" source: hosted - version: "1.4.3+2" + version: "1.9.0" flutter: dependency: "direct main" description: flutter @@ -173,7 +194,7 @@ packages: name: flutter_advanced_networkimage url: "https://pub.dartlang.org" source: hosted - version: "0.6.0-alpha.1" + version: "0.7.0" flutter_cache_manager: dependency: "direct main" description: @@ -187,7 +208,7 @@ packages: name: flutter_colorpicker url: "https://pub.dartlang.org" source: hosted - version: "0.3.1" + version: "0.3.4" flutter_ffmpeg: dependency: "direct main" description: @@ -201,14 +222,14 @@ packages: name: flutter_image_compress url: "https://pub.dartlang.org" source: hosted - version: "0.6.5+1" + version: "0.6.7" flutter_launcher_icons: dependency: "direct dev" description: name: flutter_launcher_icons url: "https://pub.dartlang.org" source: hosted - version: "0.7.4" + version: "0.7.5" flutter_localizations: dependency: "direct main" description: flutter @@ -234,14 +255,14 @@ packages: name: flutter_plugin_android_lifecycle url: "https://pub.dartlang.org" source: hosted - version: "1.0.5" + version: "1.0.7" flutter_secure_storage: dependency: "direct main" description: name: flutter_secure_storage url: "https://pub.dartlang.org" source: hosted - version: "3.3.1+1" + version: "3.3.3" flutter_slidable: dependency: "direct main" description: @@ -255,7 +276,7 @@ packages: name: flutter_svg url: "https://pub.dartlang.org" source: hosted - version: "0.14.4" + version: "0.17.4" flutter_test: dependency: "direct dev" description: flutter @@ -272,7 +293,7 @@ packages: name: flutter_widgets url: "https://pub.dartlang.org" source: hosted - version: "0.1.11" + version: "0.1.12" glob: dependency: transitive description: @@ -293,7 +314,7 @@ packages: name: http url: "https://pub.dartlang.org" source: hosted - version: "0.12.0+4" + version: "0.12.1" http_multi_server: dependency: transitive description: @@ -307,7 +328,7 @@ packages: name: http_parser url: "https://pub.dartlang.org" source: hosted - version: "3.1.3" + version: "3.1.4" http_retry: dependency: "direct main" description: @@ -321,7 +342,7 @@ packages: name: image url: "https://pub.dartlang.org" source: hosted - version: "2.1.4" + version: "2.1.12" image_cropper: dependency: "direct main" description: @@ -342,21 +363,21 @@ packages: name: intercom_flutter url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.2.0" intl: dependency: "direct main" description: name: intl url: "https://pub.dartlang.org" source: hosted - version: "0.16.0" + version: "0.16.1" intl_translation: dependency: "direct dev" description: name: intl_translation url: "https://pub.dartlang.org" source: hosted - version: "0.17.9" + version: "0.17.10" inview_notifier_list: dependency: "direct main" description: @@ -372,7 +393,7 @@ packages: name: io url: "https://pub.dartlang.org" source: hosted - version: "0.3.3" + version: "0.3.4" js: dependency: transitive description: @@ -449,7 +470,7 @@ packages: name: onesignal_flutter url: "https://pub.dartlang.org" source: hosted - version: "2.3.3" + version: "2.4.1" open_iconic_flutter: dependency: "direct main" description: @@ -463,28 +484,21 @@ packages: name: package_config url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.9.3" package_info: dependency: "direct main" description: name: package_info url: "https://pub.dartlang.org" source: hosted - version: "0.4.0+13" - package_resolver: - dependency: transitive - description: - name: package_resolver - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.10" + version: "0.4.0+17" path: dependency: "direct main" description: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.6.4" + version: "1.7.0" path_drawing: dependency: transitive description: @@ -505,28 +519,42 @@ packages: name: path_provider url: "https://pub.dartlang.org" source: hosted - version: "1.6.0" + version: "1.6.7" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.4+1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" pedantic: dependency: transitive description: name: pedantic url: "https://pub.dartlang.org" source: hosted - version: "1.8.0+1" + version: "1.9.0" petitparser: dependency: transitive description: name: petitparser url: "https://pub.dartlang.org" source: hosted - version: "2.4.0" + version: "3.0.2" photo_view: dependency: "direct main" description: name: photo_view url: "https://pub.dartlang.org" source: hosted - version: "0.4.2" + version: "0.9.2" pigment: dependency: "direct main" description: @@ -541,6 +569,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.2.1" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" pool: dependency: transitive description: @@ -554,7 +589,7 @@ packages: name: pub_semver url: "https://pub.dartlang.org" source: hosted - version: "1.4.3" + version: "1.4.4" public_suffix: dependency: "direct main" description: @@ -575,7 +610,7 @@ packages: name: quiver url: "https://pub.dartlang.org" source: hosted - version: "2.0.5" + version: "2.1.3" retry: dependency: "direct main" description: @@ -610,35 +645,35 @@ packages: name: share url: "https://pub.dartlang.org" source: hosted - version: "0.6.3+5" + version: "0.6.4" shared_preferences: dependency: "direct main" description: name: shared_preferences url: "https://pub.dartlang.org" source: hosted - version: "0.5.6+1" + version: "0.5.7" shared_preferences_macos: dependency: transitive description: name: shared_preferences_macos url: "https://pub.dartlang.org" source: hosted - version: "0.0.1+5" + version: "0.0.1+7" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.1" + version: "1.0.3" shared_preferences_web: dependency: transitive description: name: shared_preferences_web url: "https://pub.dartlang.org" source: hosted - version: "0.1.2+3" + version: "0.1.2+4" shelf: dependency: transitive description: @@ -652,7 +687,7 @@ packages: name: shelf_packages_handler url: "https://pub.dartlang.org" source: hosted - version: "1.0.4" + version: "2.0.0" shelf_static: dependency: transitive description: @@ -673,7 +708,7 @@ packages: name: shimmer url: "https://pub.dartlang.org" source: hosted - version: "1.0.1" + version: "1.1.1" sky_engine: dependency: transitive description: flutter @@ -685,7 +720,7 @@ packages: name: source_map_stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.1.5" + version: "2.0.0" source_maps: dependency: transitive description: @@ -699,7 +734,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.5.5" + version: "1.7.0" sprintf: dependency: "direct main" description: @@ -713,7 +748,14 @@ packages: name: sqflite url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.0" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0+1" stack_trace: dependency: transitive description: @@ -762,21 +804,21 @@ packages: name: test url: "https://pub.dartlang.org" source: hosted - version: "1.9.4" + version: "1.14.3" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.11" + version: "0.2.15" test_core: dependency: transitive description: name: test_core url: "https://pub.dartlang.org" source: hosted - version: "0.2.15" + version: "0.3.4" throttling: dependency: "direct main" description: @@ -811,7 +853,7 @@ packages: name: uni_links url: "https://pub.dartlang.org" source: hosted - version: "0.1.4" + version: "0.2.0" url_launcher: dependency: "direct main" description: @@ -869,21 +911,21 @@ packages: name: vm_service url: "https://pub.dartlang.org" source: hosted - version: "2.3.1" + version: "4.0.2" wakelock: dependency: "direct main" description: name: wakelock url: "https://pub.dartlang.org" source: hosted - version: "0.1.3+4" + version: "0.1.4+1" watcher: dependency: transitive description: name: watcher url: "https://pub.dartlang.org" source: hosted - version: "0.9.7+13" + version: "0.9.7+15" web_socket_channel: dependency: transitive description: @@ -891,20 +933,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.0" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + url: "https://pub.dartlang.org" + source: hosted + version: "0.5.1" xml: dependency: transitive description: name: xml url: "https://pub.dartlang.org" source: hosted - version: "3.5.0" + version: "3.7.0" yaml: dependency: transitive description: name: yaml url: "https://pub.dartlang.org" source: hosted - version: "2.2.0" + version: "2.2.1" sdks: dart: ">=2.7.0 <3.0.0" flutter: ">=1.12.13+hotfix.7 <2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index f960d93e9..e4af09c5a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,7 +7,7 @@ description: Social Network # Both the version and the builder number may be overridden in flutter # build by specifying --build-name and --build-number, respectively. # Read more about versioning at semver.org. -version: 0.0.65+82 +version: 0.0.66+83 environment: sdk: ">=2.1.0 <3.0.0" @@ -31,7 +31,7 @@ dependencies: retry: ^3.0.0+1 shared_preferences: ^0.5.2 flutter_markdown: ^0.2.0 - file_picker: ^1.4.2 + file_picker: ^1.9.0 sentry: ^2.2.0 screen: ^0.0.5 back_button_interceptor: ^4.0.1 @@ -41,19 +41,19 @@ dependencies: flutter_pagewise: ^1.2.2 tinycolor: ^1.0.2 onesignal_flutter: ^2.2.0 - flutter_advanced_networkimage: 0.6.0-alpha.1 + flutter_advanced_networkimage: ^0.7.0 dcache: ^0.1.0 validators: ^1.0.0+1 url_launcher: ^4.0.1 - uni_links: ^0.1.4 + uni_links: ^0.2.0 flutter_slidable: "^0.4.9" flutter_cache_manager: ^1.1.1 cached_network_image: ^2.0.0-rc timeago: ^2.0.9 public_suffix: ^1.2.0 pigment: ^1.0.3 - photo_view: ^0.4.2 - flutter_svg: ^0.14.0 + photo_view: ^0.9.2 + flutter_svg: ^0.17.4 flutter_secure_storage: ^3.1.2 mime: ^0.9.6+2 http: ^0.12.0 @@ -70,8 +70,8 @@ dependencies: image_picker: 0.6.3+1 image_cropper: ^1.2.1 shimmer: ^1.0.0 - share: ^0.6.1 - path: ^1.6.2 + share: ^0.6.4 + path: ^1.7.0 package_info: ^0.4.0 flutter: sdk: flutter @@ -143,6 +143,7 @@ flutter: - assets/images/icons/chat-icon.png - assets/images/icons/photo-icon.png - assets/images/icons/video-icon.png + - assets/images/icons/camera-icon.png - assets/images/icons/warning-icon.png - assets/images/icons/success-icon.png - assets/images/icons/error-icon.png