Skip to content

Commit

Permalink
implemented web
Browse files Browse the repository at this point in the history
  • Loading branch information
miquelbeltran committed Dec 18, 2024
1 parent 5952490 commit 9294109
Show file tree
Hide file tree
Showing 2 changed files with 166 additions and 224 deletions.
379 changes: 156 additions & 223 deletions packages/share_plus/share_plus/lib/src/share_plus_web.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import 'dart:developer' as developer;
import 'dart:js_interop';
import 'dart:typed_data';
import 'dart:ui';

import 'package:flutter_web_plugins/flutter_web_plugins.dart';
import 'package:meta/meta.dart';
import 'package:mime/mime.dart' show lookupMimeType;
import 'package:share_plus/share_plus.dart';
import 'package:share_plus_platform_interface/share_plus_platform_interface.dart';
import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';
import 'package:url_launcher_web/url_launcher_web.dart';
Expand All @@ -29,229 +27,164 @@ class SharePlusWebPlugin extends SharePlatform {
@visibleForTesting Navigator? debugNavigator,
}) : _navigator = debugNavigator ?? window.navigator;

// @override
// Future<ShareResult> shareUri(
// Uri uri, {
// Rect? sharePositionOrigin,
// }) async {
// final data = ShareData(
// url: uri.toString(),
// );

// final bool canShare;
// try {
// canShare = _navigator.canShare(data);
// } on NoSuchMethodError catch (e) {
// developer.log(
// 'Share API is not supported in this User Agent.',
// error: e,
// );

// throw Exception('Navigator.canShare() is unavailable');
// }

// if (!canShare) {
// throw Exception('Navigator.canShare() is false');
// }

// try {
// await _navigator.share(data).toDart;
// } on DOMException catch (e) {
// if (e.name case 'AbortError') {
// return _resultDismissed;
// }

// developer.log(
// 'Failed to share uri',
// error: '${e.name}: ${e.message}',
// );

// throw Exception('Navigator.share() failed: ${e.message}');
// }

// return ShareResult.unavailable;
// }

// @override
// Future<ShareResult> share(
// String text, {
// String? subject,
// Rect? sharePositionOrigin,
// }) async {
// final ShareData data;
// if (subject != null && subject.isNotEmpty) {
// data = ShareData(
// title: subject,
// text: text,
// );
// } else {
// data = ShareData(
// text: text,
// );
// }

// final bool canShare;
// try {
// canShare = _navigator.canShare(data);
// } on NoSuchMethodError catch (e) {
// developer.log(
// 'Share API is not supported in this User Agent.',
// error: e,
// );

// // Navigator is not available or the webPage is not served on https
// final queryParameters = {
// if (subject != null) 'subject': subject,
// 'body': text,
// };

// // see https://github.com/dart-lang/sdk/issues/43838#issuecomment-823551891
// final uri = Uri(
// scheme: 'mailto',
// query: queryParameters.entries
// .map((e) =>
// '${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}')
// .join('&'),
// );

// final launchResult = await urlLauncher.launchUrl(
// uri.toString(),
// const LaunchOptions(),
// );
// if (!launchResult) {
// throw Exception('Failed to launch $uri');
// }

// return ShareResult.unavailable;
// }

// if (!canShare) {
// throw Exception('Navigator.canShare() is false');
// }

// try {
// await _navigator.share(data).toDart;

// // actions is success, but can't get the action name
// return ShareResult.unavailable;
// } on DOMException catch (e) {
// if (e.name case 'AbortError') {
// return _resultDismissed;
// }

// developer.log(
// 'Failed to share text',
// error: '${e.name}: ${e.message}',
// );

// throw Exception('Navigator.share() failed: ${e.message}');
// }
// }

// /// Share [XFile] objects.
// ///
// /// Remarks for the web implementation:
// /// This uses the [Web Share API](https://web.dev/web-share/) if it's
// /// available. This builds on the
// /// [`cross_file`](https://pub.dev/packages/cross_file) package.
// @override
// Future<ShareResult> shareXFiles(
// List<XFile> files, {
// String? subject,
// String? text,
// Rect? sharePositionOrigin,
// List<String>? fileNameOverrides,
// }) async {
// assert(
// fileNameOverrides == null || files.length == fileNameOverrides.length);
// final webFiles = <File>[];
// for (var index = 0; index < files.length; index++) {
// final xFile = files[index];
// final filename = fileNameOverrides?.elementAt(index);
// webFiles.add(await _fromXFile(xFile, nameOverride: filename));
// }

// final ShareData data;
// if (text != null && text.isNotEmpty) {
// if (subject != null && subject.isNotEmpty) {
// data = ShareData(
// files: webFiles.toJS,
// text: text,
// title: subject,
// );
// } else {
// data = ShareData(
// files: webFiles.toJS,
// text: text,
// );
// }
// } else if (subject != null && subject.isNotEmpty) {
// data = ShareData(
// files: webFiles.toJS,
// title: subject,
// );
// } else {
// data = ShareData(
// files: webFiles.toJS,
// );
// }

