chore: per-view field visibility UI (#3296)

* chore: default field settings if not exist

* chore: field settings listeners and services

* chore: don't need to updateFieldInfos

* feat: per-view field visibilty UI

* fix: remove unresolved imports
This commit is contained in:
Richard Shiue 2023-09-02 00:42:46 +08:00 committed by GitHub
parent 9b7ff375b2
commit f3aaff77b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 241 additions and 110 deletions

View File

@ -0,0 +1,55 @@
import 'package:appflowy/plugins/database_view/grid/presentation/grid_page.dart';
import 'package:appflowy/plugins/database_view/tar_bar/tar_bar_add_button.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pbenum.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'util/database_test_op.dart';
import 'util/util.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('database field settings', () {
testWidgets('field visibility', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
await tester.tapCreateLinkedDatabaseViewButton(AddButtonAction.grid);
// create a field
await tester.scrollToRight(find.byType(GridPage));
await tester.tapNewPropertyButton();
await tester.renameField('New field 1');
await tester.dismissFieldEditor();
// hide the field
await tester.tapGridFieldWithName('New field 1');
await tester.tapHidePropertyButton();
await tester.noFieldWithName('New field 1');
// go back to inline database view, expect field to be shown
await tester.tapTabBarLinkedViewByViewName('Untitled');
await tester.findFieldWithName('New field 1');
// go back to linked database view, expect field to be hidden
await tester.tapTabBarLinkedViewByViewName('grid');
await tester.noFieldWithName('New field 1');
// use the settings button to show the field
await tester.tapDatabaseSettingButton();
await tester.tapViewPropertiesButton();
await tester.tapViewTogglePropertyVisibilityButtonByName('New field 1');
await tester.dismissFieldEditor();
await tester.findFieldWithName('New field 1');
// open first row in popup then hide the field
await tester.openFirstRowDetailPage();
await tester.tapGridFieldWithNameInRowDetailPage('New field 1');
await tester.tapHidePropertyButtonInFieldEditor();
await tester.dismissRowDetailPage();
await tester.noFieldWithName('New field 1');
});
});
}

View File

