diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..36907521 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,3 @@ +# Contributing + +We welcome contributions! \ No newline at end of file diff --git a/README.md b/README.md index 1a86623c..c5261690 100644 --- a/README.md +++ b/README.md @@ -668,6 +668,10 @@ Special thanks for everyone that have contributed to this project... Made with [contrib.rocks](https://contrib.rocks). +We welcome contributions! + +Please follow these guidelines when contributing to our project. See [CONTRIBUTING.md](./../CONTRIBUTING.md) for more details. + [Quill]: https://quilljs.com/docs/formats [Flutter]: https://github.com/flutter/flutter [FlutterQuill]: https://pub.dev/packages/flutter_quill diff --git a/example/lib/pages/home_page.dart b/example/lib/pages/home_page.dart index 6d24895f..81947f0f 100644 --- a/example/lib/pages/home_page.dart +++ b/example/lib/pages/home_page.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'dart:convert'; -import 'dart:io' show File, Platform; +import 'dart:io' show File; import 'dart:ui'; import 'package:flutter/foundation.dart'; @@ -35,6 +35,7 @@ class _HomePageState extends State { final FocusNode _focusNode = FocusNode(); Timer? _selectAllTimer; _SelectionType _selectionType = _SelectionType.none; + var _isReadOnly = false; @override void dispose() { @@ -85,6 +86,13 @@ class _HomePageState extends State { 'Flutter Quill', ), actions: [ + IconButton( + onPressed: () { + setState(() => _isReadOnly = !_isReadOnly); + }, + icon: Icon( + _isReadOnly ? Icons.lock : Icons.edit, + )), IconButton( onPressed: () => _insertTimeStamp( _controller, @@ -219,7 +227,7 @@ class _HomePageState extends State { return QuillEditor( configurations: QuillEditorConfigurations( placeholder: 'Add content', - readOnly: false, + readOnly: _isReadOnly, autoFocus: false, enableSelectionToolbar: isMobile(), expands: false, @@ -250,7 +258,11 @@ class _HomePageState extends State { ), ), embedBuilders: [ - ...FlutterQuillEmbeds.editorBuilders(), + ...FlutterQuillEmbeds.editorBuilders( + imageEmbedConfigurations: const QuillEditorImageEmbedConfigurations( + forceUseMobileOptionMenuForImageClick: true, + ), + ), TimeStampEmbedBuilderWidget() ], ), @@ -282,7 +294,7 @@ class _HomePageState extends State { // afterButtonPressed: _focusNode.requestFocus, ); } - if (_isDesktop()) { + if (isDesktop()) { return QuillToolbar( configurations: QuillToolbarConfigurations( embedButtons: FlutterQuillEmbeds.toolbarButtons( @@ -292,8 +304,6 @@ class _HomePageState extends State { _onImagePickCallback(File(image)); }, ), - // onImagePickCallback: _onImagePickCallback, - // filePickImpl: openFileSystemPickerForDesktop, ), ), showAlignmentButtons: true, @@ -303,7 +313,6 @@ class _HomePageState extends State { ), ), ), - // afterButtonPressed: _focusNode.requestFocus, ); } return QuillToolbar( @@ -385,24 +394,23 @@ class _HomePageState extends State { child: quillEditor, ), ), - kIsWeb - ? Expanded( - child: Container( - padding: - const EdgeInsets.symmetric(vertical: 16, horizontal: 8), - child: quillToolbar, - )) - : Container( - child: quillToolbar, - ) + if (!_isReadOnly) + kIsWeb + ? Expanded( + child: Container( + padding: const EdgeInsets.symmetric( + vertical: 16, horizontal: 8), + child: quillToolbar, + )) + : Container( + child: quillToolbar, + ) ], ), ), ); } - bool _isDesktop() => !kIsWeb && !Platform.isAndroid && !Platform.isIOS; - // Future _openFileSystemPickerForDesktop(BuildContext context) // async { // return await FilesystemPicker.open( @@ -515,7 +523,7 @@ class _HomePageState extends State { )), dense: true, visualDensity: VisualDensity.compact, - onTap: _readOnly, + onTap: _openReadOnlyPage, ), Divider( thickness: 2, @@ -526,7 +534,7 @@ class _HomePageState extends State { ); } - void _readOnly() { + void _openReadOnlyPage() { Navigator.pop(super.context); Navigator.push( super.context, diff --git a/flutter_quill_extensions/CHANGELOG.md b/flutter_quill_extensions/CHANGELOG.md index 0c462f61..5ff6b595 100644 --- a/flutter_quill_extensions/CHANGELOG.md +++ b/flutter_quill_extensions/CHANGELOG.md @@ -1,5 +1,8 @@ ## 0.6.0-dev.4 - Add more exports +- Update `README.md`` +- Fix save image bug +- Quick fixes ## 0.6.0-dev.3 - Disable the camera option by default on desktop diff --git a/flutter_quill_extensions/README.md b/flutter_quill_extensions/README.md index e33f8362..b1810c95 100644 --- a/flutter_quill_extensions/README.md +++ b/flutter_quill_extensions/README.md @@ -59,17 +59,11 @@ Before starting using this package you must follow the setup Set the `embedBuilders` and `embedToolbar` params in configurations of `QuillEditor` and `QuillToolbar` with the values provided by this repository. -**Quill toolbar**: +**Quill Toolbar**: ```dart QuillToolbar( configurations: QuillToolbarConfigurations( - embedButtons: FlutterQuillEmbeds.toolbarButtons( - imageButtonOptions: QuillToolbarImageButtonOptions( - onImagePickCallback: (file) async { - return file.path; - }, - ), - ), + embedButtons: FlutterQuillEmbeds.toolbarButtons(), ), ), ``` @@ -79,13 +73,7 @@ QuillToolbar( Expanded( child: QuillEditor.basic( configurations: QuillEditorConfigurations( - readOnly: true, - embedBuilders: FlutterQuillEmbeds.editorBuilders( - imageEmbedConfigurations: - const QuillEditorImageEmbedConfigurations( - forceUseMobileOptionMenuForImageClick: true, - ), - ), + embedBuilders: kIsWeb ? FlutterQuillEmbeds.editorsWebBuilders() : FlutterQuillEmbeds.editorBuilders(), ), ), ) @@ -113,7 +101,7 @@ QuillProvider( child: QuillEditor.basic( configurations: QuillEditorConfigurations( padding: const EdgeInsets.all(16), - embedBuilders: FlutterQuillEmbeds.editorBuilders(), + embedBuilders: kIsWeb ? FlutterQuillEmbeds.editorsWebBuilders() : FlutterQuillEmbeds.editorBuilders(), ), ), ) @@ -122,11 +110,6 @@ QuillProvider( ) ``` -For web, use: -```dart -FlutterQuillEmbeds.editorsWebBuilders() -``` - ## Features ```markdown @@ -142,7 +125,7 @@ FlutterQuillEmbeds.editorsWebBuilders() We welcome contributions! -Please follow these guidelines when contributing to our project. See [CONTRIBUTING.md](CONTRIBUTING.md) for more details. +Please follow these guidelines when contributing to our project. See [CONTRIBUTING.md](./../CONTRIBUTING.md) for more details. ## License diff --git a/flutter_quill_extensions/lib/presentation/embeds/editor/image/image.dart b/flutter_quill_extensions/lib/presentation/embeds/editor/image/image.dart index 45fd1399..666fe993 100644 --- a/flutter_quill_extensions/lib/presentation/embeds/editor/image/image.dart +++ b/flutter_quill_extensions/lib/presentation/embeds/editor/image/image.dart @@ -1,5 +1,3 @@ -import 'dart:io' show File; - import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -8,7 +6,7 @@ import 'package:flutter_quill/flutter_quill.dart'; import 'package:flutter_quill/translations.dart'; import '../../../models/config/editor/image.dart'; -import '../../embed_types.dart'; +import '../../embed_types/image.dart'; import '../../utils.dart'; import '../../widgets/image.dart'; import '../../widgets/image_resizer.dart'; @@ -138,11 +136,9 @@ class QuillEditorImageEmbedBuilder extends EmbedBuilder { onPressed: () async { Navigator.of(context).pop(); - final imageFile = File(imageUrl); - // Call the remove check callback if set if (await configurations.shouldRemoveImageCallback - ?.call(imageFile) == + ?.call(imageUrl) == false) { return; } @@ -158,7 +154,7 @@ class QuillEditorImageEmbedBuilder extends EmbedBuilder { TextSelection.collapsed(offset: offset), ); // Call the post remove callback if set - await configurations.onImageRemovedCallback.call(imageFile); + await configurations.onImageRemovedCallback.call(imageUrl); }, ); return Padding( @@ -259,7 +255,7 @@ Widget _menuOptionsForReadonlyImage({ onTap: () { showDialog( context: context, - builder: (context) { + builder: (_) { final saveOption = SimpleDialogItem( icon: Icons.save, color: Colors.greenAccent, diff --git a/flutter_quill_extensions/lib/presentation/embeds/embed_types.dart b/flutter_quill_extensions/lib/presentation/embeds/embed_types.dart index 3a2e9330..249fb40b 100644 --- a/flutter_quill_extensions/lib/presentation/embeds/embed_types.dart +++ b/flutter_quill_extensions/lib/presentation/embeds/embed_types.dart @@ -1,25 +1,8 @@ -import 'dart:io' show File; -import 'dart:typed_data'; - -import 'package:flutter/material.dart' - show ImageErrorWidgetBuilder, BuildContext, ImageProvider; - -typedef OnVideoPickCallback = Future Function(File file); - -/// [FilePickImpl] is an implementation for picking files. -typedef FilePickImpl = Future Function(BuildContext context); - -/// [WebImagePickImpl] is an implementation for picking web images. -// typedef WebImagePickImpl = Future Function( -// OnImagePickCallback onImagePickCallback, -// ); -typedef WebVideoPickImpl = Future Function( - OnVideoPickCallback onImagePickCallback, -); +import 'package:cross_file/cross_file.dart' show XFile; typedef MediaFileUrl = String; -typedef MediaFilePicker = Future Function(QuillMediaType mediaType); -typedef MediaPickedCallback = Future Function(QuillFile file); +typedef MediaFilePicker = Future Function(QuillMediaType mediaType); +typedef MediaPickedCallback = Future Function(XFile file); enum QuillMediaType { image, video } @@ -27,32 +10,3 @@ extension QuillMediaTypeX on QuillMediaType { bool get isImage => this == QuillMediaType.image; bool get isVideo => this == QuillMediaType.video; } - -/// Represents a file data which returned by file picker. -class QuillFile { - QuillFile({ - required this.name, - this.path = '', - Uint8List? bytes, - }) : assert(name.isNotEmpty), - bytes = bytes ?? Uint8List(0); - - final String name; - final String path; - final Uint8List bytes; -} - -typedef ImageEmbedBuilderWillRemoveCallback = Future Function( - File imageFile, -); - -typedef ImageEmbedBuilderOnRemovedCallback = Future Function( - File imageFile, -); - -typedef ImageEmbedBuilderProviderBuilder = ImageProvider Function( - String imageUrl, - // {required bool isLocalImage} -); - -typedef ImageEmbedBuilderErrorWidgetBuilder = ImageErrorWidgetBuilder; diff --git a/flutter_quill_extensions/lib/presentation/embeds/embed_types/image.dart b/flutter_quill_extensions/lib/presentation/embeds/embed_types/image.dart index e191c129..0a10ba6a 100644 --- a/flutter_quill_extensions/lib/presentation/embeds/embed_types/image.dart +++ b/flutter_quill_extensions/lib/presentation/embeds/embed_types/image.dart @@ -1,6 +1,9 @@ +import 'package:flutter/widgets.dart' + show ImageErrorWidgetBuilder, ImageProvider; import 'package:flutter/widgets.dart' show BuildContext; import 'package:flutter_quill/flutter_quill.dart'; import 'package:meta/meta.dart' show immutable; + import '../../../logic/extensions/controller.dart'; import '../../../logic/services/image_picker/s_image_picker.dart'; @@ -63,3 +66,17 @@ class QuillToolbarImageConfigurations { return _onImageInsertCallback ?? defaultOnImageInsertCallback(); } } + +typedef ImageEmbedBuilderWillRemoveCallback = Future Function( + String imageUrl, +); + +typedef ImageEmbedBuilderOnRemovedCallback = Future Function( + String imageUrl, +); + +typedef ImageEmbedBuilderProviderBuilder = ImageProvider Function( + String imageUrl, +); + +typedef ImageEmbedBuilderErrorWidgetBuilder = ImageErrorWidgetBuilder; diff --git a/flutter_quill_extensions/lib/presentation/embeds/toolbar/camera_button/camera_button.dart b/flutter_quill_extensions/lib/presentation/embeds/toolbar/camera_button/camera_button.dart index 404bd589..5cde1ace 100644 --- a/flutter_quill_extensions/lib/presentation/embeds/toolbar/camera_button/camera_button.dart +++ b/flutter_quill_extensions/lib/presentation/embeds/toolbar/camera_button/camera_button.dart @@ -73,7 +73,6 @@ class QuillToolbarCameraButton extends StatelessWidget { if (childBuilder != null) { childBuilder( QuillToolbarCameraButtonOptions( - onVideoPickCallback: options.onVideoPickCallback, afterButtonPressed: _afterButtonPressed(context), iconData: options.iconData, fillColor: options.fillColor, @@ -81,7 +80,6 @@ class QuillToolbarCameraButton extends StatelessWidget { iconTheme: options.iconTheme, tooltip: options.tooltip, cameraConfigurations: options.cameraConfigurations, - webVideoPickImpl: options.webVideoPickImpl, ), QuillToolbarCameraButtonExtraOptions( controller: controller, diff --git a/flutter_quill_extensions/lib/presentation/embeds/toolbar/video_button/video_button.dart b/flutter_quill_extensions/lib/presentation/embeds/toolbar/video_button/video_button.dart index 2c2dbefa..76b5a4d0 100644 --- a/flutter_quill_extensions/lib/presentation/embeds/toolbar/video_button/video_button.dart +++ b/flutter_quill_extensions/lib/presentation/embeds/toolbar/video_button/video_button.dart @@ -85,7 +85,6 @@ class QuillToolbarVideoButton extends StatelessWidget { linkRegExp: options.linkRegExp, tooltip: options.tooltip, iconTheme: options.iconTheme, - webVideoPickImpl: options.webVideoPickImpl, videoConfigurations: options.videoConfigurations, ), QuillToolbarVideoButtonExtraOptions( diff --git a/flutter_quill_extensions/lib/presentation/embeds/widgets/image.dart b/flutter_quill_extensions/lib/presentation/embeds/widgets/image.dart index 8e3b617d..6d00500c 100644 --- a/flutter_quill_extensions/lib/presentation/embeds/widgets/image.dart +++ b/flutter_quill_extensions/lib/presentation/embeds/widgets/image.dart @@ -5,7 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_quill/flutter_quill.dart'; import 'package:photo_view/photo_view.dart'; -import '../embed_types.dart'; +import '../embed_types/image.dart'; import '../utils.dart'; const List imageFileExtensions = [ diff --git a/flutter_quill_extensions/lib/presentation/models/config/editor/image.dart b/flutter_quill_extensions/lib/presentation/models/config/editor/image.dart index 2b494128..31fa75ef 100644 --- a/flutter_quill_extensions/lib/presentation/models/config/editor/image.dart +++ b/flutter_quill_extensions/lib/presentation/models/config/editor/image.dart @@ -1,7 +1,9 @@ +import 'dart:io' show File; + import 'package:flutter_quill/extensions.dart'; import 'package:meta/meta.dart' show immutable; -import '../../../embeds/embed_types.dart'; +import '../../../embeds/embed_types/image.dart'; /// [QuillEditorImageEmbedConfigurations] for desktop, mobile and /// other platforms @@ -109,7 +111,7 @@ class QuillEditorImageEmbedConfigurations { final bool forceUseMobileOptionMenuForImageClick; static ImageEmbedBuilderOnRemovedCallback get defaultOnImageRemovedCallback { - return (imageFile) async { + return (imageUrl) async { final mobile = isMobile(); // If the platform is not mobile, return void; // Since the mobile OS gives us a copy of the image @@ -132,9 +134,15 @@ class QuillEditorImageEmbedConfigurations { // it without // permission - final isFileExists = await imageFile.exists(); + if (isWeb()) { + return; + } + + final dartIoImageFile = File(imageUrl); + + final isFileExists = await dartIoImageFile.exists(); if (isFileExists) { - await imageFile.delete(); + await dartIoImageFile.delete(); } }; } diff --git a/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/camera.dart b/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/camera.dart index c685bb4a..3c4c21cb 100644 --- a/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/camera.dart +++ b/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/camera.dart @@ -1,7 +1,6 @@ import 'package:flutter/widgets.dart' show Color; import 'package:flutter_quill/flutter_quill.dart'; -import '../../../../embeds/embed_types.dart'; import '../../../../embeds/embed_types/camera.dart'; class QuillToolbarCameraButtonExtraOptions @@ -16,9 +15,7 @@ class QuillToolbarCameraButtonExtraOptions class QuillToolbarCameraButtonOptions extends QuillToolbarBaseButtonOptions< QuillToolbarCameraButtonOptions, QuillToolbarCameraButtonExtraOptions> { const QuillToolbarCameraButtonOptions({ - required this.onVideoPickCallback, this.cameraConfigurations = const QuillToolbarCameraConfigurations(), - this.webVideoPickImpl, this.iconSize, this.fillColor, super.iconData, @@ -33,9 +30,5 @@ class QuillToolbarCameraButtonOptions extends QuillToolbarBaseButtonOptions< final Color? fillColor; - final OnVideoPickCallback onVideoPickCallback; - - final WebVideoPickImpl? webVideoPickImpl; - final QuillToolbarCameraConfigurations cameraConfigurations; } diff --git a/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/media_button.dart b/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/media_button.dart index a1c6fca9..d55f20f1 100644 --- a/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/media_button.dart +++ b/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/media_button.dart @@ -18,7 +18,7 @@ class QuillToolbarMediaButtonOptions extends QuillToolbarBaseButtonOptions< const QuillToolbarMediaButtonOptions({ required this.type, required this.onMediaPickedCallback, - required this.onVideoPickCallback, + // required this.onVideoPickCallback, this.dialogBarrierColor, this.mediaFilePicker, this.childrenSpacing = 16.0, @@ -33,8 +33,6 @@ class QuillToolbarMediaButtonOptions extends QuillToolbarBaseButtonOptions< this.galleryButtonText, this.linkButtonText, this.validationMessage, - this.filePickImpl, - this.webVideoPickImpl, super.iconData, super.afterButtonPressed, super.tooltip, @@ -74,7 +72,4 @@ class QuillToolbarMediaButtonOptions extends QuillToolbarBaseButtonOptions< final AutovalidateMode autovalidateMode; final String? validationMessage; - final FilePickImpl? filePickImpl; - final OnVideoPickCallback onVideoPickCallback; - final WebVideoPickImpl? webVideoPickImpl; } diff --git a/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/video.dart b/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/video.dart index 4e030192..17f80453 100644 --- a/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/video.dart +++ b/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/video.dart @@ -1,7 +1,6 @@ import 'package:flutter/widgets.dart' show Color; import 'package:flutter_quill/flutter_quill.dart'; -import '../../../../embeds/embed_types.dart'; import '../../../../embeds/embed_types/video.dart'; class QuillToolbarVideoButtonExtraOptions @@ -18,7 +17,6 @@ class QuillToolbarVideoButtonOptions extends QuillToolbarBaseButtonOptions< const QuillToolbarVideoButtonOptions({ this.linkRegExp, this.dialogTheme, - this.webVideoPickImpl, this.fillColor, this.iconSize, super.iconData, @@ -34,8 +32,6 @@ class QuillToolbarVideoButtonOptions extends QuillToolbarBaseButtonOptions< final QuillDialogTheme? dialogTheme; final QuillToolbarVideoConfigurations videoConfigurations; - final WebVideoPickImpl? webVideoPickImpl; - final Color? fillColor; final double? iconSize;