Преглед изворни кода

fix: use empty event title and add placeholder (#3234)

* fix: use empty event title and add placeholder

* chore: update frontend/appflowy_flutter/lib/plugins/database_view/calendar/presentation/calendar_event_card.dart

Co-authored-by: Mathias Mogensen <[email protected]>

* chore: remove unnecessary visible for testing

* chore: apply suggestions from code review

Co-authored-by: Mathias Mogensen <[email protected]>

* fix: typo on suggested changes

---------

Co-authored-by: Mathias Mogensen <[email protected]>
Richard Shiue пре 1 година
родитељ
комит
72363921b0

+ 1 - 0
frontend/appflowy_flutter/integration_test/util/database_test_op.dart

@@ -4,6 +4,7 @@ import 'package:appflowy/generated/locale_keys.g.dart';
 import 'package:appflowy/plugins/database_view/board/presentation/board_page.dart';
 import 'package:appflowy/plugins/database_view/calendar/application/calendar_bloc.dart';
 import 'package:appflowy/plugins/database_view/calendar/presentation/calendar_day.dart';
+import 'package:appflowy/plugins/database_view/calendar/presentation/calendar_event_card.dart';
 import 'package:appflowy/plugins/database_view/calendar/presentation/calendar_page.dart';
 import 'package:appflowy/plugins/database_view/calendar/presentation/toolbar/calendar_layout_setting.dart';
 import 'package:appflowy/plugins/database_view/grid/presentation/grid_page.dart';

+ 2 - 0
frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_info.dart

@@ -19,6 +19,8 @@ class FieldInfo with _$FieldInfo {
 
   String get name => field.name;
 
+  bool get isPrimary => field.isPrimary;
+
   factory FieldInfo.initial(FieldPB field) => FieldInfo(
         field: field,
         hasFilter: false,

+ 5 - 20
frontend/appflowy_flutter/lib/plugins/database_view/calendar/application/calendar_bloc.dart

@@ -60,8 +60,8 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
               ),
             );
           },
-          createEvent: (DateTime date, String title) async {
-            await _createEvent(date, title);
+          createEvent: (DateTime date) async {
+            await _createEvent(date);
           },
           moveEvent: (CalendarDayEvent event, DateTime date) async {
             await _moveEvent(event, date);
@@ -120,18 +120,6 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
     }
   }
 
-  FieldInfo? _getTitleFieldInfo() {
-    final fieldInfos = databaseController.fieldController.fieldInfos;
-    final index = fieldInfos.indexWhere(
-      (element) => element.field.isPrimary,
-    );
-    if (index != -1) {
-      return fieldInfos[index];
-    } else {
-      return null;
-    }
-  }
-
   Future<void> _openDatabase(Emitter<CalendarState> emit) async {
     final result = await databaseController.open();
     result.fold(
@@ -147,19 +135,17 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
     );
   }
 
