From 385eab8a54d718c519817b57e4b209cd32f9cb1c Mon Sep 17 00:00:00 2001 From: Jon Salmon Date: Sun, 7 Jun 2020 02:58:34 +0100 Subject: [PATCH 1/6] Added converter for Quill syntax --- packages/notus/lib/convert.dart | 3 + packages/notus/lib/src/convert/quill.dart | 110 ++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 packages/notus/lib/src/convert/quill.dart diff --git a/packages/notus/lib/convert.dart b/packages/notus/lib/convert.dart index f2fd672d6..fd70759a4 100644 --- a/packages/notus/lib/convert.dart +++ b/packages/notus/lib/convert.dart @@ -6,8 +6,11 @@ library notus.convert; import 'src/convert/markdown.dart'; +import 'src/convert/quill.dart'; export 'src/convert/markdown.dart'; +export 'src/convert/quill.dart'; /// Markdown codec for Notus documents. const NotusMarkdownCodec notusMarkdown = NotusMarkdownCodec(); +const NotusQuillCodec notusQuill = NotusQuillCodec(); diff --git a/packages/notus/lib/src/convert/quill.dart b/packages/notus/lib/src/convert/quill.dart new file mode 100644 index 000000000..979941fa8 --- /dev/null +++ b/packages/notus/lib/src/convert/quill.dart @@ -0,0 +1,110 @@ +// Copyright (c) 2018, the Zefyr project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:convert'; + +import 'package:notus/notus.dart'; +import 'package:quill_delta/quill_delta.dart'; + +class NotusQuillCodec extends Codec { + const NotusQuillCodec(); + + @override + Converter get decoder => _NotusQuillDecoder(); + + @override + Converter get encoder => _NotusQuillEncoder(); +} + +class _NotusQuillEncoder extends Converter { + @override + Delta convert(Delta input) { + final result = Delta(); + + for (final op in input.toList()) { + if (!op.isInsert) continue; + + final attributes = {}; + op.attributes?.forEach((String key, dynamic value) { + switch (key) { + case 'b': + attributes['bold'] = value; + break; + case 'i': + attributes['italic'] = value; + break; + case 'heading': + attributes['header'] = value; + break; + case 'block': + if (value == NotusAttribute.block.bulletList.value) { + attributes['list'] = 'bullet'; + } else if (value == NotusAttribute.block.numberList.value) { + attributes['list'] = 'ordered'; + } else if (value == NotusAttribute.block.code.value) { + attributes['code-block'] = true; + } else if (value == NotusAttribute.block.quote.value) { + attributes['blockquote'] = true; + } else { + attributes[key] = value; + } + break; + default: + attributes[key] = value; + } + }); + result.insert(op.data, attributes.isEmpty ? null : attributes); + } + return result; + } +} + +class _NotusQuillDecoder extends Converter { + @override + Delta convert(Delta input) { + final result = Delta(); + + for (final op in input.toList()) { + if (!op.isInsert) continue; + + final attributes = {}; + op.attributes?.forEach((String key, dynamic value) { + switch (key) { + case 'bold': + attributes[NotusAttribute.bold.key] = value; + break; + case 'italic': + attributes[NotusAttribute.italic.key] = value; + break; + case 'header': + attributes[NotusAttribute.heading.key] = value; + break; + case 'list': + if (value == 'bullet') { + attributes[NotusAttribute.block.key] = NotusAttribute.block.bulletList.value; + } else if (value == 'ordered') { + attributes[NotusAttribute.block.key] = NotusAttribute.block.numberList.value; + } else { + attributes[key] = value; + } + break; + case 'code-block': + if (value == true) { + attributes[NotusAttribute.block.key] = NotusAttribute.block.code.value; + } + break; + case 'blockquote': + if (value == true) { + attributes[NotusAttribute.block.key] = NotusAttribute.block.quote.value; + } + break; + default: + attributes[key] = value; + } + }); + result.insert(op.data, attributes.isEmpty ? null : attributes); + } + return result; + } +} From 409145a7578453352bafff7af47758fda1574a48 Mon Sep 17 00:00:00 2001 From: Jon Salmon Date: Sun, 7 Jun 2020 13:48:11 +0100 Subject: [PATCH 2/6] Improved converter readability --- packages/notus/lib/src/convert/quill.dart | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/notus/lib/src/convert/quill.dart b/packages/notus/lib/src/convert/quill.dart index 979941fa8..6bebaebbe 100644 --- a/packages/notus/lib/src/convert/quill.dart +++ b/packages/notus/lib/src/convert/quill.dart @@ -38,13 +38,13 @@ class _NotusQuillEncoder extends Converter { attributes['header'] = value; break; case 'block': - if (value == NotusAttribute.block.bulletList.value) { + if (value == 'ul') { attributes['list'] = 'bullet'; - } else if (value == NotusAttribute.block.numberList.value) { + } else if (value == 'ol') { attributes['list'] = 'ordered'; - } else if (value == NotusAttribute.block.code.value) { + } else if (value == 'code') { attributes['code-block'] = true; - } else if (value == NotusAttribute.block.quote.value) { + } else if (value == 'quote') { attributes['blockquote'] = true; } else { attributes[key] = value; @@ -72,31 +72,31 @@ class _NotusQuillDecoder extends Converter { op.attributes?.forEach((String key, dynamic value) { switch (key) { case 'bold': - attributes[NotusAttribute.bold.key] = value; + attributes['b'] = value; break; case 'italic': - attributes[NotusAttribute.italic.key] = value; + attributes['i'] = value; break; case 'header': - attributes[NotusAttribute.heading.key] = value; + attributes['heading'] = value; break; case 'list': if (value == 'bullet') { - attributes[NotusAttribute.block.key] = NotusAttribute.block.bulletList.value; + attributes['block'] = 'ul'; } else if (value == 'ordered') { - attributes[NotusAttribute.block.key] = NotusAttribute.block.numberList.value; + attributes['block'] = 'ol'; } else { attributes[key] = value; } break; case 'code-block': if (value == true) { - attributes[NotusAttribute.block.key] = NotusAttribute.block.code.value; + attributes['block'] = 'code'; } break; case 'blockquote': if (value == true) { - attributes[NotusAttribute.block.key] = NotusAttribute.block.quote.value; + attributes['block'] = 'quote'; } break; default: From 68c8c0dbb7478f919cbb00c4d3795dcadad54e79 Mon Sep 17 00:00:00 2001 From: Jon Salmon Date: Sun, 7 Jun 2020 16:23:03 +0100 Subject: [PATCH 3/6] Added support for link attributes --- packages/notus/lib/src/convert/quill.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/notus/lib/src/convert/quill.dart b/packages/notus/lib/src/convert/quill.dart index 6bebaebbe..4f4fcf6a7 100644 --- a/packages/notus/lib/src/convert/quill.dart +++ b/packages/notus/lib/src/convert/quill.dart @@ -50,6 +50,9 @@ class _NotusQuillEncoder extends Converter { attributes[key] = value; } break; + case 'a': + attributes['link'] = value; + break; default: attributes[key] = value; } @@ -89,6 +92,9 @@ class _NotusQuillDecoder extends Converter { attributes[key] = value; } break; + case 'link': + attributes['a'] = value; + break; case 'code-block': if (value == true) { attributes['block'] = 'code'; From 9faf7fd42fc3126ce0f0eb03a09925ac1dc95c1d Mon Sep 17 00:00:00 2001 From: Jon Salmon <26483285+Jon-Salmon@users.noreply.github.com> Date: Mon, 8 Jun 2020 19:53:41 +0100 Subject: [PATCH 4/6] Removed unused import --- packages/notus/lib/src/convert/quill.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/notus/lib/src/convert/quill.dart b/packages/notus/lib/src/convert/quill.dart index 4f4fcf6a7..52459c5da 100644 --- a/packages/notus/lib/src/convert/quill.dart +++ b/packages/notus/lib/src/convert/quill.dart @@ -4,7 +4,6 @@ import 'dart:convert'; -import 'package:notus/notus.dart'; import 'package:quill_delta/quill_delta.dart'; class NotusQuillCodec extends Codec { From 6669df7d701722d880b62e696654a5f28d3ecc3e Mon Sep 17 00:00:00 2001 From: Jon Salmon Date: Mon, 8 Jun 2020 22:41:22 +0100 Subject: [PATCH 5/6] Added unit tests --- packages/notus/test/convert/quill_test.dart | 35 +++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 packages/notus/test/convert/quill_test.dart diff --git a/packages/notus/test/convert/quill_test.dart b/packages/notus/test/convert/quill_test.dart new file mode 100644 index 000000000..7b6da3efd --- /dev/null +++ b/packages/notus/test/convert/quill_test.dart @@ -0,0 +1,35 @@ +// Copyright (c) 2018, the Zefyr project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +import 'dart:convert'; + +import 'package:notus/convert.dart'; +import 'package:quill_delta/quill_delta.dart'; +import 'package:test/test.dart'; + +void main() { + group('$NotusQuillCodec.encode', () { + test('Should return correct string', () { + var result = notusQuill.encode(notus_doc); + + expect(jsonEncode(result.toJson()), quill_string); + }); + }); + + group('$NotusQuillCodec.decode', () { + test('Should return correct string', () { + var result = notusQuill.decode(quill_doc); + + expect(jsonEncode(result.toJson()), notus_string); + }); + }); + +} + +final notus_string = + r'[{"insert":"Zefyr"},{"insert":"\n","attributes":{"heading":1}},{"insert":"Soft and gentle rich text editing for Flutter applications.","attributes":{"i":true}},{"insert":"\nZefyr is an "},{"insert":"early preview","attributes":{"b":true}},{"insert":" open source library.\nDocumentation"},{"insert":"\n","attributes":{"heading":3}},{"insert":"Quick Start"},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"Data format and Document Model"},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"Style attributes"},{"insert":"\n","attributes":{"block":"ul"}},{"insert":"Heuristic rules"},{"insert":"\n","attributes":{"block":"ol"}},{"insert":"Clean and modern look"},{"insert":"\n","attributes":{"heading":2}},{"insert":"Zefyr’s rich text editor is built with simplicity and flexibility in mind. It provides clean interface for distraction-free editing. Think Medium.com-like experience.\nimport ‘package:flutter/material.dart’;"},{"insert":"\n","attributes":{"block":"code"}},{"insert":"import ‘package:notus/notus.dart’;"},{"insert":"\n\n","attributes":{"block":"code"}},{"insert":"void main() {"},{"insert":"\n","attributes":{"block":"code"}},{"insert":" print(“Hello world!”);"},{"insert":"\n","attributes":{"block":"code"}},{"insert":"}"},{"insert":"\n","attributes":{"block":"code"}}]'; +final notus_doc = Delta.fromJson(json.decode(notus_string) as List); + +final quill_string = + r'[{"insert":"Zefyr"},{"insert":"\n","attributes":{"header":1}},{"insert":"Soft and gentle rich text editing for Flutter applications.","attributes":{"italic":true}},{"insert":"\nZefyr is an "},{"insert":"early preview","attributes":{"bold":true}},{"insert":" open source library.\nDocumentation"},{"insert":"\n","attributes":{"header":3}},{"insert":"Quick Start"},{"insert":"\n","attributes":{"list":"bullet"}},{"insert":"Data format and Document Model"},{"insert":"\n","attributes":{"list":"bullet"}},{"insert":"Style attributes"},{"insert":"\n","attributes":{"list":"bullet"}},{"insert":"Heuristic rules"},{"insert":"\n","attributes":{"list":"ordered"}},{"insert":"Clean and modern look"},{"insert":"\n","attributes":{"header":2}},{"insert":"Zefyr’s rich text editor is built with simplicity and flexibility in mind. It provides clean interface for distraction-free editing. Think Medium.com-like experience.\nimport ‘package:flutter/material.dart’;"},{"insert":"\n","attributes":{"code-block":true}},{"insert":"import ‘package:notus/notus.dart’;"},{"insert":"\n\n","attributes":{"code-block":true}},{"insert":"void main() {"},{"insert":"\n","attributes":{"code-block":true}},{"insert":" print(“Hello world!”);"},{"insert":"\n","attributes":{"code-block":true}},{"insert":"}"},{"insert":"\n","attributes":{"code-block":true}}]'; +final quill_doc = Delta.fromJson(json.decode(notus_string) as List); \ No newline at end of file From a72d7dc9746ee6653e5dfa0829392d71220eea46 Mon Sep 17 00:00:00 2001 From: Jon Salmon Date: Tue, 9 Jun 2020 23:17:03 +0100 Subject: [PATCH 6/6] Fixed formatting issues --- packages/notus/test/convert/quill_test.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/notus/test/convert/quill_test.dart b/packages/notus/test/convert/quill_test.dart index 7b6da3efd..825a5fa24 100644 --- a/packages/notus/test/convert/quill_test.dart +++ b/packages/notus/test/convert/quill_test.dart @@ -23,7 +23,6 @@ void main() { expect(jsonEncode(result.toJson()), notus_string); }); }); - } final notus_string = @@ -32,4 +31,4 @@ final notus_doc = Delta.fromJson(json.decode(notus_string) as List); final quill_string = r'[{"insert":"Zefyr"},{"insert":"\n","attributes":{"header":1}},{"insert":"Soft and gentle rich text editing for Flutter applications.","attributes":{"italic":true}},{"insert":"\nZefyr is an "},{"insert":"early preview","attributes":{"bold":true}},{"insert":" open source library.\nDocumentation"},{"insert":"\n","attributes":{"header":3}},{"insert":"Quick Start"},{"insert":"\n","attributes":{"list":"bullet"}},{"insert":"Data format and Document Model"},{"insert":"\n","attributes":{"list":"bullet"}},{"insert":"Style attributes"},{"insert":"\n","attributes":{"list":"bullet"}},{"insert":"Heuristic rules"},{"insert":"\n","attributes":{"list":"ordered"}},{"insert":"Clean and modern look"},{"insert":"\n","attributes":{"header":2}},{"insert":"Zefyr’s rich text editor is built with simplicity and flexibility in mind. It provides clean interface for distraction-free editing. Think Medium.com-like experience.\nimport ‘package:flutter/material.dart’;"},{"insert":"\n","attributes":{"code-block":true}},{"insert":"import ‘package:notus/notus.dart’;"},{"insert":"\n\n","attributes":{"code-block":true}},{"insert":"void main() {"},{"insert":"\n","attributes":{"code-block":true}},{"insert":" print(“Hello world!”);"},{"insert":"\n","attributes":{"code-block":true}},{"insert":"}"},{"insert":"\n","attributes":{"code-block":true}}]'; -final quill_doc = Delta.fromJson(json.decode(notus_string) as List); \ No newline at end of file +final quill_doc = Delta.fromJson(json.decode(notus_string) as List);