mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
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:
parent
9b7ff375b2
commit
f3aaff77b9
@ -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');
|
||||
});
|
||||
});
|
||||
}
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
@ -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),
|
||||
|
@ -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(),
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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: []);
|
||||
}
|
||||
|
@ -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) =
|
||||
|
@ -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,
|
||||
),
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user