Browse Source

feat: customizing the background of a block with theme color (#3691)

Lucas.Xu 1 year ago
parent
commit
c6e3c1fd7c

+ 33 - 0
frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart

@@ -1,4 +1,5 @@
 import 'package:appflowy/plugins/document/application/doc_bloc.dart';
+import 'package:appflowy/plugins/document/presentation/editor_plugins/background_color/theme_background_color.dart';
 import 'package:appflowy/plugins/document/presentation/editor_plugins/image/custom_image_block_component.dart';
 import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
 import 'package:appflowy/plugins/document/presentation/editor_style.dart';
@@ -160,6 +161,9 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
       TableBlockKeys.type,
     ]);
     AppFlowyRichTextKeys.supportSliced.add(AppFlowyRichTextKeys.fontFamily);
+
+    // customize the dynamic theme color
+    _customizeBlockComponentBackgroundColorDecorator();
   }
 
   @override
@@ -541,4 +545,33 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
       ),
     );
   }
+
+  void _customizeBlockComponentBackgroundColorDecorator() {
+    blockComponentBackgroundColorDecorator = (Node node, String colorString) {
+      // the color string is from FlowyTint.
+      final tintColor = FlowyTint.values.firstWhereOrNull(
+        (e) => e.id == colorString,
+      );
+      if (tintColor != null) {
+        return tintColor.color(context);
+      }
+      final themeColor = themeBackgroundColors[colorString];
+      if (themeColor != null) {
+        return themeColor.color(context);
+      }
+
+      if (colorString == optionActionColorDefaultColor) {
+        final defaultColor = node.type == CalloutBlockKeys.type
+            ? AFThemeExtension.of(context).calloutBGColor
+            : Colors.transparent;
+        return defaultColor;
+      }
+
+      if (colorString == tableCellDefaultColor) {
+        return AFThemeExtension.of(context).tableCellBGColor;
+      }
+
+      return null;
+    };
+  }
 }

+ 8 - 5
frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option_action.dart

@@ -10,6 +10,8 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flutter/material.dart';
 import 'package:styled_widget/styled_widget.dart';
 