// final bool canShare;
// try {
// canShare = _navigator.canShare(data);
// } on NoSuchMethodError catch (e) {
// developer.log(
// 'Share API is not supported in this User Agent.',
// error: e,
// );

// return _downloadIfFallbackEnabled(
// files,
// fileNameOverrides,
// 'Navigator.canShare() is unavailable',
// );
// }

// if (!canShare) {
// return _downloadIfFallbackEnabled(
// files,
// fileNameOverrides,
// 'Navigator.canShare() is false',
// );
// }

// try {
// await _navigator.share(data).toDart;

// // actions is success, but can't get the action name
// return ShareResult.unavailable;
// } on DOMException catch (e) {
// final name = e.name;
// final message = e.message;

// if (name case 'AbortError') {
// return _resultDismissed;
// }

// return _downloadIfFallbackEnabled(
// files,
// fileNameOverrides,
// 'Navigator.share() failed: $message',
// );
// }
// }

Future<ShareResult> _downloadIfFallbackEnabled(
List<XFile> files,
List<String>? fileNameOverrides,
String message,
) {
developer.log(message);
if (Share.downloadFallbackEnabled) {
return _download(files, fileNameOverrides);
@override
Future<ShareResult> share(ShareParams params) async {
// Prepare share data params
final ShareData data = await prepareData(params);

// Check if can share
final bool canShare;
try {
canShare = _navigator.canShare(data);
} on NoSuchMethodError catch (e) {
developer.log(
'Share API is not supported in this User Agent.',
error: e,
);

return _fallback(params, 'Navigator.canShare() is unavailable');
}

if (!canShare) {
return _fallback(params, 'Navigator.canShare() is false');
}

try {
await _navigator.share(data).toDart;
} on DOMException catch (e) {
if (e.name case 'AbortError') {
return _resultDismissed;
}

developer.log(
'Failed to share uri',
error: '${e.name}: ${e.message}',
);

return _fallback(params, 'Navigator.share() failed: ${e.message}');
}

return ShareResult.unavailable;
}

Future<ShareData> prepareData(ShareParams params) async {
// Prepare share data params
final uri = params.uri?.toString();
final text = params.text;
final title = params.subject ?? params.title;
ShareData data;

// Prepare files
final webFiles = <File>[];
if (params.files != null) {
final files = params.files;
if (files != null && files.isNotEmpty == true) {
for (var index = 0; index < files.length; index++) {
final xFile = files[index];
final filename = params.fileNameOverrides?.elementAt(index);
webFiles.add(await _fromXFile(xFile, nameOverride: filename));
}
}
}

if (uri == null && text == null && webFiles.isEmpty) {
throw ArgumentError(
'At least one of uri, text, or files must be provided',
);
}

if (uri != null && text != null) {
throw ArgumentError('Only one of uri or text can be provided');
}

if (uri != null) {
data = ShareData(
url: uri,
);
} else if (webFiles.isNotEmpty && text != null && title != null) {
data = ShareData(
text: text,
title: title,
files: webFiles.toJS,
);
} else if (webFiles.isNotEmpty && text != null) {
data = ShareData(
text: text,
files: webFiles.toJS,
);
} else if (webFiles.isNotEmpty && title != null) {
data = ShareData(
title: title,
files: webFiles.toJS,
);
} else if (webFiles.isNotEmpty) {
data = ShareData(
files: webFiles.toJS,
);
} else if (text != null && title != null) {
data = ShareData(
text: text,
title: title,
);
} else {
throw Exception(message);
data = ShareData(
text: text!,
);
}

return data;
}

/// Fallback method to when sharing on web fails.
/// If [ShareParams.downloadFallbackEnabled] is true, it will attempt to download the files.
/// If [ShareParams.mailToFallbackEnabled] is true, it will attempt to share text as email.
/// Otherwise, it will throw an exception.
Future<ShareResult> _fallback(ShareParams params, String error) async {
developer.log(error);

final title = params.title ?? params.subject;
final text = params.text ?? params.uri?.toString() ?? '';
final files = params.files;
final fileNameOverrides = params.fileNameOverrides;
final downloadFallbackEnabled = params.downloadFallbackEnabled;
final mailToFallbackEnabled = params.mailToFallbackEnabled;

if (files != null && files.isNotEmpty) {
if (downloadFallbackEnabled) {
return _download(files, fileNameOverrides);
} else {
throw Exception(error);
}
}

if (!mailToFallbackEnabled) {
throw Exception(error);
}

final queryParameters = {
if (title != null) 'subject': title,
'body': text,
};

// see https://github.com/dart-lang/sdk/issues/43838#issuecomment-823551891
final uri = Uri(
scheme: 'mailto',
query: queryParameters.entries
.map((e) =>
'${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}')
.join('&'),
);

final launchResult = await urlLauncher.launchUrl(
uri.toString(),
const LaunchOptions(),
);

if (!launchResult) {
throw Exception(error);
}

return ShareResult.unavailable;
}

Future<ShareResult> _download(
Expand Down
Loading

0 comments on commit 9294109

Please sign in to comment.