-  Future<void> _createEvent(DateTime date, String title) async {
+  Future<void> _createEvent(DateTime date) async {
     return state.settings.fold(
       () {
         Log.warn('Calendar settings not found');
       },
       (settings) async {
         final dateField = _getCalendarFieldInfo(settings.fieldId);
-        final titleField = _getTitleFieldInfo();
-        if (dateField != null && titleField != null) {
+        if (dateField != null) {
           final newRow = await databaseController.createRow(
             withCells: (builder) {
               builder.insertDate(dateField, date);
-              builder.insertText(titleField, title);
             },
           ).then(
             (result) => result.fold(
@@ -402,8 +388,7 @@ class CalendarEvent with _$CalendarEvent {
       _DidDeleteEvents;
 
   // Called when creating a new event
-  const factory CalendarEvent.createEvent(DateTime date, String title) =
-      _CreateEvent;
+  const factory CalendarEvent.createEvent(DateTime date) = _CreateEvent;
 
   // Called when moving an event
   const factory CalendarEvent.moveEvent(CalendarDayEvent event, DateTime date) =

+ 1 - 169
frontend/appflowy_flutter/lib/plugins/database_view/calendar/presentation/calendar_day.dart

@@ -1,25 +1,17 @@
 import 'package:appflowy/generated/flowy_svgs.g.dart';
 import 'package:appflowy/plugins/database_view/application/row/row_cache.dart';
-import 'package:appflowy/plugins/database_view/widgets/card/card.dart';
-import 'package:appflowy/plugins/database_view/widgets/card/card_cell_builder.dart';
-import 'package:appflowy/plugins/database_view/widgets/card/cells/card_cell.dart';
-import 'package:appflowy/plugins/database_view/widgets/card/cells/number_card_cell.dart';
-import 'package:appflowy/plugins/database_view/widgets/card/cells/url_card_cell.dart';
-import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
 import 'package:easy_localization/easy_localization.dart';
 
 import 'package:flowy_infra/size.dart';
 import 'package:flowy_infra/theme_extension.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
-import 'package:flowy_infra_ui/style_widget/hover.dart';
 import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
 import 'package:table_calendar/table_calendar.dart';
 
 import '../../grid/presentation/layout/sizes.dart';
-import '../../widgets/row/cells/select_option_cell/extension.dart';
 import '../application/calendar_bloc.dart';
-import 'calendar_page.dart';
+import 'calendar_event_card.dart';
 
 class CalendarDayCard extends StatelessWidget {
   final String viewId;
@@ -277,166 +269,6 @@ class _EventList extends StatelessWidget {
   }
 }
 
-@visibleForTesting
-class EventCard extends StatelessWidget {
-  final CalendarDayEvent event;
-  final String viewId;
-  final RowCache rowCache;
-  final BoxConstraints constraints;
-
-  const EventCard({
-    required this.event,
-    required this.viewId,
-    required this.rowCache,
-    required this.constraints,
-    Key? key,
-  }) : super(key: key);
-
-  @override
-  Widget build(BuildContext context) {
-    final rowInfo = rowCache.getRow(event.eventId);
-    final styles = <FieldType, CardCellStyle>{
-      FieldType.Number: NumberCardCellStyle(10),
-      FieldType.URL: URLCardCellStyle(10),
-    };
-    final cellBuilder = CardCellBuilder<String>(
-      rowCache.cellCache,
-      styles: styles,
-    );
-    final renderHook = _calendarEventCardRenderHook(context);
-
-    final card = RowCard<String>(
-      // Add the key here to make sure the card is rebuilt when the cells
-      // in this row are updated.
-      key: ValueKey(event.eventId),
-      rowMeta: rowInfo!.rowMeta,
-      viewId: viewId,
-      rowCache: rowCache,
-      cardData: event.dateFieldId,
-      isEditing: false,
-      cellBuilder: cellBuilder,
-      openCard: (context) => showEventDetails(
-        context: context,
-        event: event.event,
-        viewId: viewId,
-        rowCache: rowCache,
-      ),
-      styleConfiguration: RowCardStyleConfiguration(
-        showAccessory: false,
-        cellPadding: EdgeInsets.zero,
-        hoverStyle: HoverStyle(
-          hoverColor: AFThemeExtension.of(context).lightGreyHover,
-          foregroundColorOnHover: Theme.of(context).colorScheme.onBackground,
-        ),
-      ),
-      renderHook: renderHook,
-      onStartEditing: () {},
-      onEndEditing: () {},
-    );
-
-    final decoration = BoxDecoration(
-      border: Border.fromBorderSide(
-        BorderSide(color: Theme.of(context).dividerColor),
-      ),
-      borderRadius: Corners.s6Border,
-    );
-
-    return Draggable<CalendarDayEvent>(
-      data: event,
-      feedback: ConstrainedBox(
-        constraints: BoxConstraints(
-          maxWidth: constraints.maxWidth - 16.0,
-        ),
-        child: Container(
-          decoration: decoration.copyWith(
-            color: AFThemeExtension.of(context).lightGreyHover,
-          ),
-          child: card,
-        ),
-      ),
-      child: Container(
-        decoration: decoration,
-        child: card,
-      ),
-    );
-  }
-
-  RowCardRenderHook<String> _calendarEventCardRenderHook(BuildContext context) {
-    final renderHook = RowCardRenderHook<String>();
-    renderHook.addTextCellHook((cellData, primaryFieldId, _) {
-      if (cellData.isEmpty) {
-        return const SizedBox.shrink();
-      }
-      return Align(
-        alignment: Alignment.centerLeft,
-        child: FlowyText.medium(
-          cellData,
-          textAlign: TextAlign.left,
-          fontSize: 11,
-          maxLines: null, // Enable multiple lines
-        ),
-      );
-    });
-
-    renderHook.addDateCellHook((cellData, cardData, _) {
-      return Align(
-        alignment: Alignment.centerLeft,
-        child: Padding(
-          padding: const EdgeInsets.symmetric(vertical: 2),
-          child: Row(
-            mainAxisAlignment: MainAxisAlignment.spaceBetween,
-            children: [
-              Flexible(
-                flex: 3,
-                child: FlowyText.regular(
-                  cellData.date,
-                  fontSize: 10,
-                  color: Theme.of(context).hintColor,
-                  overflow: TextOverflow.ellipsis,
-                ),
-              ),
-              if (cellData.includeTime)
-                Flexible(
-                  child: FlowyText.regular(
-                    cellData.time,
-                    fontSize: 10,
-                    color: Theme.of(context).hintColor,
-                    overflow: TextOverflow.ellipsis,
-                  ),
-                )
-            ],
-          ),
-        ),
-      );
-    });
-
-    renderHook.addSelectOptionHook((selectedOptions, cardData, _) {
-      if (selectedOptions.isEmpty) {
-        return const SizedBox.shrink();
-      }
-      final children = selectedOptions.map(
-        (option) {
-          return SelectOptionTag.fromOption(
-            context: context,
-            option: option,
-          );
-        },
-      ).toList();
-
-      return IntrinsicHeight(
-        child: Padding(
-          padding: const EdgeInsets.symmetric(vertical: 2),
-          child: SizedBox.expand(
-            child: Wrap(spacing: 4, runSpacing: 4, children: children),
-          ),
-        ),
-      );
-    });
-
-    return renderHook;
-  }
-}
-
 class _CardEnterNotifier extends ChangeNotifier {
   bool _onEnter = false;
 

+ 195 - 0
frontend/appflowy_flutter/lib/plugins/database_view/calendar/presentation/calendar_event_card.dart

@@ -0,0 +1,195 @@
+import 'package:appflowy/generated/locale_keys.g.dart';
+import 'package:appflowy/plugins/database_view/application/row/row_cache.dart';
+import 'package:appflowy/plugins/database_view/widgets/card/bloc/text_card_cell_bloc.dart';
+import 'package:appflowy/plugins/database_view/widgets/card/card.dart';
+import 'package:appflowy/plugins/database_view/widgets/card/card_cell_builder.dart';
+import 'package:appflowy/plugins/database_view/widgets/card/cells/card_cell.dart';
+import 'package:appflowy/plugins/database_view/widgets/card/cells/number_card_cell.dart';
+import 'package:appflowy/plugins/database_view/widgets/card/cells/url_card_cell.dart';
+import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/extension.dart';
+import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flowy_infra/size.dart';
+import 'package:flowy_infra/theme_extension.dart';
+import 'package:flowy_infra_ui/flowy_infra_ui.dart';
+import 'package:flowy_infra_ui/style_widget/hover.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import '../application/calendar_bloc.dart';
+import 'calendar_page.dart';
+
+class EventCard extends StatelessWidget {
+  final CalendarDayEvent event;
+  final String viewId;
+  final RowCache rowCache;
+  final BoxConstraints constraints;
+
+  const EventCard({
+    required this.event,
+    required this.viewId,
+    required this.rowCache,
+    required this.constraints,
+    super.key,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    final rowInfo = rowCache.getRow(event.eventId);
+    final styles = <FieldType, CardCellStyle>{
+      FieldType.Number: NumberCardCellStyle(10),
+      FieldType.URL: URLCardCellStyle(10),
+    };
+    final cellBuilder = CardCellBuilder<CalendarDayEvent>(
+      rowCache.cellCache,
+      styles: styles,
+    );
+    final renderHook = _calendarEventCardRenderHook(context);
+
+    final card = RowCard<CalendarDayEvent>(
+      // Add the key here to make sure the card is rebuilt when the cells
+      // in this row are updated.
+      key: ValueKey(event.eventId),
+      rowMeta: rowInfo!.rowMeta,
+      viewId: viewId,
+      rowCache: rowCache,
+      cardData: event,
+      isEditing: false,
+      cellBuilder: cellBuilder,
+      openCard: (context) => showEventDetails(
+        context: context,
+        event: event.event,
+        viewId: viewId,
+        rowCache: rowCache,
+      ),
+      styleConfiguration: RowCardStyleConfiguration(
+        showAccessory: false,
+        cellPadding: EdgeInsets.zero,
+        hoverStyle: HoverStyle(
+          hoverColor: AFThemeExtension.of(context).lightGreyHover,
+          foregroundColorOnHover: Theme.of(context).colorScheme.onBackground,
+        ),
+      ),
+      renderHook: renderHook,
+      onStartEditing: () {},
+      onEndEditing: () {},
+    );
+
+    final decoration = BoxDecoration(
+      border: Border.fromBorderSide(
+        BorderSide(color: Theme.of(context).dividerColor),
+      ),
+      borderRadius: Corners.s6Border,
+    );
+
+    return Draggable<CalendarDayEvent>(
+      data: event,
+      feedback: ConstrainedBox(
+        constraints: BoxConstraints(
+          maxWidth: constraints.maxWidth - 16.0,
+        ),
+        child: DecoratedBox(
+          decoration: decoration.copyWith(
+            color: AFThemeExtension.of(context).lightGreyHover,
+          ),
+          child: card,
+        ),
+      ),
+      child: DecoratedBox(
+        decoration: decoration,
+        child: card,
+      ),
+    );
+  }
+
+  RowCardRenderHook<CalendarDayEvent> _calendarEventCardRenderHook(
+    BuildContext context,
+  ) {
+    final renderHook = RowCardRenderHook<CalendarDayEvent>();
+    renderHook.addTextCellHook((cellData, eventData, _) {
+      return BlocBuilder<TextCardCellBloc, TextCardCellState>(
+        builder: (context, state) {
+          final isTitle = context
+              .read<TextCardCellBloc>()
+              .cellController
+              .fieldInfo
+              .isPrimary;
+          final text = isTitle && cellData.isEmpty
+              ? LocaleKeys.grid_row_titlePlaceholder.tr()
+              : cellData;
+
+          if (text.isEmpty) {
+            return const SizedBox.shrink();
+          }
+
+          return Align(
+            alignment: Alignment.centerLeft,
+            child: FlowyText.medium(
+              text,
+              textAlign: TextAlign.left,
+              fontSize: 11,
+              maxLines: null, // Enable multiple lines
+            ),
+          );
+        },
+      );
+    });
+
+    renderHook.addDateCellHook((cellData, cardData, _) {
+      return Align(
+        alignment: Alignment.centerLeft,
+        child: Padding(
+          padding: const EdgeInsets.symmetric(vertical: 2),
+          child: Row(
+            mainAxisAlignment: MainAxisAlignment.spaceBetween,
+            children: [
+              Flexible(
+                flex: 3,
+                child: FlowyText.regular(
+                  cellData.date,
+                  fontSize: 10,
+                  color: Theme.of(context).hintColor,
+                  overflow: TextOverflow.ellipsis,
+                ),
+              ),
+              if (cellData.includeTime)
+                Flexible(
+                  child: FlowyText.regular(
+                    cellData.time,
+                    fontSize: 10,
+                    color: Theme.of(context).hintColor,
+                    overflow: TextOverflow.ellipsis,
+                  ),
+                )
+            ],
+          ),
+        ),
+      );
+    });
+
+    renderHook.addSelectOptionHook((selectedOptions, cardData, _) {
+      if (selectedOptions.isEmpty) {
+        return const SizedBox.shrink();
+      }
+      final children = selectedOptions.map(
+        (option) {
+          return SelectOptionTag.fromOption(
+            context: context,
+            option: option,
+          );
+        },
+      ).toList();
+
+      return IntrinsicHeight(
+        child: Padding(
+          padding: const EdgeInsets.symmetric(vertical: 2),
+          child: SizedBox.expand(
+            child: Wrap(spacing: 4, runSpacing: 4, children: children),
+          ),
+        ),
+      );
+    });
+
+    return renderHook;
+  }
+}

+ 1 - 4
frontend/appflowy_flutter/lib/plugins/database_view/calendar/presentation/calendar_page.dart

@@ -285,10 +285,7 @@ class _CalendarPageState extends State<CalendarPage> {
       rowCache: _calendarBloc.rowCache,
       onCreateEvent: (date) {
         _calendarBloc.add(
-          CalendarEvent.createEvent(
-            date,
-            LocaleKeys.calendar_defaultNewCalendarTitle.tr(),
-          ),
+          CalendarEvent.createEvent(date),
         );
       },
     );

+ 1 - 1
frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/row_detail.dart

@@ -91,7 +91,7 @@ class _RowDetailPageState extends State<RowDetailPage> {
 
               if (fieldInfo != null) {
                 final style = GridTextCellStyle(
-                  placeholder: LocaleKeys.grid_row_textPlaceholder.tr(),
+                  placeholder: LocaleKeys.grid_row_titlePlaceholder.tr(),
                   textStyle: Theme.of(context).textTheme.titleLarge,
                   showEmoji: false,
                   autofocus: true,

+ 1 - 0
frontend/resources/translations/en.json

@@ -424,6 +424,7 @@
     "row": {
       "duplicate": "Duplicate",
       "delete": "Delete",
+      "titlePlaceholder": "Untitled",
       "textPlaceholder": "Empty",
       "copyProperty": "Copied property to clipboard",
       "count": "Count",