+const optionActionColorDefaultColor = 'appflowy_theme_default_color';
+
 enum OptionAction {
   delete,
   duplicate,
@@ -251,12 +253,14 @@ class ColorOptionAction extends PopoverActionCell {
           // reset to default background color
           FlowyColorOption(
             color: defaultColor,
-            name: LocaleKeys.document_plugins_optionAction_defaultColor.tr(),
+            i18n: LocaleKeys.document_plugins_optionAction_defaultColor.tr(),
+            id: optionActionColorDefaultColor,
           ),
           ...FlowyTint.values.map(
             (e) => FlowyColorOption(
               color: e.color(context),
-              name: e.tintName(AppFlowyEditorLocalizations.current),
+              i18n: e.tintName(AppFlowyEditorLocalizations.current),
+              id: e.id,
             ),
           ),
         ];
@@ -268,11 +272,10 @@ class ColorOptionAction extends PopoverActionCell {
             color: Theme.of(context).colorScheme.onBackground,
             width: 1,
           ),
-          onTap: (color, index) async {
+          onTap: (option, index) async {
             final transaction = editorState.transaction;
-            final backgroundColor = color.toHex();
             transaction.updateNode(node, {
-              blockComponentBackgroundColor: backgroundColor,
+              blockComponentBackgroundColor: option.id,
             });
             await editorState.apply(transaction);
 

+ 15 - 0
frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/background_color/theme_background_color.dart

@@ -0,0 +1,15 @@
+import 'package:flowy_infra/theme_extension.dart';
+
+// DON'T MODIFY THIS KEY BECAUSE IT'S SAVED IN THE DATABASE!
+// Used for the block component background color
+const themeBackgroundColors = {
+  'appflowy_them_color_tint1': FlowyTint.tint1,
+  'appflowy_them_color_tint2': FlowyTint.tint2,
+  'appflowy_them_color_tint3': FlowyTint.tint3,
+  'appflowy_them_color_tint4': FlowyTint.tint4,
+  'appflowy_them_color_tint5': FlowyTint.tint5,
+  'appflowy_them_color_tint6': FlowyTint.tint6,
+  'appflowy_them_color_tint7': FlowyTint.tint7,
+  'appflowy_them_color_tint8': FlowyTint.tint8,
+  'appflowy_them_color_tint9': FlowyTint.tint9,
+};

+ 9 - 7
frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/callout/callout_block_component.dart

@@ -88,8 +88,7 @@ class CalloutBlockComponentBuilder extends BlockComponentBuilder {
   bool validate(Node node) =>
       node.delta != null &&
       node.children.isEmpty &&
-      node.attributes[CalloutBlockKeys.icon] is String &&
-      node.attributes[CalloutBlockKeys.backgroundColor] is String;
+      node.attributes[CalloutBlockKeys.icon] is String;
 }
 
 // the main widget for rendering the callout block
@@ -117,7 +116,8 @@ class _CalloutBlockComponentWidgetState
         DefaultSelectableMixin,
         BlockComponentConfigurable,
         BlockComponentTextDirectionMixin,
-        BlockComponentAlignMixin {
+        BlockComponentAlignMixin,
+        BlockComponentBackgroundColorMixin {
   // the key used to forward focus to the richtext child
   @override
   final forwardKey = GlobalKey(debugLabel: 'flowy_rich_text');
@@ -137,11 +137,13 @@ class _CalloutBlockComponentWidgetState
   @override
   Node get node => widget.node;
 
-  // get the background color of the note block from the node's attributes
+  @override
   Color get backgroundColor {
-    final colorString =
-        node.attributes[CalloutBlockKeys.backgroundColor] as String;
-    return colorString.tryToColor() ?? Colors.transparent;
+    final color = super.backgroundColor;
+    if (color == Colors.transparent) {
+      return AFThemeExtension.of(context).calloutBGColor;
+    }
+    return color;
   }
 
   // get the emoji of the note block from the node's attributes or default to '📌'

+ 2 - 1
frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/code_block/code_block_component.dart

@@ -4,6 +4,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/base/strin
 import 'package:appflowy_editor/appflowy_editor.dart';
 import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:easy_localization/easy_localization.dart' hide TextDirection;
+import 'package:flowy_infra/theme_extension.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flutter/material.dart';
 import 'package:highlight/highlight.dart' as highlight;
@@ -190,7 +191,7 @@ class _CodeBlockComponentWidgetState extends State<CodeBlockComponentWidget>
     Widget child = Container(
       decoration: BoxDecoration(
         borderRadius: const BorderRadius.all(Radius.circular(8.0)),
-        color: Colors.grey.withOpacity(0.1),
+        color: AFThemeExtension.of(context).calloutBGColor,
       ),
       width: MediaQuery.of(context).size.width,
       child: Column(

+ 4 - 11
frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/outline/outline_block_component.dart

@@ -68,23 +68,16 @@ class OutlineBlockWidget extends BlockComponentStatefulWidget {
 }
 
 class _OutlineBlockWidgetState extends State<OutlineBlockWidget>
-    with BlockComponentConfigurable, BlockComponentTextDirectionMixin {
+    with
+        BlockComponentConfigurable,
+        BlockComponentTextDirectionMixin,
+        BlockComponentBackgroundColorMixin {
   @override
   BlockComponentConfiguration get configuration => widget.configuration;
 
   @override
   Node get node => widget.node;
 
-  // get the background color of the note block from the node's attributes
-  Color get backgroundColor {
-    final colorString =
-        node.attributes[OutlineBlockKeys.backgroundColor] as String?;
-    if (colorString == null) {
-      return Colors.transparent;
-    }
-    return colorString.tryToColor() ?? Colors.transparent;
-  }
-
   @override
   late EditorState editorState = context.read<EditorState>();
   late Stream<(TransactionTime, Transaction)> stream =

+ 9 - 4
frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/table/table_option_action.dart

@@ -11,6 +11,8 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flowy_infra_ui/style_widget/extension.dart';
 import 'package:flutter/material.dart';
 
+const tableCellDefaultColor = 'appflowy_table_cell_default_color';
+
 enum TableOptionAction {
   addAfter,
   addBefore,
@@ -119,12 +121,14 @@ class TableColorOptionAction extends PopoverActionCell {
           // reset to default background color
           FlowyColorOption(
             color: defaultColor,
-            name: LocaleKeys.document_plugins_optionAction_defaultColor.tr(),
+            i18n: LocaleKeys.document_plugins_optionAction_defaultColor.tr(),
+            id: tableCellDefaultColor,
           ),
           ...FlowyTint.values.map(
             (e) => FlowyColorOption(
               color: e.color(context),
-              name: e.tintName(AppFlowyEditorLocalizations.current),
+              i18n: e.tintName(AppFlowyEditorLocalizations.current),
+              id: e.id,
             ),
           ),
         ];
@@ -136,8 +140,9 @@ class TableColorOptionAction extends PopoverActionCell {
             color: Theme.of(context).colorScheme.onBackground,
             width: 1,
           ),
-          onTap: (color, index) async {
-            final backgroundColor = selectedColor != color ? color.toHex() : "";
+          onTap: (option, index) async {
+            final backgroundColor =
+                selectedColor != option.color ? option.id : '';
             TableActions.setBgColor(
               node,
               position,

+ 4 - 3
frontend/appflowy_flutter/lib/user/application/user_auth_listener.dart

@@ -1,14 +1,15 @@
 import 'dart:async';
+import 'dart:typed_data';
+
 import 'package:appflowy/core/notification/user_notification.dart';
 import 'package:appflowy_backend/log.dart';
-import 'package:appflowy_backend/protobuf/flowy-user/auth.pb.dart';
-import 'package:dartz/dartz.dart';
 import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
-import 'dart:typed_data';
 import 'package:appflowy_backend/protobuf/flowy-notification/protobuf.dart';
+import 'package:appflowy_backend/protobuf/flowy-user/auth.pb.dart';
 import 'package:appflowy_backend/protobuf/flowy-user/notification.pb.dart'
     as user;
 import 'package:appflowy_backend/rust_stream.dart';
+import 'package:dartz/dartz.dart';
 
 class UserAuthStateListener {
   void Function(String)? _onInvalidAuth;

+ 24 - 0
frontend/appflowy_flutter/packages/flowy_infra/lib/theme_extension.dart

@@ -204,4 +204,28 @@ enum FlowyTint {
         return AFThemeExtension.of(context).tint9;
     }
   }
+
+  String get id {
+    switch (this) {
+      // DON'T change this name because it's saved in the database!
+      case FlowyTint.tint1:
+        return 'appflowy_them_color_tint1';
+      case FlowyTint.tint2:
+        return 'appflowy_them_color_tint2';
+      case FlowyTint.tint3:
+        return 'appflowy_them_color_tint3';
+      case FlowyTint.tint4:
+        return 'appflowy_them_color_tint4';
+      case FlowyTint.tint5:
+        return 'appflowy_them_color_tint5';
+      case FlowyTint.tint6:
+        return 'appflowy_them_color_tint6';
+      case FlowyTint.tint7:
+        return 'appflowy_them_color_tint7';
+      case FlowyTint.tint8:
+        return 'appflowy_them_color_tint8';
+      case FlowyTint.tint9:
+        return 'appflowy_them_color_tint9';
+    }
+  }
 }

+ 7 - 5
frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/color_picker.dart

@@ -5,17 +5,19 @@ import 'package:flutter/material.dart';
 class FlowyColorOption {
   const FlowyColorOption({
     required this.color,
-    required this.name,
+    required this.i18n,
+    required this.id,
   });
 
   final Color color;
-  final String name;
+  final String i18n;
+  final String id;
 }
 
 class FlowyColorPicker extends StatelessWidget {
   final List<FlowyColorOption> colors;
   final Color? selected;
-  final Function(Color color, int index)? onTap;
+  final Function(FlowyColorOption option, int index)? onTap;
   final double separatorSize;
   final double iconSize;
   final double itemHeight;
@@ -70,11 +72,11 @@ class FlowyColorPicker extends StatelessWidget {
     return SizedBox(
       height: itemHeight,
       child: FlowyButton(
-        text: FlowyText.medium(option.name),
+        text: FlowyText.medium(option.i18n),
         leftIcon: colorIcon,
         rightIcon: checkmark,
         onTap: () {
-          onTap?.call(option.color, i);
+          onTap?.call(option, i);
         },
       ),
     );

+ 0 - 1
frontend/rust-lib/flowy-core/assets/read_me.json

@@ -193,7 +193,6 @@
     {
       "type": "callout",
       "data": {
-        "bgColor": "#F0F0F0",
         "delta": [
           { "insert": "\nLike AppFlowy? Follow us:\n" },
           {