mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: request permission again if the user denied the photo permission (#5251)
* fix: clear the email field after sending email * fix: ask permission before picking image * feat: improve photo permission UI design * chore: update translations * fix: android photo permission * chore: update translations * fix: awareness meta data decode error
This commit is contained in:
parent
d52042fa4f
commit
266a2a53ab
@ -47,6 +47,12 @@
|
||||
</application>
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<!-- Permission to read files from external storage (outside application container).
|
||||
As of Android 12 this permission no longer has any effect. Instead use the
|
||||
READ_MEDIA_IMAGES, READ_MEDIA_VIDEO or READM_MEDIA_AUDIO permissions. -->
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
|
||||
<!-- Permissions to read media files. -->
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.support.customtabs.action.CustomTabsService" />
|
||||
|
@ -37,6 +37,16 @@ end
|
||||
post_install do |installer|
|
||||
installer.pods_project.targets.each do |target|
|
||||
flutter_additional_ios_build_settings(target)
|
||||
|
||||
target.build_configurations.each do |config|
|
||||
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
|
||||
'$(inherited)',
|
||||
|
||||
# dart: PermissionGroup.photos
|
||||
'PERMISSION_PHOTOS=1',
|
||||
]
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
installer.aggregate_targets.each do |target|
|
||||
|
@ -63,6 +63,8 @@ PODS:
|
||||
- path_provider_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- permission_handler_apple (9.3.0):
|
||||
- Flutter
|
||||
- ReachabilitySwift (5.0.0)
|
||||
- SDWebImage (5.14.2):
|
||||
- SDWebImage/Core (= 5.14.2)
|
||||
@ -98,6 +100,7 @@ DEPENDENCIES:
|
||||
- keyboard_height_plugin (from `.symlinks/plugins/keyboard_height_plugin/ios`)
|
||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
|
||||
@ -144,6 +147,8 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||
path_provider_foundation:
|
||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||
permission_handler_apple:
|
||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||
share_plus:
|
||||
:path: ".symlinks/plugins/share_plus/ios"
|
||||
shared_preferences_foundation:
|
||||
@ -173,6 +178,7 @@ SPEC CHECKSUMS:
|
||||
keyboard_height_plugin: 43fa8bba20fd5c4fdeed5076466b8b9d43cc6b86
|
||||
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
|
||||
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
|
||||
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
|
||||
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
||||
SDWebImage: b9a731e1d6307f44ca703b3976d18c24ca561e84
|
||||
share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5
|
||||
@ -183,6 +189,6 @@ SPEC CHECKSUMS:
|
||||
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
|
||||
url_launcher_ios: bbd758c6e7f9fd7b5b1d4cde34d2b95fcce5e812
|
||||
|
||||
PODFILE CHECKSUM: d94f9be27d1db182e9bc77d10f065555d518f127
|
||||
PODFILE CHECKSUM: d0d9b4ff572d8695c38eb3f9b490f55cdfc57eca
|
||||
|
||||
COCOAPODS: 1.11.3
|
||||
|
@ -127,6 +127,7 @@
|
||||
97C146EC1CF9000F007C117D /* Resources */,
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||
08FAA63113168DEC7FB74204 /* [CP] Embed Pods Frameworks */,
|
||||
A548E58D5F4006A34D7DAA88 /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@ -233,6 +234,23 @@
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
||||
};
|
||||
A548E58D5F4006A34D7DAA88 /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
E790B8FE5609053209ED85CB /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
@ -1,53 +1,86 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
enum ConfirmDialogActionAlignment {
|
||||
// The action buttons are aligned vertically
|
||||
// ---------------------
|
||||
// | Action Button |
|
||||
// | Cancel Button |
|
||||
vertical,
|
||||
// The action buttons are aligned horizontally
|
||||
// ---------------------
|
||||
// | Action Button | Cancel Button |
|
||||
horizontal,
|
||||
}
|
||||
|
||||
/// show the dialog to confirm one single action
|
||||
/// [onActionButtonPressed] and [onCancelButtonPressed] end with close the dialog
|
||||
Future<T?> showFlowyMobileConfirmDialog<T>(
|
||||
BuildContext context, {
|
||||
Widget? title,
|
||||
Widget? content,
|
||||
ConfirmDialogActionAlignment actionAlignment =
|
||||
ConfirmDialogActionAlignment.horizontal,
|
||||
required String actionButtonTitle,
|
||||
required VoidCallback? onActionButtonPressed,
|
||||
Color? actionButtonColor,
|
||||
String? cancelButtonTitle,
|
||||
required void Function()? onActionButtonPressed,
|
||||
void Function()? onCancelButtonPressed,
|
||||
Color? cancelButtonColor,
|
||||
VoidCallback? onCancelButtonPressed,
|
||||
}) async {
|
||||
return showDialog(
|
||||
context: context,
|
||||
builder: (dialogContext) {
|
||||
final foregroundColor = Theme.of(context).colorScheme.onSurface;
|
||||
return AlertDialog.adaptive(
|
||||
title: title,
|
||||
content: content,
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text(
|
||||
final actionButton = TextButton(
|
||||
child: FlowyText(
|
||||
actionButtonTitle,
|
||||
style: TextStyle(
|
||||
color: actionButtonColor ?? foregroundColor,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
onActionButtonPressed?.call();
|
||||
// we cannot use dialogContext.pop() here because this is no GoRouter in dialogContext. Use Navigator instead to close the dialog.
|
||||
Navigator.of(dialogContext).pop();
|
||||
},
|
||||
),
|
||||
TextButton(
|
||||
child: Text(
|
||||
);
|
||||
final cancelButton = TextButton(
|
||||
child: FlowyText(
|
||||
cancelButtonTitle ?? LocaleKeys.button_cancel.tr(),
|
||||
style: TextStyle(
|
||||
color: foregroundColor,
|
||||
),
|
||||
color: cancelButtonColor ?? foregroundColor,
|
||||
),
|
||||
onPressed: () {
|
||||
onCancelButtonPressed?.call();
|
||||
Navigator.of(dialogContext).pop();
|
||||
},
|
||||
);
|
||||
|
||||
final actions = switch (actionAlignment) {
|
||||
ConfirmDialogActionAlignment.horizontal => [
|
||||
actionButton,
|
||||
cancelButton,
|
||||
],
|
||||
ConfirmDialogActionAlignment.vertical => [
|
||||
Column(
|
||||
children: [
|
||||
actionButton,
|
||||
const Divider(height: 1, color: Colors.grey),
|
||||
cancelButton,
|
||||
],
|
||||
),
|
||||
],
|
||||
};
|
||||
|
||||
return AlertDialog.adaptive(
|
||||
title: title,
|
||||
content: content,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 24.0,
|
||||
vertical: 4.0,
|
||||
),
|
||||
actionsAlignment: MainAxisAlignment.center,
|
||||
actions: actions,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
@ -182,14 +182,21 @@ class DocumentCollabAdapter {
|
||||
);
|
||||
for (final state in values) {
|
||||
// the following code is only for version 1
|
||||
if (state.version != 1) {
|
||||
if (state.version != 1 || state.metadata.isEmpty) {
|
||||
return;
|
||||
}
|
||||
final uid = state.user.uid.toString();
|
||||
final did = state.user.deviceId;
|
||||
final metadata = DocumentAwarenessMetadata.fromJson(
|
||||
debugPrint('metadata: ${state.metadata}');
|
||||
DocumentAwarenessMetadata metadata;
|
||||
try {
|
||||
metadata = DocumentAwarenessMetadata.fromJson(
|
||||
jsonDecode(state.metadata),
|
||||
);
|
||||
} catch (e) {
|
||||
Log.error('Failed to parse metadata: $e, ${state.metadata}');
|
||||
continue;
|
||||
}
|
||||
final selectionColor = metadata.selectionColor.tryToColor();
|
||||
final cursorColor = metadata.cursorColor.tryToColor();
|
||||
if ((uid == userId && did == deviceId) ||
|
||||
|
@ -1,21 +1,28 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/application/page_style/document_page_style_bloc.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||
import 'package:appflowy/mobile/presentation/widgets/show_flowy_mobile_confirm_dialog.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/image_util.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/unsplash_image_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/page_style/_page_cover_bottom_sheet.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/page_style/_page_style_util.dart';
|
||||
import 'package:appflowy/shared/feedback_gesture_detector.dart';
|
||||
import 'package:appflowy/startup/tasks/device_info_task.dart';
|
||||
import 'package:appflowy/user/application/user_service.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/snap_bar.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
|
||||
class PageStyleCoverImage extends StatelessWidget {
|
||||
PageStyleCoverImage({
|
||||
@ -114,9 +121,22 @@ class PageStyleCoverImage extends StatelessWidget {
|
||||
}
|
||||
|
||||
Future<void> _pickImage(BuildContext context) async {
|
||||
final result = await _imagePicker.pickImage(
|
||||
final photoPermission = await _checkPhotoPermission(context);
|
||||
if (!photoPermission) {
|
||||
Log.error('Has no permission to access the photo library');
|
||||
return;
|
||||
}
|
||||
|
||||
XFile? result;
|
||||
try {
|
||||
result = await _imagePicker.pickImage(
|
||||
source: ImageSource.gallery,
|
||||
);
|
||||
} catch (e) {
|
||||
Log.error('Error while picking image: $e');
|
||||
return;
|
||||
}
|
||||
|
||||
final path = result?.path;
|
||||
if (path != null && context.mounted) {
|
||||
final String? result;
|
||||
@ -204,6 +224,54 @@ class PageStyleCoverImage extends StatelessWidget {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<bool> _checkPhotoPermission(BuildContext context) async {
|
||||
// check the permission first
|
||||
final status = await Permission.photos.status;
|
||||
// if the permission is permanently denied, we should open the app settings
|
||||
if (status.isPermanentlyDenied && context.mounted) {
|
||||
unawaited(
|
||||
showFlowyMobileConfirmDialog(
|
||||
context,
|
||||
title: FlowyText.semibold(
|
||||
LocaleKeys.pageStyle_photoPermissionTitle.tr(),
|
||||
maxLines: 3,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
content: FlowyText(
|
||||
LocaleKeys.pageStyle_photoPermissionDescription.tr(),
|
||||
maxLines: 5,
|
||||
textAlign: TextAlign.center,
|
||||
fontSize: 12.0,
|
||||
),
|
||||
actionAlignment: ConfirmDialogActionAlignment.vertical,
|
||||
actionButtonTitle: LocaleKeys.pageStyle_openSettings.tr(),
|
||||
actionButtonColor: Colors.blue,
|
||||
cancelButtonTitle: LocaleKeys.pageStyle_doNotAllow.tr(),
|
||||
cancelButtonColor: Colors.blue,
|
||||
onActionButtonPressed: () {
|
||||
openAppSettings();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
return false;
|
||||
} else if (status.isDenied) {
|
||||
// https://github.com/Baseflow/flutter-permission-handler/issues/1262#issuecomment-2006340937
|
||||
Permission permission = Permission.photos;
|
||||
if (defaultTargetPlatform == TargetPlatform.android &&
|
||||
ApplicationInfo.androidSDKVersion <= 32) {
|
||||
permission = Permission.storage;
|
||||
}
|
||||
// if the permission is denied, we should request the permission
|
||||
final newStatus = await permission.request();
|
||||
if (newStatus.isDenied) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class _UnsplashCover extends StatelessWidget {
|
||||
|
@ -1,5 +1,3 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/shared/af_role_pb_extension.dart';
|
||||
@ -18,6 +16,7 @@ import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
||||
import 'package:flowy_infra_ui/widget/rounded_button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:string_validator/string_validator.dart';
|
||||
|
||||
@ -215,6 +214,8 @@ class _InviteMemberState extends State<_InviteMember> {
|
||||
context
|
||||
.read<WorkspaceMemberBloc>()
|
||||
.add(WorkspaceMemberEvent.inviteWorkspaceMember(email));
|
||||
// clear the email field after inviting
|
||||
_emailController.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1329,6 +1329,54 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.3"
|
||||
permission_handler:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: permission_handler
|
||||
sha256: "18bf33f7fefbd812f37e72091a15575e72d5318854877e0e4035a24ac1113ecb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.3.1"
|
||||
permission_handler_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_android
|
||||
sha256: "8bb852cd759488893805c3161d0b2b5db55db52f773dbb014420b304055ba2c5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "12.0.6"
|
||||
permission_handler_apple:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_apple
|
||||
sha256: e9ad66020b89ff1b63908f247c2c6f931c6e62699b756ef8b3c4569350cd8662
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.4.4"
|
||||
permission_handler_html:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_html
|
||||
sha256: "54bf176b90f6eddd4ece307e2c06cf977fb3973719c35a93b85cc7093eb6070d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.1"
|
||||
permission_handler_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_platform_interface
|
||||
sha256: "48d4fcf201a1dad93ee869ab0d4101d084f49136ec82a8a06ed9cfeacab9fd20"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.1"
|
||||
permission_handler_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_windows
|
||||
sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.1"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -134,6 +134,7 @@ dependencies:
|
||||
avatar_stack: ^1.2.0
|
||||
numerus: ^2.1.2
|
||||
flutter_animate: ^4.5.0
|
||||
permission_handler: ^11.3.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_lints: ^3.0.1
|
||||
|
@ -1534,7 +1534,11 @@
|
||||
"photo": "Photo",
|
||||
"unsplash": "Unsplash",
|
||||
"pageCover": "Page cover",
|
||||
"none": "None"
|
||||
"none": "None",
|
||||
"photoPermissionDescription": "Allow access to the photo library for uploading images.",
|
||||
"openSettings": "Open Settings",
|
||||
"photoPermissionTitle": "AppFlowy Would Like to Access Your Photo Library",
|
||||
"doNotAllow": "Don't Allow"
|
||||
},
|
||||
"commandPalette": {
|
||||
"placeholder": "Type to search for views...",
|
||||
|
Loading…
Reference in New Issue
Block a user