@ -106,26 +106,6 @@ void main() {
await tester.pumpAndSettle();
});
testWidgets('hide field', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
// create a field
await tester.scrollToRight(find.byType(GridPage));
await tester.tapNewPropertyButton();
await tester.renameField('New field 1');
await tester.dismissFieldEditor();
// Delete the field
await tester.tapGridFieldWithName('New field 1');
await tester.tapHidePropertyButton();
await tester.noFieldWithName('New field 1');
await tester.pumpAndSettle();
});
testWidgets('create checklist field ', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();

View File

@ -4,6 +4,7 @@ import 'package:integration_test/integration_test.dart';
import 'database_calendar_test.dart' as database_calendar_test;
import 'database_cell_test.dart' as database_cell_test;
import 'database_field_test.dart' as database_field_test;
import 'database_field_settings_test.dart' as database_field_settings_test;
import 'database_filter_test.dart' as database_filter_test;
import 'database_row_page_test.dart' as database_row_page_test;
import 'database_row_test.dart' as database_row_test;
@ -47,6 +48,7 @@ void main() {
// Database integration tests
database_cell_test.main();
database_field_test.main();
database_field_settings_test.main();
database_share_test.main();
database_row_page_test.main();
database_row_test.main();

View File

@ -35,6 +35,7 @@ import 'package:appflowy/plugins/database_view/grid/presentation/widgets/toolbar
import 'package:appflowy/plugins/database_view/tar_bar/tab_bar_header.dart';
import 'package:appflowy/plugins/database_view/tar_bar/tar_bar_add_button.dart';
import 'package:appflowy/plugins/database_view/widgets/database_layout_ext.dart';
import 'package:appflowy/plugins/database_view/widgets/field/grid_property.dart';
import 'package:appflowy/plugins/database_view/widgets/row/accessory/cell_accessory.dart';
import 'package:appflowy/plugins/database_view/widgets/row/cells/cells.dart';
import 'package:appflowy/plugins/database_view/widgets/row/cells/checklist_cell/checklist_progress_bar.dart';
@ -503,6 +504,18 @@ extension AppFlowyDatabaseTest on WidgetTester {
await tapButton(findDateCell);
}
Future<void> tapGridFieldWithNameInRowDetailPage(String name) async {
final fields = find.byWidgetPredicate(
(widget) => widget is FieldCellButton && widget.field.name == name,
);
final field = find.descendant(
of: find.byType(RowDetailPage),
matching: fields,
);
await tapButton(field);
await pumpAndSettle();
}
Future<void> duplicateRowInRowDetailPage() async {
final duplicateButton = find.byType(RowDetailPageDuplicateButton);
await tapButton(duplicateButton);
@ -585,6 +598,11 @@ extension AppFlowyDatabaseTest on WidgetTester {
await tapButton(field);
}
Future<void> tapHidePropertyButtonInFieldEditor() async {
final button = find.byType(HideFieldButton);
await tapButton(button);
}
Future<void> tapRowDetailPageCreatePropertyButton() async {
await tapButton(find.byType(CreateRowFieldButton));
}
@ -925,6 +943,23 @@ extension AppFlowyDatabaseTest on WidgetTester {
await tapButton(button);
}
/// Should call [tapDatabaseSettingButton] first.
Future<void> tapViewPropertiesButton() async {
final findSettingItem = find.byType(DatabaseSettingItem);
final findLayoutButton = find.byWidgetPredicate(
(widget) =>
widget is FlowyText &&
widget.text == DatabaseSettingAction.showProperties.title(),
);
final button = find.descendant(
of: findSettingItem,
matching: findLayoutButton,
);
await tapButton(button);
}
/// Should call [tapDatabaseSettingButton] first.
Future<void> tapDatabaseLayoutButton() async {
final findSettingItem = find.byType(DatabaseSettingItem);
@ -1111,6 +1146,11 @@ extension AppFlowyDatabaseTest on WidgetTester {
await tapButton(findCreateButton);
}
Future<void> tapTabBarLinkedViewByViewName(String name) async {
final viewButton = findTabBarLinkViewByViewName(name);
await tapButton(viewButton);
}
Finder findTabBarLinkViewByViewLayout(ViewLayoutPB layout) {
return find.byWidgetPredicate(
(widget) => widget is TabBarItemButton && widget.view.layout == layout,
@ -1212,6 +1252,18 @@ extension AppFlowyDatabaseTest on WidgetTester {
Future<void> tapAddSelectOptionButton() async {
await tapButtonWithName(LocaleKeys.grid_field_addSelectOption.tr());
}
Future<void> tapViewTogglePropertyVisibilityButtonByName(
String fieldName,
) async {
final field = find.byWidgetPredicate(
(widget) =>
widget is GridPropertyCell && widget.fieldInfo.name == fieldName,
);
final toggleVisibilityButton =
find.descendant(of: field, matching: find.byType(FlowyIconButton));
await tapButton(toggleVisibilityButton);
}
}
Finder finderForDatabaseLayoutType(DatabaseLayoutPB layout) {

View File

@ -1,5 +1,7 @@
import 'package:appflowy/plugins/database_view/application/field_settings/field_settings_service.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_settings_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'field_service.dart';
@ -8,13 +10,18 @@ part 'field_action_sheet_bloc.freezed.dart';
class FieldActionSheetBloc
extends Bloc<FieldActionSheetEvent, FieldActionSheetState> {
final String fieldId;
final FieldBackendService fieldService;
final FieldSettingsBackendService fieldSettingsService;
FieldActionSheetBloc({required FieldContext fieldCellContext})
: fieldService = FieldBackendService(
: fieldId = fieldCellContext.field.id,
fieldService = FieldBackendService(
viewId: fieldCellContext.viewId,
fieldId: fieldCellContext.field.id,
),
fieldSettingsService =
FieldSettingsBackendService(viewId: fieldCellContext.viewId),
super(
FieldActionSheetState.initial(
TypeOptionPB.create()..field_2 = fieldCellContext.field,
@ -31,14 +38,20 @@ class FieldActionSheetBloc
);
},
hideField: (_HideField value) async {
final result = await fieldService.updateField(visibility: false);
final result = await fieldSettingsService.updateFieldSettings(
fieldId: fieldId,
fieldVisibility: FieldVisibility.AlwaysHidden,
);
result.fold(
(l) => null,
(err) => Log.error(err),
);
},
showField: (_ShowField value) async {
final result = await fieldService.updateField(visibility: true);
final result = await fieldSettingsService.updateFieldSettings(
fieldId: fieldId,
fieldVisibility: FieldVisibility.AlwaysShown,
);
result.fold(
(l) => null,
(err) => Log.error(err),

View File

@ -417,7 +417,7 @@ class FieldController {
_fieldNotifier.fieldInfos = newFieldInfos;
}
List<FieldInfo> updateFields(List<FieldPB> updatedFieldPBs) {
Future<List<FieldInfo>> updateFields(List<FieldPB> updatedFieldPBs) async {
if (updatedFieldPBs.isEmpty) {
return [];
}
@ -429,7 +429,8 @@ class FieldController {
newFields.indexWhere((field) => field.id == updatedFieldPB.id);
if (index != -1) {
newFields.removeAt(index);
final fieldInfo = FieldInfo.initial(updatedFieldPB);
final initial = FieldInfo.initial(updatedFieldPB);
final fieldInfo = await attachFieldSettings(initial);
newFields.insert(index, fieldInfo);
updatedFields.add(fieldInfo);
}
@ -443,16 +444,16 @@ class FieldController {
// Listen on field's changes
_fieldListener.start(
onFieldsChanged: (result) {
onFieldsChanged: (result) async {
result.fold(
(changeset) {
(changeset) async {
if (_isDisposed) {
return;
}
deleteFields(changeset.deletedFields);
insertFields(changeset.insertedFields);
final updatedFields = updateFields(changeset.updatedFields);
final updatedFields = await updateFields(changeset.updatedFields);
for (final listener in _updatedFieldCallbacks.values) {
listener(updatedFields);
}
@ -548,7 +549,7 @@ class FieldController {
_fieldNotifier.fieldInfos =
newFields.map((field) => FieldInfo.initial(field)).toList();
Future.wait([
await Future.wait([
_loadFilters(),
_loadSorts(),
_loadAllFieldSettings(),

View File

@ -30,7 +30,6 @@ class FieldBackendService {
Future<Either<Unit, FlowyError>> updateField({
String? name,
bool? frozen,
bool? visibility,
double? width,
}) {
final payload = FieldChangesetPB.create()
@ -45,10 +44,6 @@ class FieldBackendService {
payload.frozen = frozen;
}
if (visibility != null) {
payload.visibility = visibility;
}
if (width != null) {
payload.width = width.toInt();
}

View File

@ -246,7 +246,8 @@ class RowCache {
// ignore: prefer_collection_literals
final cellContextMap = CellContextByFieldId();
for (final fieldInfo in _fieldDelegate.fieldInfos) {
if (fieldInfo.field.visibility) {
if (fieldInfo.visibility != null &&
fieldInfo.visibility != FieldVisibility.AlwaysHidden) {
cellContextMap[fieldInfo.id] = DatabaseCellContext(
rowMeta: rowMeta,
viewId: viewId,

View File

@ -1,6 +1,8 @@
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
import 'package:appflowy/plugins/database_view/application/field_settings/field_settings_service.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_settings_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
@ -28,13 +30,13 @@ class DatabasePropertyBloc
_startListening();
},
setFieldVisibility: (_SetFieldVisibility value) async {
final fieldBackendSvc = FieldBackendService(
final fieldSettingsSvc = FieldSettingsBackendService(
viewId: viewId,
fieldId: value.fieldId,
);
final result = await fieldBackendSvc.updateField(
visibility: value.visibility,
final result = await fieldSettingsSvc.updateFieldSettings(
fieldId: value.fieldId,
fieldVisibility: value.visibility,
);
result.fold((l) => null, (err) => Log.error(err));
@ -84,7 +86,7 @@ class DatabasePropertyEvent with _$DatabasePropertyEvent {
const factory DatabasePropertyEvent.initial() = _Initial;
const factory DatabasePropertyEvent.setFieldVisibility(
String fieldId,
bool visibility,
FieldVisibility visibility,
) = _SetFieldVisibility;
const factory DatabasePropertyEvent.didReceiveFieldUpdate(
List<FieldInfo> fields,

View File

@ -2,6 +2,7 @@ import 'package:appflowy/plugins/database_view/application/field/field_controlle
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_settings_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
@ -16,18 +17,25 @@ class GridHeaderBloc extends Bloc<GridHeaderEvent, GridHeaderState> {
GridHeaderBloc({
required this.viewId,
required this.fieldController,
}) : super(GridHeaderState.initial(fieldController.fieldInfos)) {
}) : super(GridHeaderState.initial()) {
on<GridHeaderEvent>(
(event, emit) async {
await event.map(
initial: (_InitialHeader value) async {
_startListening();
add(
GridHeaderEvent.didReceiveFieldUpdate(fieldController.fieldInfos),
);
},
didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) {
emit(
state.copyWith(
fields: value.fields
.where((element) => element.field.visibility)
.where(
(element) =>
element.visibility != null &&
element.visibility != FieldVisibility.AlwaysHidden,
)
.toList(),
),
);
@ -83,9 +91,5 @@ class GridHeaderState with _$GridHeaderState {
const factory GridHeaderState({required List<FieldInfo> fields}) =
_GridHeaderState;
factory GridHeaderState.initial(List<FieldInfo> fields) {
// final List<FieldPB> newFields = List.from(fields);
// newFields.retainWhere((field) => field.visibility);
return GridHeaderState(fields: fields);
}
factory GridHeaderState.initial() => const GridHeaderState(fields: []);
}

View File

@ -1,5 +1,7 @@
import 'package:appflowy/plugins/database_view/application/field_settings/field_settings_service.dart';
import 'package:appflowy/plugins/database_view/application/row/row_service.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_settings_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
@ -10,18 +12,18 @@ part 'row_detail_bloc.freezed.dart';
class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
final RowBackendService rowService;
final RowController dataController;
final RowController rowController;
RowDetailBloc({
required this.dataController,
}) : rowService = RowBackendService(viewId: dataController.viewId),
required this.rowController,
}) : rowService = RowBackendService(viewId: rowController.viewId),
super(RowDetailState.initial()) {
on<RowDetailEvent>(
(event, emit) async {
await event.when(
initial: () async {
await _startListening();
final cells = dataController.loadData();
final cells = rowController.loadData();
if (!isClosed) {
add(RowDetailEvent.didReceiveCellDatas(cells.values.toList()));
}
@ -32,9 +34,24 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
deleteField: (fieldId) {
_fieldBackendService(fieldId).deleteField();
},
showField: (fieldId) async {
final result =
await FieldSettingsBackendService(viewId: rowController.viewId)
.updateFieldSettings(
fieldId: fieldId,
fieldVisibility: FieldVisibility.AlwaysShown,
);
result.fold(
(l) {},
(err) => Log.error(err),
);
},
hideField: (fieldId) async {
final result = await _fieldBackendService(fieldId).updateField(
visibility: false,
final result =
await FieldSettingsBackendService(viewId: rowController.viewId)
.updateFieldSettings(
fieldId: fieldId,
fieldVisibility: FieldVisibility.AlwaysHidden,
);
result.fold(
(l) {},
@ -57,12 +74,12 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
@override
Future<void> close() async {
dataController.dispose();
rowController.dispose();
return super.close();
}
Future<void> _startListening() async {
dataController.addListener(
rowController.addListener(
onRowChanged: (cells, reason) {
if (!isClosed) {
add(RowDetailEvent.didReceiveCellDatas(cells.values.toList()));
@ -73,7 +90,7 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
FieldBackendService _fieldBackendService(String fieldId) {
return FieldBackendService(
viewId: dataController.viewId,
viewId: rowController.viewId,
fieldId: fieldId,
);
}
@ -83,6 +100,7 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
class RowDetailEvent with _$RowDetailEvent {
const factory RowDetailEvent.initial() = _Initial;
const factory RowDetailEvent.deleteField(String fieldId) = _DeleteField;
const factory RowDetailEvent.showField(String fieldId) = _ShowField;
const factory RowDetailEvent.hideField(String fieldId) = _HideField;
const factory RowDetailEvent.deleteRow(String rowId) = _DeleteRow;
const factory RowDetailEvent.duplicateRow(String rowId, String? groupId) =

View File

@ -164,11 +164,6 @@ class FieldCellButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Using this technique to have proper text ellipsis
// https://github.com/flutter/flutter/issues/18761#issuecomment-812390920
final text = Characters(field.name)
.replaceAll(Characters(''), Characters('\u{200B}'))
.toString();
return FlowyButton(
hoverColor: AFThemeExtension.of(context).greyHover,
onTap: onTap,
@ -177,7 +172,7 @@ class FieldCellButton extends StatelessWidget {
),
radius: radius,
text: FlowyText.medium(
text,
field.name,
maxLines: maxLines,
overflow: TextOverflow.ellipsis,
),

View File

@ -102,7 +102,7 @@ class _FieldEditorState extends State<FieldEditor> {
builder: (context, state) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: _HideFieldButton(
child: HideFieldButton(
popoverMutex: popoverMutex,
onHidden: () {
state.field.fold(
@ -236,11 +236,12 @@ class _DeleteFieldButton extends StatelessWidget {
}
}
class _HideFieldButton extends StatelessWidget {
@visibleForTesting
class HideFieldButton extends StatelessWidget {
final PopoverMutex popoverMutex;
final VoidCallback? onHidden;
const _HideFieldButton({
const HideFieldButton({
required this.popoverMutex,
required this.onHidden,
Key? key,

View File

@ -42,12 +42,10 @@ class _GridHeaderSliverAdaptorState extends State<GridHeaderSliverAdaptor> {
Widget build(BuildContext context) {
return BlocProvider(
create: (context) {
final bloc = getIt<GridHeaderBloc>(
return getIt<GridHeaderBloc>(
param1: widget.viewId,
param2: widget.fieldController,
);
bloc.add(const GridHeaderEvent.initial());
return bloc;
)..add(const GridHeaderEvent.initial());
},
child: BlocBuilder<GridHeaderBloc, GridHeaderState>(
buildWhen: (previous, current) =>
@ -97,7 +95,6 @@ class _GridHeaderState extends State<_GridHeader> {
buildWhen: (previous, current) => previous.fields != current.fields,
builder: (context, state) {
final cells = state.fields
.where((fieldInfo) => fieldInfo.field.visibility)
.map(
(field) => FieldContext(
viewId: widget.viewId,

View File

@ -5,6 +5,7 @@ import 'package:appflowy/plugins/database_view/application/field/type_option/typ
import 'package:appflowy/plugins/database_view/application/setting/property_bloc.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_type_extension.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:flowy_infra/theme_extension.dart';
@ -44,7 +45,7 @@ class _DatabasePropertyListState extends State<DatabasePropertyList> {
child: BlocBuilder<DatabasePropertyBloc, DatabasePropertyState>(
builder: (context, state) {
final cells = state.fieldContexts.map((field) {
return _GridPropertyCell(
return GridPropertyCell(
key: ValueKey(field.id),
viewId: widget.viewId,
fieldInfo: field,
@ -75,12 +76,13 @@ class _DatabasePropertyListState extends State<DatabasePropertyList> {
}
}
class _GridPropertyCell extends StatefulWidget {
@visibleForTesting
class GridPropertyCell extends StatefulWidget {
final FieldInfo fieldInfo;
final String viewId;
final PopoverMutex popoverMutex;
const _GridPropertyCell({
const GridPropertyCell({
super.key,
required this.fieldInfo,
required this.viewId,
@ -88,26 +90,22 @@ class _GridPropertyCell extends StatefulWidget {
});
@override
State<_GridPropertyCell> createState() => _GridPropertyCellState();
State<GridPropertyCell> createState() => _GridPropertyCellState();
}
class _GridPropertyCellState extends State<_GridPropertyCell> {
class _GridPropertyCellState extends State<GridPropertyCell> {
final PopoverController _popoverController = PopoverController();
@override
Widget build(BuildContext context) {
final checkmark = FlowySvg(
widget.fieldInfo.field.visibility ? FlowySvgs.show_m : FlowySvgs.hide_m,
final visiblity = widget.fieldInfo.visibility;
final visibleIcon = FlowySvg(
visiblity != null && visiblity != FieldVisibility.AlwaysHidden
? FlowySvgs.show_m
: FlowySvgs.hide_m,
color: Theme.of(context).iconTheme.color,
);
return SizedBox(
height: GridSize.popoverItemHeight,
child: _editFieldButton(context, checkmark),
);
}
Widget _editFieldButton(BuildContext context, Widget checkmark) {
return AppFlowyPopover(
mutex: widget.popoverMutex,
controller: _popoverController,
@ -116,30 +114,40 @@ class _GridPropertyCellState extends State<_GridPropertyCell> {
constraints: BoxConstraints.loose(const Size(240, 400)),
triggerActions: PopoverTriggerFlags.none,
margin: EdgeInsets.zero,
child: FlowyButton(
hoverColor: AFThemeExtension.of(context).lightGreyHover,
text: FlowyText.medium(
widget.fieldInfo.name,
color: AFThemeExtension.of(context).textColor,
),
leftIcon: FlowySvg(
widget.fieldInfo.fieldType.icon(),
color: Theme.of(context).iconTheme.color,
),
rightIcon: FlowyIconButton(
hoverColor: Colors.transparent,
onPressed: () {
context.read<DatabasePropertyBloc>().add(
DatabasePropertyEvent.setFieldVisibility(
widget.fieldInfo.id,
!widget.fieldInfo.field.visibility,
),
);
},
icon: checkmark.padding(all: 6.0),
),
onTap: () => _popoverController.show(),
).padding(horizontal: 6.0),
child: SizedBox(
height: GridSize.popoverItemHeight,
child: FlowyButton(
hoverColor: AFThemeExtension.of(context).lightGreyHover,
text: FlowyText.medium(
widget.fieldInfo.name,
color: AFThemeExtension.of(context).textColor,
),
leftIcon: FlowySvg(
widget.fieldInfo.fieldType.icon(),
color: Theme.of(context).iconTheme.color,
),
rightIcon: FlowyIconButton(
hoverColor: Colors.transparent,
onPressed: () {
if (widget.fieldInfo.fieldSettings == null) {
return;
}
final newVisiblity = _newFieldVisibility(
widget.fieldInfo.fieldSettings!.visibility,
);
context.read<DatabasePropertyBloc>().add(
DatabasePropertyEvent.setFieldVisibility(
widget.fieldInfo.id,
newVisiblity,
),
);
},
icon: visibleIcon.padding(all: 6.0),
),
onTap: () => _popoverController.show(),
).padding(horizontal: 6.0),
),
popupBuilder: (BuildContext context) {
return FieldEditor(
viewId: widget.viewId,
@ -151,4 +159,12 @@ class _GridPropertyCellState extends State<_GridPropertyCell> {
},
);
}
FieldVisibility _newFieldVisibility(FieldVisibility current) {
return switch (current) {
FieldVisibility.AlwaysShown => FieldVisibility.AlwaysHidden,
FieldVisibility.AlwaysHidden => FieldVisibility.AlwaysShown,
_ => FieldVisibility.AlwaysHidden,
};
}
}

View File

@ -48,7 +48,7 @@ class _RowDetailPageState extends State<RowDetailPage> {
return FlowyDialog(
child: BlocProvider(
create: (context) {
return RowDetailBloc(dataController: widget.rowController)
return RowDetailBloc(rowController: widget.rowController)
..add(const RowDetailEvent.initial());
},
child: ListView(

View File

@ -17,7 +17,6 @@ use crate::database::mock_data::{COMPLETED, FACEBOOK, GOOGLE, PAUSED, PLANNED, T
pub fn make_test_board() -> DatabaseData {
let mut fields = vec![];
let mut rows = vec![];
// Iterate through the FieldType to create the corresponding Field.
for field_type in FieldType::iter() {
match field_type {