feat: allow fields to not wrap cell content (#5128)

This commit is contained in:
Richard Shiue 2024-04-13 16:48:28 +08:00 committed by GitHub
parent 891fd16a0c
commit 8947a89a24
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
54 changed files with 660 additions and 475 deletions

View File

@ -77,7 +77,7 @@ class _MobileDateCellEditScreenState extends State<MobileDateCellEditScreen> {
create: (_) => DateCellEditorBloc( create: (_) => DateCellEditorBloc(
reminderBloc: getIt<ReminderBloc>(), reminderBloc: getIt<ReminderBloc>(),
cellController: widget.controller, cellController: widget.controller,
)..add(const DateCellEditorEvent.initial()), ),
), ),
], ],
child: BlocBuilder<DateCellEditorBloc, DateCellEditorState>( child: BlocBuilder<DateCellEditorBloc, DateCellEditorState>(

View File

@ -63,7 +63,7 @@ class _MobileEditPropertyScreenState extends State<MobileEditPropertyScreen> {
isPrimary: widget.field.isPrimary, isPrimary: widget.field.isPrimary,
defaultValues: FieldOptionValues.fromField(field: widget.field.field), defaultValues: FieldOptionValues.fromField(field: widget.field.field),
actions: [ actions: [
widget.field.fieldSettings?.visibility.isVisibleState() ?? true widget.field.visibility?.isVisibleState() ?? true
? FieldOptionAction.hide ? FieldOptionAction.hide
: FieldOptionAction.show, : FieldOptionAction.show,
FieldOptionAction.duplicate, FieldOptionAction.duplicate,

View File

@ -44,8 +44,8 @@ class _QuickEditFieldState extends State<QuickEditField> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
fieldVisibility = widget.fieldInfo.fieldSettings?.visibility ?? fieldVisibility =
FieldVisibility.AlwaysShown; widget.fieldInfo.visibility ?? FieldVisibility.AlwaysShown;
controller.text = widget.fieldInfo.field.name; controller.text = widget.fieldInfo.field.name;
} }

View File

@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart'; import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart';
import 'package:appflowy/plugins/database/application/field/field_info.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
@ -20,8 +21,10 @@ class CheckboxCellBloc extends Bloc<CheckboxCellEvent, CheckboxCellState> {
@override @override
Future<void> close() async { Future<void> close() async {
if (_onCellChangedFn != null) { if (_onCellChangedFn != null) {
cellController.removeListener(_onCellChangedFn!); cellController.removeListener(
_onCellChangedFn = null; onCellChanged: _onCellChangedFn!,
onFieldChanged: _onFieldChangedListener,
);
} }
await cellController.dispose(); await cellController.dispose();
return super.close(); return super.close();
@ -53,13 +56,15 @@ class CheckboxCellBloc extends Bloc<CheckboxCellEvent, CheckboxCellState> {
add(CheckboxCellEvent.didUpdateCell(_isSelected(cellData))); add(CheckboxCellEvent.didUpdateCell(_isSelected(cellData)));
} }
}, },
onCellFieldChanged: (field) { onFieldChanged: _onFieldChangedListener,
if (!isClosed) {
add(CheckboxCellEvent.didUpdateField(field.name));
}
},
); );
} }
void _onFieldChangedListener(FieldInfo fieldInfo) {
if (!isClosed) {
add(CheckboxCellEvent.didUpdateField(fieldInfo.name));
}
}
} }
@freezed @freezed

View File

@ -36,8 +36,7 @@ class ChecklistCellBloc extends Bloc<ChecklistCellEvent, ChecklistCellState> {
@override @override
Future<void> close() async { Future<void> close() async {
if (_onCellChangedFn != null) { if (_onCellChangedFn != null) {
cellController.removeListener(_onCellChangedFn!); cellController.removeListener(onCellChanged: _onCellChangedFn!);
_onCellChangedFn = null;
} }
await cellController.dispose(); await cellController.dispose();
return super.close(); return super.close();
@ -47,7 +46,7 @@ class ChecklistCellBloc extends Bloc<ChecklistCellEvent, ChecklistCellState> {
on<ChecklistCellEvent>( on<ChecklistCellEvent>(
(event, emit) async { (event, emit) async {
await event.when( await event.when(
didReceiveOptions: (data) { didUpdateCell: (data) {
if (data == null) { if (data == null) {
emit( emit(
const ChecklistCellState( const ChecklistCellState(
@ -58,7 +57,6 @@ class ChecklistCellBloc extends Bloc<ChecklistCellEvent, ChecklistCellState> {
); );
return; return;
} }
emit( emit(
state.copyWith( state.copyWith(
tasks: _makeChecklistSelectOptions(data), tasks: _makeChecklistSelectOptions(data),
@ -91,7 +89,7 @@ class ChecklistCellBloc extends Bloc<ChecklistCellEvent, ChecklistCellState> {
_onCellChangedFn = cellController.addListener( _onCellChangedFn = cellController.addListener(
onCellChanged: (data) { onCellChanged: (data) {
if (!isClosed) { if (!isClosed) {
add(ChecklistCellEvent.didReceiveOptions(data)); add(ChecklistCellEvent.didUpdateCell(data));
} }
}, },
); );
@ -111,9 +109,9 @@ class ChecklistCellBloc extends Bloc<ChecklistCellEvent, ChecklistCellState> {
@freezed @freezed
class ChecklistCellEvent with _$ChecklistCellEvent { class ChecklistCellEvent with _$ChecklistCellEvent {
const factory ChecklistCellEvent.didReceiveOptions( const factory ChecklistCellEvent.didUpdateCell(
ChecklistCellDataPB? data, ChecklistCellDataPB? data,
) = _DidReceiveCellUpdate; ) = _DidUpdateCell;
const factory ChecklistCellEvent.updateTaskName( const factory ChecklistCellEvent.updateTaskName(
SelectOptionPB option, SelectOptionPB option,
String name, String name,

View File

@ -12,6 +12,7 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
DateCellBloc({required this.cellController}) DateCellBloc({required this.cellController})
: super(DateCellState.initial(cellController)) { : super(DateCellState.initial(cellController)) {
_dispatch(); _dispatch();
_startListening();
} }
final DateCellController cellController; final DateCellController cellController;
@ -20,8 +21,10 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
@override @override
Future<void> close() async { Future<void> close() async {
if (_onCellChangedFn != null) { if (_onCellChangedFn != null) {
cellController.removeListener(_onCellChangedFn!); cellController.removeListener(
_onCellChangedFn = null; onCellChanged: _onCellChangedFn!,
onFieldChanged: _onFieldChangedListener,
);
} }
await cellController.dispose(); await cellController.dispose();
return super.close(); return super.close();
@ -31,7 +34,6 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
on<DateCellEvent>( on<DateCellEvent>(
(event, emit) async { (event, emit) async {
event.when( event.when(
initial: () => _startListening(),
didReceiveCellUpdate: (DateCellDataPB? cellData) { didReceiveCellUpdate: (DateCellDataPB? cellData) {
emit( emit(
state.copyWith( state.copyWith(
@ -40,6 +42,9 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
), ),
); );
}, },
didUpdateField: (fieldInfo) {
emit(state.copyWith(fieldInfo: fieldInfo));
},
); );
}, },
); );
@ -52,15 +57,23 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
add(DateCellEvent.didReceiveCellUpdate(data)); add(DateCellEvent.didReceiveCellUpdate(data));
} }
}, },
onFieldChanged: _onFieldChangedListener,
); );
} }
void _onFieldChangedListener(FieldInfo fieldInfo) {
if (!isClosed) {
add(DateCellEvent.didUpdateField(fieldInfo));
}
}
} }
@freezed @freezed
class DateCellEvent with _$DateCellEvent { class DateCellEvent with _$DateCellEvent {
const factory DateCellEvent.initial() = _InitialCell;
const factory DateCellEvent.didReceiveCellUpdate(DateCellDataPB? data) = const factory DateCellEvent.didReceiveCellUpdate(DateCellDataPB? data) =
_DidReceiveCellUpdate; _DidReceiveCellUpdate;
const factory DateCellEvent.didUpdateField(FieldInfo fieldInfo) =
_DidUpdateField;
} }
@freezed @freezed

View File

@ -39,6 +39,7 @@ class DateCellEditorBloc
), ),
super(DateCellEditorState.initial(cellController, reminderBloc)) { super(DateCellEditorState.initial(cellController, reminderBloc)) {
_dispatch(); _dispatch();
_startListening();
} }
final DateCellBackendService _dateCellBackendService; final DateCellBackendService _dateCellBackendService;
@ -50,7 +51,6 @@ class DateCellEditorBloc
on<DateCellEditorEvent>( on<DateCellEditorEvent>(
(event, emit) async { (event, emit) async {
await event.when( await event.when(
initial: () async => _startListening(),
didReceiveCellUpdate: (DateCellDataPB? cellData) { didReceiveCellUpdate: (DateCellDataPB? cellData) {
final dateCellData = _dateDataFromCellData(cellData); final dateCellData = _dateDataFromCellData(cellData);
final endDay = final endDay =
@ -365,8 +365,9 @@ class DateCellEditorBloc
@override @override
Future<void> close() async { Future<void> close() async {
if (_onCellChangedFn != null) { if (_onCellChangedFn != null) {
cellController.removeListener(_onCellChangedFn!); cellController.removeListener(
_onCellChangedFn = null; onCellChanged: _onCellChangedFn!,
);
} }
return super.close(); return super.close();
} }
@ -417,9 +418,6 @@ class DateCellEditorBloc
@freezed @freezed
class DateCellEditorEvent with _$DateCellEditorEvent { class DateCellEditorEvent with _$DateCellEditorEvent {
// initial event
const factory DateCellEditorEvent.initial() = _Initial;
// notification that cell is updated in the backend // notification that cell is updated in the backend
const factory DateCellEditorEvent.didReceiveCellUpdate( const factory DateCellEditorEvent.didReceiveCellUpdate(
DateCellDataPB? data, DateCellDataPB? data,

View File

@ -1,15 +1,18 @@
import 'dart:async'; import 'dart:async';
import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart'; import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart';
import 'package:appflowy/plugins/database/application/field/field_info.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
part 'number_cell_bloc.freezed.dart'; part 'number_cell_bloc.freezed.dart';
class NumberCellBloc extends Bloc<NumberCellEvent, NumberCellState> { class NumberCellBloc extends Bloc<NumberCellEvent, NumberCellState> {
NumberCellBloc({required this.cellController}) NumberCellBloc({
: super(NumberCellState.initial(cellController)) { required this.cellController,
}) : super(NumberCellState.initial(cellController)) {
_dispatch(); _dispatch();
_startListening();
} }
final NumberCellController cellController; final NumberCellController cellController;
@ -18,8 +21,10 @@ class NumberCellBloc extends Bloc<NumberCellEvent, NumberCellState> {
@override @override
Future<void> close() async { Future<void> close() async {
if (_onCellChangedFn != null) { if (_onCellChangedFn != null) {
cellController.removeListener(_onCellChangedFn!); cellController.removeListener(
_onCellChangedFn = null; onCellChanged: _onCellChangedFn!,
onFieldChanged: _onFieldChangedListener,
);
} }
await cellController.dispose(); await cellController.dispose();
return super.close(); return super.close();
@ -29,12 +34,15 @@ class NumberCellBloc extends Bloc<NumberCellEvent, NumberCellState> {
on<NumberCellEvent>( on<NumberCellEvent>(
(event, emit) async { (event, emit) async {
await event.when( await event.when(
initial: () {
_startListening();
},
didReceiveCellUpdate: (cellData) { didReceiveCellUpdate: (cellData) {
emit(state.copyWith(content: cellData ?? "")); emit(state.copyWith(content: cellData ?? ""));
}, },
didUpdateField: (fieldInfo) {
final wrap = fieldInfo.wrapCellContent;
if (wrap != null) {
emit(state.copyWith(wrap: wrap));
}
},
updateCell: (text) async { updateCell: (text) async {
if (state.content != text) { if (state.content != text) {
emit(state.copyWith(content: text)); emit(state.copyWith(content: text));
@ -62,27 +70,38 @@ class NumberCellBloc extends Bloc<NumberCellEvent, NumberCellState> {
add(NumberCellEvent.didReceiveCellUpdate(cellContent)); add(NumberCellEvent.didReceiveCellUpdate(cellContent));
} }
}, },
onFieldChanged: _onFieldChangedListener,
); );
} }
void _onFieldChangedListener(FieldInfo fieldInfo) {
if (!isClosed) {
add(NumberCellEvent.didUpdateField(fieldInfo));
}
}
} }
@freezed @freezed
class NumberCellEvent with _$NumberCellEvent { class NumberCellEvent with _$NumberCellEvent {
const factory NumberCellEvent.initial() = _Initial;
const factory NumberCellEvent.updateCell(String text) = _UpdateCell;
const factory NumberCellEvent.didReceiveCellUpdate(String? cellContent) = const factory NumberCellEvent.didReceiveCellUpdate(String? cellContent) =
_DidReceiveCellUpdate; _DidReceiveCellUpdate;
const factory NumberCellEvent.didUpdateField(FieldInfo fieldInfo) =
_DidUpdateField;
const factory NumberCellEvent.updateCell(String text) = _UpdateCell;
} }
@freezed @freezed
class NumberCellState with _$NumberCellState { class NumberCellState with _$NumberCellState {
const factory NumberCellState({ const factory NumberCellState({
required String content, required String content,
required bool wrap,
}) = _NumberCellState; }) = _NumberCellState;
factory NumberCellState.initial(TextCellController cellController) { factory NumberCellState.initial(TextCellController cellController) {
final wrap = cellController.fieldInfo.wrapCellContent;
return NumberCellState( return NumberCellState(
content: cellController.getCellData() ?? "", content: cellController.getCellData() ?? "",
wrap: wrap ?? true,
); );
} }
} }

View File

@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart'; import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart';
import 'package:appflowy/plugins/database/application/field/field_info.dart';
import 'package:appflowy/plugins/database/application/field/type_option/relation_type_option_cubit.dart'; import 'package:appflowy/plugins/database/application/field/type_option/relation_type_option_cubit.dart';
import 'package:appflowy/plugins/database/application/field/type_option/type_option_data_parser.dart'; import 'package:appflowy/plugins/database/application/field/type_option/type_option_data_parser.dart';
import 'package:appflowy/plugins/database/domain/field_service.dart'; import 'package:appflowy/plugins/database/domain/field_service.dart';
@ -16,7 +17,7 @@ part 'relation_cell_bloc.freezed.dart';
class RelationCellBloc extends Bloc<RelationCellEvent, RelationCellState> { class RelationCellBloc extends Bloc<RelationCellEvent, RelationCellState> {
RelationCellBloc({required this.cellController}) RelationCellBloc({required this.cellController})
: super(RelationCellState.initial()) { : super(RelationCellState.initial(cellController)) {
_dispatch(); _dispatch();
_startListening(); _startListening();
_init(); _init();
@ -28,8 +29,10 @@ class RelationCellBloc extends Bloc<RelationCellEvent, RelationCellState> {
@override @override
Future<void> close() async { Future<void> close() async {
if (_onCellChangedFn != null) { if (_onCellChangedFn != null) {
cellController.removeListener(_onCellChangedFn!); cellController.removeListener(
_onCellChangedFn = null; onCellChanged: _onCellChangedFn!,
onFieldChanged: _onFieldChangedListener,
);
} }
return super.close(); return super.close();
} }
@ -38,7 +41,7 @@ class RelationCellBloc extends Bloc<RelationCellEvent, RelationCellState> {
on<RelationCellEvent>( on<RelationCellEvent>(
(event, emit) async { (event, emit) async {
await event.when( await event.when(
didUpdateCell: (RelationCellDataPB? cellData) async { didUpdateCell: (cellData) async {
if (cellData == null || if (cellData == null ||
cellData.rowIds.isEmpty || cellData.rowIds.isEmpty ||
state.relatedDatabaseMeta == null) { state.relatedDatabaseMeta == null) {
@ -60,7 +63,13 @@ class RelationCellBloc extends Bloc<RelationCellEvent, RelationCellState> {
); );
emit(state.copyWith(rows: rows)); emit(state.copyWith(rows: rows));
}, },
didUpdateRelationTypeOption: (typeOption) async { didUpdateField: (FieldInfo fieldInfo) async {
final wrap = fieldInfo.wrapCellContent;
if (wrap != null) {
emit(state.copyWith(wrap: wrap));
}
final RelationTypeOptionPB typeOption =
cellController.getTypeOption(RelationTypeOptionDataParser());
if (typeOption.databaseId.isEmpty) { if (typeOption.databaseId.isEmpty) {
return; return;
} }
@ -86,25 +95,23 @@ class RelationCellBloc extends Bloc<RelationCellEvent, RelationCellState> {
add(RelationCellEvent.didUpdateCell(data)); add(RelationCellEvent.didUpdateCell(data));
} }
}, },
onCellFieldChanged: (field) { onFieldChanged: _onFieldChangedListener,
if (!isClosed) {
final RelationTypeOptionPB typeOption =
cellController.getTypeOption(RelationTypeOptionDataParser());
add(RelationCellEvent.didUpdateRelationTypeOption(typeOption));
}
},
); );
} }
void _onFieldChangedListener(FieldInfo fieldInfo) {
if (!isClosed) {
add(RelationCellEvent.didUpdateField(fieldInfo));
}
}
void _init() { void _init() {
final typeOption = add(RelationCellEvent.didUpdateField(cellController.fieldInfo));
cellController.getTypeOption(RelationTypeOptionDataParser());
add(RelationCellEvent.didUpdateRelationTypeOption(typeOption));
} }
void _loadCellData() { void _loadCellData() {
final cellData = cellController.getCellData(); final cellData = cellController.getCellData();
if (!isClosed) { if (!isClosed && cellData != null) {
add(RelationCellEvent.didUpdateCell(cellData)); add(RelationCellEvent.didUpdateCell(cellData));
} }
} }
@ -166,11 +173,10 @@ class RelationCellBloc extends Bloc<RelationCellEvent, RelationCellState> {
@freezed @freezed
class RelationCellEvent with _$RelationCellEvent { class RelationCellEvent with _$RelationCellEvent {
const factory RelationCellEvent.didUpdateRelationTypeOption(
RelationTypeOptionPB typeOption,
) = _DidUpdateRelationTypeOption;
const factory RelationCellEvent.didUpdateCell(RelationCellDataPB? data) = const factory RelationCellEvent.didUpdateCell(RelationCellDataPB? data) =
_DidUpdateCell; _DidUpdateCell;
const factory RelationCellEvent.didUpdateField(FieldInfo fieldInfo) =
_DidUpdateField;
const factory RelationCellEvent.selectDatabaseId( const factory RelationCellEvent.selectDatabaseId(
String databaseId, String databaseId,
) = _SelectDatabaseId; ) = _SelectDatabaseId;
@ -182,10 +188,15 @@ class RelationCellState with _$RelationCellState {
const factory RelationCellState({ const factory RelationCellState({
required DatabaseMeta? relatedDatabaseMeta, required DatabaseMeta? relatedDatabaseMeta,
required List<RelatedRowDataPB> rows, required List<RelatedRowDataPB> rows,
required bool wrap,
}) = _RelationCellState; }) = _RelationCellState;
factory RelationCellState.initial() => const RelationCellState( factory RelationCellState.initial(RelationCellController cellController) {
relatedDatabaseMeta: null, final wrap = cellController.fieldInfo.wrapCellContent;
rows: [], return RelationCellState(
); relatedDatabaseMeta: null,
rows: [],
wrap: wrap ?? true,
);
}
} }

View File

@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart'; import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart';
import 'package:appflowy/plugins/database/application/field/field_info.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/select_option_entities.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/select_option_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
@ -9,9 +10,11 @@ part 'select_option_cell_bloc.freezed.dart';
class SelectOptionCellBloc class SelectOptionCellBloc
extends Bloc<SelectOptionCellEvent, SelectOptionCellState> { extends Bloc<SelectOptionCellEvent, SelectOptionCellState> {
SelectOptionCellBloc({required this.cellController}) SelectOptionCellBloc({
: super(SelectOptionCellState.initial(cellController)) { required this.cellController,
}) : super(SelectOptionCellState.initial(cellController)) {
_dispatch(); _dispatch();
_startListening();
} }
final SelectOptionCellController cellController; final SelectOptionCellController cellController;
@ -20,8 +23,10 @@ class SelectOptionCellBloc
@override @override
Future<void> close() async { Future<void> close() async {
if (_onCellChangedFn != null) { if (_onCellChangedFn != null) {
cellController.removeListener(_onCellChangedFn!); cellController.removeListener(
_onCellChangedFn = null; onCellChanged: _onCellChangedFn!,
onFieldChanged: _onFieldChangedListener,
);
} }
await cellController.dispose(); await cellController.dispose();
return super.close(); return super.close();
@ -29,11 +34,8 @@ class SelectOptionCellBloc
void _dispatch() { void _dispatch() {
on<SelectOptionCellEvent>( on<SelectOptionCellEvent>(
(event, emit) async { (event, emit) {
await event.when( event.when(
initial: () async {
_startListening();
},
didReceiveOptions: (List<SelectOptionPB> selectedOptions) { didReceiveOptions: (List<SelectOptionPB> selectedOptions) {
emit( emit(
state.copyWith( state.copyWith(
@ -41,6 +43,12 @@ class SelectOptionCellBloc
), ),
); );
}, },
didUpdateField: (fieldInfo) {
final wrap = fieldInfo.wrapCellContent;
if (wrap != null) {
emit(state.copyWith(wrap: wrap));
}
},
); );
}, },
); );
@ -57,31 +65,41 @@ class SelectOptionCellBloc
); );
} }
}, },
onFieldChanged: _onFieldChangedListener,
); );
} }
void _onFieldChangedListener(FieldInfo fieldInfo) {
if (!isClosed) {
add(SelectOptionCellEvent.didUpdateField(fieldInfo));
}
}
} }
@freezed @freezed
class SelectOptionCellEvent with _$SelectOptionCellEvent { class SelectOptionCellEvent with _$SelectOptionCellEvent {
const factory SelectOptionCellEvent.initial() = _InitialCell;
const factory SelectOptionCellEvent.didReceiveOptions( const factory SelectOptionCellEvent.didReceiveOptions(
List<SelectOptionPB> selectedOptions, List<SelectOptionPB> selectedOptions,
) = _DidReceiveOptions; ) = _DidReceiveOptions;
const factory SelectOptionCellEvent.didUpdateField(FieldInfo fieldInfo) =
_DidUpdateField;
} }
@freezed @freezed
class SelectOptionCellState with _$SelectOptionCellState { class SelectOptionCellState with _$SelectOptionCellState {
const factory SelectOptionCellState({ const factory SelectOptionCellState({
required List<SelectOptionPB> selectedOptions, required List<SelectOptionPB> selectedOptions,
required bool wrap,
}) = _SelectOptionCellState; }) = _SelectOptionCellState;
factory SelectOptionCellState.initial( factory SelectOptionCellState.initial(
SelectOptionCellController cellController, SelectOptionCellController cellController,
) { ) {
final data = cellController.getCellData(); final data = cellController.getCellData();
final wrap = cellController.fieldInfo.wrapCellContent;
return SelectOptionCellState( return SelectOptionCellState(
selectedOptions: data?.selectOptions ?? [], selectedOptions: data?.selectOptions ?? [],
wrap: wrap ?? true,
); );
} }
} }

View File

@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart'; import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart';
import 'package:appflowy/plugins/database/application/field/field_info.dart';
import 'package:appflowy/plugins/database/application/field/type_option/select_type_option_actions.dart'; import 'package:appflowy/plugins/database/application/field/type_option/select_type_option_actions.dart';
import 'package:appflowy/plugins/database/application/field/type_option/type_option_data_parser.dart'; import 'package:appflowy/plugins/database/application/field/type_option/type_option_data_parser.dart';
import 'package:appflowy/plugins/database/domain/field_service.dart'; import 'package:appflowy/plugins/database/domain/field_service.dart';
@ -163,8 +164,10 @@ class SelectOptionCellEditorBloc
@override @override
Future<void> close() async { Future<void> close() async {
if (_onCellChangedFn != null) { if (_onCellChangedFn != null) {
cellController.removeListener(_onCellChangedFn!); cellController.removeListener(
_onCellChangedFn = null; onCellChanged: _onCellChangedFn!,
onFieldChanged: _onFieldChangedListener,
);
} }
return super.close(); return super.close();
} }
@ -172,27 +175,25 @@ class SelectOptionCellEditorBloc
void _startListening() { void _startListening() {
_onCellChangedFn = cellController.addListener( _onCellChangedFn = cellController.addListener(
onCellChanged: (cellData) { onCellChanged: (cellData) {
if (isClosed) { if (!isClosed) {
Log.warn("Unexpecteded closing the bloc"); add(
return; SelectOptionCellEditorEvent.didUpdateCell(
cellData == null ? [] : cellData.selectOptions,
),
);
} }
add(
SelectOptionCellEditorEvent.didUpdateCell(
cellData == null ? [] : cellData.selectOptions,
),
);
},
onCellFieldChanged: (field) {
if (isClosed) {
Log.warn("Unexpecteded closing the bloc");
return;
}
final loadedOptions = _loadAllOptions(cellController);
add(SelectOptionCellEditorEvent.didUpdateOptions(loadedOptions));
}, },
onFieldChanged: _onFieldChangedListener,
); );
} }
void _onFieldChangedListener(FieldInfo fieldInfo) {
if (!isClosed) {
final loadedOptions = _loadAllOptions(cellController);
add(SelectOptionCellEditorEvent.didUpdateOptions(loadedOptions));
}
}
Future<void> _createOption({ Future<void> _createOption({
required String name, required String name,
required SelectOptionColorPB color, required SelectOptionColorPB color,

View File

@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart'; import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart';
import 'package:appflowy/plugins/database/application/field/field_info.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
@ -10,6 +11,7 @@ class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
TextCellBloc({required this.cellController}) TextCellBloc({required this.cellController})
: super(TextCellState.initial(cellController)) { : super(TextCellState.initial(cellController)) {
_dispatch(); _dispatch();
_startListening();
} }
final TextCellController cellController; final TextCellController cellController;
@ -18,8 +20,10 @@ class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
@override @override
Future<void> close() async { Future<void> close() async {
if (_onCellChangedFn != null) { if (_onCellChangedFn != null) {
cellController.removeListener(_onCellChangedFn!); cellController.removeListener(
_onCellChangedFn = null; onCellChanged: _onCellChangedFn!,
onFieldChanged: _onFieldChangedListener,
);
} }
await cellController.dispose(); await cellController.dispose();
return super.close(); return super.close();
@ -29,12 +33,15 @@ class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
on<TextCellEvent>( on<TextCellEvent>(
(event, emit) { (event, emit) {
event.when( event.when(
initial: () {
_startListening();
},
didReceiveCellUpdate: (String content) { didReceiveCellUpdate: (String content) {
emit(state.copyWith(content: content)); emit(state.copyWith(content: content));
}, },
didUpdateField: (fieldInfo) {
final wrap = fieldInfo.wrapCellContent;
if (wrap != null) {
emit(state.copyWith(wrap: wrap));
}
},
didUpdateEmoji: (String emoji) { didUpdateEmoji: (String emoji) {
emit(state.copyWith(emoji: emoji)); emit(state.copyWith(emoji: emoji));
}, },
@ -58,20 +65,30 @@ class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
add(TextCellEvent.didReceiveCellUpdate(cellContent ?? "")); add(TextCellEvent.didReceiveCellUpdate(cellContent ?? ""));
} }
}, },
onRowMetaChanged: () { onFieldChanged: _onFieldChangedListener,
if (!isClosed && cellController.fieldInfo.isPrimary) { onRowMetaChanged: cellController.fieldInfo.isPrimary
add(TextCellEvent.didUpdateEmoji(cellController.icon ?? "")); ? () {
} if (!isClosed) {
}, add(TextCellEvent.didUpdateEmoji(cellController.icon ?? ""));
}
}
: null,
); );
} }
void _onFieldChangedListener(FieldInfo fieldInfo) {
if (!isClosed) {
add(TextCellEvent.didUpdateField(fieldInfo));
}
}
} }
@freezed @freezed
class TextCellEvent with _$TextCellEvent { class TextCellEvent with _$TextCellEvent {
const factory TextCellEvent.initial() = _InitialCell;
const factory TextCellEvent.didReceiveCellUpdate(String cellContent) = const factory TextCellEvent.didReceiveCellUpdate(String cellContent) =
_DidReceiveCellUpdate; _DidReceiveCellUpdate;
const factory TextCellEvent.didUpdateField(FieldInfo fieldInfo) =
_DidUpdateField;
const factory TextCellEvent.updateText(String text) = _UpdateText; const factory TextCellEvent.updateText(String text) = _UpdateText;
const factory TextCellEvent.enableEdit(bool enabled) = _EnableEdit; const factory TextCellEvent.enableEdit(bool enabled) = _EnableEdit;
const factory TextCellEvent.didUpdateEmoji(String emoji) = _UpdateEmoji; const factory TextCellEvent.didUpdateEmoji(String emoji) = _UpdateEmoji;
@ -83,13 +100,20 @@ class TextCellState with _$TextCellState {
required String content, required String content,
required String emoji, required String emoji,
required bool enableEdit, required bool enableEdit,
required bool wrap,
}) = _TextCellState; }) = _TextCellState;
factory TextCellState.initial(TextCellController cellController) => factory TextCellState.initial(TextCellController cellController) {
TextCellState( final cellData = cellController.getCellData() ?? "";
content: cellController.getCellData() ?? "", final wrap = cellController.fieldInfo.wrapCellContent ?? false;
emoji: final emoji =
cellController.fieldInfo.isPrimary ? cellController.icon ?? "" : "", cellController.fieldInfo.isPrimary ? cellController.icon ?? "" : "";
enableEdit: false,
); return TextCellState(
content: cellData,
emoji: emoji,
enableEdit: false,
wrap: wrap,
);
}
} }

View File

@ -9,9 +9,11 @@ import 'package:freezed_annotation/freezed_annotation.dart';
part 'timestamp_cell_bloc.freezed.dart'; part 'timestamp_cell_bloc.freezed.dart';
class TimestampCellBloc extends Bloc<TimestampCellEvent, TimestampCellState> { class TimestampCellBloc extends Bloc<TimestampCellEvent, TimestampCellState> {
TimestampCellBloc({required this.cellController}) TimestampCellBloc({
: super(TimestampCellState.initial(cellController)) { required this.cellController,
}) : super(TimestampCellState.initial(cellController)) {
_dispatch(); _dispatch();
_startListening();
} }
final TimestampCellController cellController; final TimestampCellController cellController;
@ -20,8 +22,10 @@ class TimestampCellBloc extends Bloc<TimestampCellEvent, TimestampCellState> {
@override @override
Future<void> close() async { Future<void> close() async {
if (_onCellChangedFn != null) { if (_onCellChangedFn != null) {
cellController.removeListener(_onCellChangedFn!); cellController.removeListener(
_onCellChangedFn = null; onCellChanged: _onCellChangedFn!,
onFieldChanged: _onFieldChangedListener,
);
} }
await cellController.dispose(); await cellController.dispose();
return super.close(); return super.close();
@ -31,7 +35,6 @@ class TimestampCellBloc extends Bloc<TimestampCellEvent, TimestampCellState> {
on<TimestampCellEvent>( on<TimestampCellEvent>(
(event, emit) async { (event, emit) async {
event.when( event.when(
initial: () => _startListening(),
didReceiveCellUpdate: (TimestampCellDataPB? cellData) { didReceiveCellUpdate: (TimestampCellDataPB? cellData) {
emit( emit(
state.copyWith( state.copyWith(
@ -40,6 +43,12 @@ class TimestampCellBloc extends Bloc<TimestampCellEvent, TimestampCellState> {
), ),
); );
}, },
didUpdateField: (fieldInfo) {
final wrap = fieldInfo.wrapCellContent;
if (wrap != null) {
emit(state.copyWith(wrap: wrap));
}
},
); );
}, },
); );
@ -52,16 +61,24 @@ class TimestampCellBloc extends Bloc<TimestampCellEvent, TimestampCellState> {
add(TimestampCellEvent.didReceiveCellUpdate(data)); add(TimestampCellEvent.didReceiveCellUpdate(data));
} }
}, },
onFieldChanged: _onFieldChangedListener,
); );
} }
void _onFieldChangedListener(FieldInfo fieldInfo) {
if (!isClosed) {
add(TimestampCellEvent.didUpdateField(fieldInfo));
}
}
} }
@freezed @freezed
class TimestampCellEvent with _$TimestampCellEvent { class TimestampCellEvent with _$TimestampCellEvent {
const factory TimestampCellEvent.initial() = _InitialCell;
const factory TimestampCellEvent.didReceiveCellUpdate( const factory TimestampCellEvent.didReceiveCellUpdate(
TimestampCellDataPB? data, TimestampCellDataPB? data,
) = _DidReceiveCellUpdate; ) = _DidReceiveCellUpdate;
const factory TimestampCellEvent.didUpdateField(FieldInfo fieldInfo) =
_DidUpdateField;
} }
@freezed @freezed
@ -70,15 +87,18 @@ class TimestampCellState with _$TimestampCellState {
required TimestampCellDataPB? data, required TimestampCellDataPB? data,
required String dateStr, required String dateStr,
required FieldInfo fieldInfo, required FieldInfo fieldInfo,
required bool wrap,
}) = _TimestampCellState; }) = _TimestampCellState;
factory TimestampCellState.initial(TimestampCellController context) { factory TimestampCellState.initial(TimestampCellController cellController) {
final cellData = context.getCellData(); final cellData = cellController.getCellData();
final wrap = cellController.fieldInfo.wrapCellContent;
return TimestampCellState( return TimestampCellState(
fieldInfo: context.fieldInfo, fieldInfo: cellController.fieldInfo,
data: cellData, data: cellData,
dateStr: cellData?.dateTime ?? "", dateStr: cellData?.dateTime ?? "",
wrap: wrap ?? true,
); );
} }
} }

View File

@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart'; import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart';
import 'package:appflowy/plugins/database/application/field/field_info.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/url_entities.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/url_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
@ -9,9 +10,11 @@ import 'package:freezed_annotation/freezed_annotation.dart';
part 'url_cell_bloc.freezed.dart'; part 'url_cell_bloc.freezed.dart';
class URLCellBloc extends Bloc<URLCellEvent, URLCellState> { class URLCellBloc extends Bloc<URLCellEvent, URLCellState> {
URLCellBloc({required this.cellController}) URLCellBloc({
: super(URLCellState.initial(cellController)) { required this.cellController,
}) : super(URLCellState.initial(cellController)) {
_dispatch(); _dispatch();
_startListening();
} }
final URLCellController cellController; final URLCellController cellController;
@ -20,8 +23,10 @@ class URLCellBloc extends Bloc<URLCellEvent, URLCellState> {
@override @override
Future<void> close() async { Future<void> close() async {
if (_onCellChangedFn != null) { if (_onCellChangedFn != null) {
cellController.removeListener(_onCellChangedFn!); cellController.removeListener(
_onCellChangedFn = null; onCellChanged: _onCellChangedFn!,
onFieldChanged: _onFieldChangedListener,
);
} }
await cellController.dispose(); await cellController.dispose();
return super.close(); return super.close();
@ -31,12 +36,9 @@ class URLCellBloc extends Bloc<URLCellEvent, URLCellState> {
on<URLCellEvent>( on<URLCellEvent>(
(event, emit) async { (event, emit) async {
await event.when( await event.when(
initial: () { didUpdateCell: (cellData) async {
_startListening();
},
didReceiveCellUpdate: (cellData) async {
final content = cellData?.content ?? ""; final content = cellData?.content ?? "";
final isValid = await isUrlValid(content); final isValid = await _isUrlValid(content);
emit( emit(
state.copyWith( state.copyWith(
content: content, content: content,
@ -44,6 +46,12 @@ class URLCellBloc extends Bloc<URLCellEvent, URLCellState> {
), ),
); );
}, },
didUpdateField: (fieldInfo) {
final wrap = fieldInfo.wrapCellContent;
if (wrap != null) {
emit(state.copyWith(wrap: wrap));
}
},
updateURL: (String url) { updateURL: (String url) {
cellController.saveCellData(url, debounce: true); cellController.saveCellData(url, debounce: true);
}, },
@ -56,13 +64,20 @@ class URLCellBloc extends Bloc<URLCellEvent, URLCellState> {
_onCellChangedFn = cellController.addListener( _onCellChangedFn = cellController.addListener(
onCellChanged: (cellData) { onCellChanged: (cellData) {
if (!isClosed) { if (!isClosed) {
add(URLCellEvent.didReceiveCellUpdate(cellData)); add(URLCellEvent.didUpdateCell(cellData));
} }
}, },
onFieldChanged: _onFieldChangedListener,
); );
} }
Future<bool> isUrlValid(String content) async { void _onFieldChangedListener(FieldInfo fieldInfo) {
if (!isClosed) {
add(URLCellEvent.didUpdateField(fieldInfo));
}
}
Future<bool> _isUrlValid(String content) async {
if (content.isEmpty) { if (content.isEmpty) {
return true; return true;
} }
@ -90,10 +105,11 @@ class URLCellBloc extends Bloc<URLCellEvent, URLCellState> {
@freezed @freezed
class URLCellEvent with _$URLCellEvent { class URLCellEvent with _$URLCellEvent {
const factory URLCellEvent.initial() = _InitialCell;
const factory URLCellEvent.updateURL(String url) = _UpdateURL; const factory URLCellEvent.updateURL(String url) = _UpdateURL;
const factory URLCellEvent.didReceiveCellUpdate(URLCellDataPB? cell) = const factory URLCellEvent.didUpdateCell(URLCellDataPB? cell) =
_DidReceiveCellUpdate; _DidUpdateCell;
const factory URLCellEvent.didUpdateField(FieldInfo fieldInfo) =
_DidUpdateField;
} }
@freezed @freezed
@ -101,13 +117,16 @@ class URLCellState with _$URLCellState {
const factory URLCellState({ const factory URLCellState({
required String content, required String content,
required bool isValid, required bool isValid,
required bool wrap,
}) = _URLCellState; }) = _URLCellState;
factory URLCellState.initial(URLCellController context) { factory URLCellState.initial(URLCellController cellController) {
final cellData = context.getCellData(); final cellData = cellController.getCellData();
final wrap = cellController.fieldInfo.wrapCellContent;
return URLCellState( return URLCellState(
content: cellData?.content ?? "", content: cellData?.content ?? "",
isValid: true, isValid: true,
wrap: wrap ?? true,
); );
} }
} }

View File

@ -1,86 +0,0 @@
import 'dart:async';
import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/url_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'url_cell_editor_bloc.freezed.dart';
class URLCellEditorBloc extends Bloc<URLCellEditorEvent, URLCellEditorState> {
URLCellEditorBloc({required this.cellController})
: super(URLCellEditorState.initial(cellController)) {
_dispatch();
}
final URLCellController cellController;
void Function()? _onCellChangedFn;
void _dispatch() {
on<URLCellEditorEvent>(
(event, emit) async {
await event.when(
initial: () {
_startListening();
},
updateText: (text) async {
await cellController.saveCellData(text);
emit(
state.copyWith(
content: text,
isFinishEditing: true,
),
);
},
didReceiveCellUpdate: (cellData) {
emit(state.copyWith(content: cellData?.content ?? ""));
},
);
},
);
}
@override
Future<void> close() async {
if (_onCellChangedFn != null) {
cellController.removeListener(_onCellChangedFn!);
_onCellChangedFn = null;
}
await cellController.dispose();
return super.close();
}
void _startListening() {
_onCellChangedFn = cellController.addListener(
onCellChanged: (cellData) {
if (!isClosed) {
add(URLCellEditorEvent.didReceiveCellUpdate(cellData));
}
},
);
}
}
@freezed
class URLCellEditorEvent with _$URLCellEditorEvent {
const factory URLCellEditorEvent.initial() = _InitialCell;
const factory URLCellEditorEvent.didReceiveCellUpdate(URLCellDataPB? cell) =
_DidReceiveCellUpdate;
const factory URLCellEditorEvent.updateText(String text) = _UpdateText;
}
@freezed
class URLCellEditorState with _$URLCellEditorState {
const factory URLCellEditorState({
required String content,
required bool isFinishEditing,
}) = _URLCellEditorState;
factory URLCellEditorState.initial(URLCellController context) {
final cellData = context.getCellData();
return URLCellEditorState(
content: cellData?.content ?? "",
isFinishEditing: true,
);
}
}

View File

@ -64,7 +64,6 @@ class CellController<T, D> {
RowMetaListener? _rowMetaListener; RowMetaListener? _rowMetaListener;
CellDataNotifier<T?>? _cellDataNotifier; CellDataNotifier<T?>? _cellDataNotifier;
void Function(FieldInfo field)? _onCellFieldChanged;
VoidCallback? _onRowMetaChanged; VoidCallback? _onRowMetaChanged;
Timer? _loadDataOperation; Timer? _loadDataOperation;
Timer? _saveDataOperation; Timer? _saveDataOperation;
@ -104,16 +103,7 @@ class CellController<T, D> {
// 2. Listen on the field event and load the cell data if needed. // 2. Listen on the field event and load the cell data if needed.
_fieldController.addSingleFieldListener( _fieldController.addSingleFieldListener(
fieldId, fieldId,
onFieldChanged: (fieldInfo) { onFieldChanged: _onFieldChangedListener,
// reloadOnFieldChanged should be true if you want to reload the cell
// data when the corresponding field is changed.
// For example:
// 12 -> $12
if (_cellDataLoader.reloadOnFieldChange) {
_loadData();
}
_onCellFieldChanged?.call(fieldInfo);
},
); );
// 3. If the field is primary listen to row meta changes. // 3. If the field is primary listen to row meta changes.
@ -130,22 +120,49 @@ class CellController<T, D> {
/// Add a new listener /// Add a new listener
VoidCallback? addListener({ VoidCallback? addListener({
required void Function(T?) onCellChanged, required void Function(T?) onCellChanged,
void Function(FieldInfo field)? onCellFieldChanged, void Function(FieldInfo fieldInfo)? onFieldChanged,
VoidCallback? onRowMetaChanged, VoidCallback? onRowMetaChanged,
}) { }) {
_onCellFieldChanged = onCellFieldChanged; /// an adaptor for the onCellChanged listener
_onRowMetaChanged = onRowMetaChanged;
/// Notify the listener, the cell data was changed.
void onCellChangedFn() => onCellChanged(_cellDataNotifier?.value); void onCellChangedFn() => onCellChanged(_cellDataNotifier?.value);
_cellDataNotifier?.addListener(onCellChangedFn); _cellDataNotifier?.addListener(onCellChangedFn);
if (onFieldChanged != null) {
_fieldController.addSingleFieldListener(
fieldId,
onFieldChanged: onFieldChanged,
);
}
_onRowMetaChanged = onRowMetaChanged;
// Return the function pointer that can be used when calling removeListener. // Return the function pointer that can be used when calling removeListener.
return onCellChangedFn; return onCellChangedFn;
} }
void removeListener(VoidCallback fn) { void removeListener({
_cellDataNotifier?.removeListener(fn); required VoidCallback onCellChanged,
void Function(FieldInfo fieldInfo)? onFieldChanged,
VoidCallback? onRowMetaChanged,
}) {
_cellDataNotifier?.removeListener(onCellChanged);
if (onFieldChanged != null) {
_fieldController.removeSingleFieldListener(
fieldId: fieldId,
onFieldChanged: onFieldChanged,
);
}
}
void _onFieldChangedListener(FieldInfo fieldInfo) {
// reloadOnFieldChanged should be true if you want to reload the cell
// data when the corresponding field is changed.
// For example:
// 12 -> $12
if (_cellDataLoader.reloadOnFieldChange) {
_loadData();
}
} }
/// Get the cell data. The cell data will be read from the cache first, /// Get the cell data. The cell data will be read from the cache first,
@ -218,6 +235,11 @@ class CellController<T, D> {
await _cellListener?.stop(); await _cellListener?.stop();
_cellListener = null; _cellListener = null;
_fieldController.removeSingleFieldListener(
fieldId: fieldId,
onFieldChanged: _onFieldChangedListener,
);
_loadDataOperation?.cancel(); _loadDataOperation?.cancel();
_saveDataOperation?.cancel(); _saveDataOperation?.cancel();
_cellDataNotifier?.dispose(); _cellDataNotifier?.dispose();

View File

@ -37,12 +37,7 @@ class CellDataLoader<T> {
(result) => result.fold( (result) => result.fold(
(CellPB cell) { (CellPB cell) {
try { try {
// Return null if the data of the cell is empty. return parser.parserData(cell.data);
if (cell.data.isEmpty) {
return null;
} else {
return parser.parserData(cell.data);
}
} catch (e, s) { } catch (e, s) {
Log.error('$parser parser cellData failed, $e'); Log.error('$parser parser cellData failed, $e');
Log.error('Stack trace \n $s'); Log.error('Stack trace \n $s');

View File

@ -30,8 +30,7 @@ class FieldCellBloc extends Bloc<FieldCellEvent, FieldCellState> {
emit(state.copyWith(width: width)); emit(state.copyWith(width: width));
}, },
endUpdateWidth: () { endUpdateWidth: () {
if (state.width != if (state.width != state.fieldInfo.width) {
state.fieldInfo.fieldSettings?.width.toDouble()) {
_fieldSettingsService.updateFieldSettings( _fieldSettingsService.updateFieldSettings(
fieldId: state.fieldInfo.id, fieldId: state.fieldInfo.id,
width: state.width, width: state.width,

View File

@ -99,6 +99,14 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
); );
_logIfError(result); _logIfError(result);
}, },
toggleWrapCellContent: () async {
final currentWrap = state.field.wrapCellContent ?? false;
final result = await fieldSettingsService.updateFieldSettings(
fieldId: state.field.id,
wrapCellContent: !currentWrap,
);
_logIfError(result);
},
); );
}, },
); );
@ -148,6 +156,8 @@ class FieldEditorEvent with _$FieldEditorEvent {
const factory FieldEditorEvent.insertRight() = _InsertRight; const factory FieldEditorEvent.insertRight() = _InsertRight;
const factory FieldEditorEvent.toggleFieldVisibility() = const factory FieldEditorEvent.toggleFieldVisibility() =
_ToggleFieldVisiblity; _ToggleFieldVisiblity;
const factory FieldEditorEvent.toggleWrapCellContent() =
_ToggleWrapCellContent;
} }
@freezed @freezed

View File

@ -31,8 +31,12 @@ class FieldInfo with _$FieldInfo {
bool get isPrimary => field.isPrimary; bool get isPrimary => field.isPrimary;
double? get width => fieldSettings?.width.toDouble();
FieldVisibility? get visibility => fieldSettings?.visibility; FieldVisibility? get visibility => fieldSettings?.visibility;
bool? get wrapCellContent => fieldSettings?.wrapCellContent;
bool get canBeGroup { bool get canBeGroup {
switch (field.fieldType) { switch (field.fieldType) {
case FieldType.URL: case FieldType.URL:

View File

@ -58,6 +58,7 @@ class FieldSettingsBackendService {
required String fieldId, required String fieldId,
FieldVisibility? fieldVisibility, FieldVisibility? fieldVisibility,
double? width, double? width,
bool? wrapCellContent,
}) { }) {
final FieldSettingsChangesetPB payload = FieldSettingsChangesetPB.create() final FieldSettingsChangesetPB payload = FieldSettingsChangesetPB.create()
..viewId = viewId ..viewId = viewId
@ -71,6 +72,10 @@ class FieldSettingsBackendService {
payload.width = width.round(); payload.width = width.round();
} }
if (wrapCellContent != null) {
payload.wrapCellContent = wrapCellContent;
}
return DatabaseEventUpdateFieldSettings(payload).send(); return DatabaseEventUpdateFieldSettings(payload).send();
} }
} }

View File

@ -441,7 +441,7 @@ class _AddRowButton extends StatelessWidget {
double getMobileGridContentWidth(List<FieldInfo> fields) { double getMobileGridContentWidth(List<FieldInfo> fields) {
final visibleFields = fields.where( final visibleFields = fields.where(
(field) => field.fieldSettings?.visibility != FieldVisibility.AlwaysHidden, (field) => field.visibility != FieldVisibility.AlwaysHidden,
); );
return (visibleFields.length + 1) * 200 + return (visibleFields.length + 1) * 200 +
GridSize.horizontalHeaderPadding * 2; GridSize.horizontalHeaderPadding * 2;

View File

@ -113,7 +113,7 @@ List<CellContext> _makeCells(
cellContexts.removeWhere((cellContext) { cellContexts.removeWhere((cellContext) {
final fieldInfo = fieldController.getField(cellContext.fieldId); final fieldInfo = fieldController.getField(cellContext.fieldId);
return fieldInfo == null || return fieldInfo == null ||
!(fieldInfo.fieldSettings?.visibility.isVisibleState() ?? false) || !(fieldInfo.visibility?.isVisibleState() ?? false) ||
(groupFieldId != null && cellContext.fieldId == groupFieldId); (groupFieldId != null && cellContext.fieldId == groupFieldId);
}); });
return cellContexts.toList(); return cellContexts.toList();

View File

@ -41,7 +41,7 @@ class _DateCellState extends State<DateCardCell> {
widget.databaseController, widget.databaseController,
widget.cellContext, widget.cellContext,
).as(), ).as(),
)..add(const DateCellEvent.initial()); );
}, },
child: BlocBuilder<DateCellBloc, DateCellState>( child: BlocBuilder<DateCellBloc, DateCellState>(
buildWhen: (previous, current) => previous.dateStr != current.dateStr, buildWhen: (previous, current) => previous.dateStr != current.dateStr,

View File

@ -41,7 +41,7 @@ class _NumberCellState extends State<NumberCardCell> {
widget.databaseController, widget.databaseController,
widget.cellContext, widget.cellContext,
).as(), ).as(),
)..add(const NumberCellEvent.initial()); );
}, },
child: BlocBuilder<NumberCellBloc, NumberCellState>( child: BlocBuilder<NumberCellBloc, NumberCellState>(
buildWhen: (previous, current) => previous.content != current.content, buildWhen: (previous, current) => previous.content != current.content,

View File

@ -46,7 +46,7 @@ class _SelectOptionCellState extends State<SelectOptionCardCell> {
widget.databaseController, widget.databaseController,
widget.cellContext, widget.cellContext,
).as(), ).as(),
)..add(const SelectOptionCellEvent.initial()); );
}, },
child: BlocBuilder<SelectOptionCellBloc, SelectOptionCellState>( child: BlocBuilder<SelectOptionCellBloc, SelectOptionCellState>(
buildWhen: (previous, current) { buildWhen: (previous, current) {

View File

@ -52,7 +52,7 @@ class _TextCellState extends State<TextCardCell> {
widget.databaseController, widget.databaseController,
widget.cellContext, widget.cellContext,
).as(), ).as(),
)..add(const TextCellEvent.initial()); );
late final TextEditingController _textEditingController = late final TextEditingController _textEditingController =
TextEditingController(text: cellBloc.state.content); TextEditingController(text: cellBloc.state.content);
final focusNode = SingleListenerFocusNode(); final focusNode = SingleListenerFocusNode();

View File

@ -41,7 +41,7 @@ class _TimestampCellState extends State<TimestampCardCell> {
widget.databaseController, widget.databaseController,
widget.cellContext, widget.cellContext,
).as(), ).as(),
)..add(const TimestampCellEvent.initial()); );
}, },
child: BlocBuilder<TimestampCellBloc, TimestampCellState>( child: BlocBuilder<TimestampCellBloc, TimestampCellState>(
buildWhen: (previous, current) => previous.dateStr != current.dateStr, buildWhen: (previous, current) => previous.dateStr != current.dateStr,

View File

@ -41,7 +41,7 @@ class _URLCellState extends State<URLCardCell> {
widget.databaseController, widget.databaseController,
widget.cellContext, widget.cellContext,
).as(), ).as(),
)..add(const URLCellEvent.initial()); );
}, },
child: BlocBuilder<URLCellBloc, URLCellState>( child: BlocBuilder<URLCellBloc, URLCellState>(
buildWhen: (previous, current) => previous.content != current.content, buildWhen: (previous, current) => previous.content != current.content,

View File

@ -27,27 +27,15 @@ class DesktopGridDateCellSkin extends IEditableDateCellSkin {
direction: PopoverDirection.bottomWithLeftAligned, direction: PopoverDirection.bottomWithLeftAligned,
constraints: BoxConstraints.loose(const Size(260, 620)), constraints: BoxConstraints.loose(const Size(260, 620)),
margin: EdgeInsets.zero, margin: EdgeInsets.zero,
child: Container( child: Align(
alignment: AlignmentDirectional.centerStart, alignment: AlignmentDirectional.centerStart,
padding: GridSize.cellContentInsets, child: state.fieldInfo.wrapCellContent ?? false
child: Row( ? _buildCellContent(state)
mainAxisSize: MainAxisSize.min, : SingleChildScrollView(
children: [ physics: const NeverScrollableScrollPhysics(),
Flexible( scrollDirection: Axis.horizontal,
child: FlowyText.medium( child: _buildCellContent(state),
state.dateStr,
overflow: TextOverflow.ellipsis,
), ),
),
if (state.data?.reminderId.isNotEmpty ?? false) ...[
const HSpace(4),
FlowyTooltip(
message: LocaleKeys.grid_field_reminderOnDateTooltip.tr(),
child: const FlowySvg(FlowySvgs.clock_alarm_s),
),
],
],
),
), ),
popupBuilder: (BuildContext popoverContent) { popupBuilder: (BuildContext popoverContent) {
return DateCellEditor( return DateCellEditor(
@ -60,4 +48,30 @@ class DesktopGridDateCellSkin extends IEditableDateCellSkin {
}, },
); );
} }
Widget _buildCellContent(DateCellState state) {
final wrap = state.fieldInfo.wrapCellContent ?? false;
return Padding(
padding: GridSize.cellContentInsets,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Flexible(
child: FlowyText.medium(
state.dateStr,
overflow: wrap ? null : TextOverflow.ellipsis,
maxLines: wrap ? null : 1,
),
),
if (state.data?.reminderId.isNotEmpty ?? false) ...[
const HSpace(4),
FlowyTooltip(
message: LocaleKeys.grid_field_reminderOnDateTooltip.tr(),
child: const FlowySvg(FlowySvgs.clock_alarm_s),
),
],
],
),
);
}
} }

View File

@ -2,6 +2,7 @@ import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart';
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart'; import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart';
import 'package:appflowy/plugins/database/application/cell/bloc/number_cell_bloc.dart'; import 'package:appflowy/plugins/database/application/cell/bloc/number_cell_bloc.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../editable_cell_skeleton/number.dart'; import '../editable_cell_skeleton/number.dart';
@ -19,7 +20,7 @@ class DesktopGridNumberCellSkin extends IEditableNumberCellSkin {
focusNode: focusNode, focusNode: focusNode,
onEditingComplete: () => focusNode.unfocus(), onEditingComplete: () => focusNode.unfocus(),
onSubmitted: (_) => focusNode.unfocus(), onSubmitted: (_) => focusNode.unfocus(),
maxLines: null, maxLines: context.watch<NumberCellBloc>().state.wrap ? null : 1,
style: Theme.of(context).textTheme.bodyMedium, style: Theme.of(context).textTheme.bodyMedium,
textInputAction: TextInputAction.done, textInputAction: TextInputAction.done,
decoration: InputDecoration( decoration: InputDecoration(

View File

@ -3,6 +3,7 @@ import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart';
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart'; import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart';
import 'package:appflowy/plugins/database/widgets/cell_editor/relation_cell_editor.dart'; import 'package:appflowy/plugins/database/widgets/cell_editor/relation_cell_editor.dart';
import 'package:appflowy/plugins/database/application/cell/bloc/relation_cell_bloc.dart'; import 'package:appflowy/plugins/database/application/cell/bloc/relation_cell_bloc.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart';
@ -32,13 +33,52 @@ class DesktopGridRelationCellSkin extends IEditableRelationCellSkin {
child: const RelationCellEditor(), child: const RelationCellEditor(),
); );
}, },
child: Container( child: Align(
alignment: AlignmentDirectional.centerStart, alignment: AlignmentDirectional.centerStart,
child: state.wrap
? _buildWrapRows(context, state.rows)
: _buildNoWrapRows(context, state.rows),
),
);
}
Widget _buildWrapRows(
BuildContext context,
List<RelatedRowDataPB> rows,
) {
return Padding(
padding: GridSize.cellContentInsets,
child: Wrap(
runSpacing: 4,
spacing: 4.0,
children: rows.map(
(row) {
final isEmpty = row.name.isEmpty;
return FlowyText.medium(
isEmpty ? LocaleKeys.grid_row_titlePlaceholder.tr() : row.name,
color: isEmpty ? Theme.of(context).hintColor : null,
decoration: TextDecoration.underline,
overflow: TextOverflow.ellipsis,
);
},
).toList(),
),
);
}
Widget _buildNoWrapRows(
BuildContext context,
List<RelatedRowDataPB> rows,
) {
return SingleChildScrollView(
physics: const NeverScrollableScrollPhysics(),
scrollDirection: Axis.horizontal,
child: Padding(
padding: GridSize.cellContentInsets, padding: GridSize.cellContentInsets,
child: Wrap( child: SeparatedRow(
runSpacing: 4.0, separatorBuilder: () => const HSpace(4.0),
spacing: 4.0, mainAxisSize: MainAxisSize.min,
children: state.rows.map( children: rows.map(
(row) { (row) {
final isEmpty = row.name.isEmpty; final isEmpty = row.name.isEmpty;
return FlowyText.medium( return FlowyText.medium(

View File

@ -3,7 +3,7 @@ import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart'
import 'package:appflowy/plugins/database/widgets/cell_editor/extension.dart'; import 'package:appflowy/plugins/database/widgets/cell_editor/extension.dart';
import 'package:appflowy/plugins/database/application/cell/bloc/select_option_cell_bloc.dart'; import 'package:appflowy/plugins/database/application/cell/bloc/select_option_cell_bloc.dart';
import 'package:appflowy/plugins/database/widgets/cell_editor/select_option_cell_editor.dart'; import 'package:appflowy/plugins/database/widgets/cell_editor/select_option_cell_editor.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/select_option_entities.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
@ -23,6 +23,7 @@ class DesktopGridSelectOptionCellSkin extends IEditableSelectOptionCellSkin {
controller: popoverController, controller: popoverController,
constraints: const BoxConstraints.tightFor(width: 300), constraints: const BoxConstraints.tightFor(width: 300),
margin: EdgeInsets.zero, margin: EdgeInsets.zero,
triggerActions: PopoverTriggerFlags.none,
direction: PopoverDirection.bottomWithLeftAligned, direction: PopoverDirection.bottomWithLeftAligned,
popupBuilder: (BuildContext popoverContext) { popupBuilder: (BuildContext popoverContext) {
return SelectOptionCellEditor( return SelectOptionCellEditor(
@ -32,35 +33,67 @@ class DesktopGridSelectOptionCellSkin extends IEditableSelectOptionCellSkin {
onClose: () => cellContainerNotifier.isFocus = false, onClose: () => cellContainerNotifier.isFocus = false,
child: BlocBuilder<SelectOptionCellBloc, SelectOptionCellState>( child: BlocBuilder<SelectOptionCellBloc, SelectOptionCellState>(
builder: (context, state) { builder: (context, state) {
return Container( return Align(
alignment: AlignmentDirectional.centerStart, alignment: AlignmentDirectional.centerStart,
padding: GridSize.cellContentInsets, child: state.wrap
child: state.selectedOptions.isEmpty ? _buildWrapOptions(context, state.selectedOptions)
? const SizedBox.shrink() : _buildNoWrapOptions(context, state.selectedOptions),
: _buildOptions(context, state.selectedOptions),
); );
}, },
), ),
); );
} }
Widget _buildOptions(context, List<SelectOptionPB> options) { Widget _buildWrapOptions(BuildContext context, List<SelectOptionPB> options) {
return Wrap( return Padding(
runSpacing: 4, padding: GridSize.cellContentInsets,
children: options.map( child: Wrap(
(option) { runSpacing: 4,
return Padding( children: options.map(
padding: const EdgeInsets.only(right: 4), (option) {
child: SelectOptionTag( return Padding(
option: option, padding: const EdgeInsets.only(right: 4),
padding: const EdgeInsets.symmetric( child: SelectOptionTag(
vertical: 1, option: option,
horizontal: 8, padding: const EdgeInsets.symmetric(
vertical: 1,
horizontal: 8,
),
), ),
), );
); },
}, ).toList(),
).toList(), ),
);
}
Widget _buildNoWrapOptions(
BuildContext context,
List<SelectOptionPB> options,
) {
return SingleChildScrollView(
physics: const NeverScrollableScrollPhysics(),
scrollDirection: Axis.horizontal,
child: Padding(
padding: GridSize.cellContentInsets,
child: Row(
mainAxisSize: MainAxisSize.min,
children: options.map(
(option) {
return Padding(
padding: const EdgeInsets.only(right: 4),
child: SelectOptionTag(
option: option,
padding: const EdgeInsets.symmetric(
vertical: 1,
horizontal: 8,
),
),
);
},
).toList(),
),
),
); );
} }
} }

View File

@ -44,7 +44,7 @@ class DesktopGridTextCellSkin extends IEditableTextCellSkin {
child: TextField( child: TextField(
controller: textEditingController, controller: textEditingController,
focusNode: focusNode, focusNode: focusNode,
maxLines: null, maxLines: context.watch<TextCellBloc>().state.wrap ? null : 1,
style: Theme.of(context).textTheme.bodyMedium, style: Theme.of(context).textTheme.bodyMedium,
decoration: const InputDecoration( decoration: const InputDecoration(
border: InputBorder.none, border: InputBorder.none,

View File

@ -16,10 +16,23 @@ class DesktopGridTimestampCellSkin extends IEditableTimestampCellSkin {
) { ) {
return Container( return Container(
alignment: AlignmentDirectional.centerStart, alignment: AlignmentDirectional.centerStart,
child: state.wrap
? _buildCellContent(state)
: SingleChildScrollView(
physics: const NeverScrollableScrollPhysics(),
scrollDirection: Axis.horizontal,
child: _buildCellContent(state),
),
);
}
Widget _buildCellContent(TimestampCellState state) {
return Padding(
padding: GridSize.cellContentInsets, padding: GridSize.cellContentInsets,
child: FlowyText.medium( child: FlowyText.medium(
state.dateStr, state.dateStr,
maxLines: null, overflow: state.wrap ? null : TextOverflow.ellipsis,
maxLines: state.wrap ? null : 1,
), ),
); );
} }

View File

@ -11,6 +11,7 @@ import 'package:appflowy/workspace/presentation/home/toast.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/theme_extension.dart'; import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart'; import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../editable_cell_skeleton/url.dart'; import '../editable_cell_skeleton/url.dart';
@ -24,28 +25,31 @@ class DesktopGridURLSkin extends IEditableURLCellSkin {
TextEditingController textEditingController, TextEditingController textEditingController,
URLCellDataNotifier cellDataNotifier, URLCellDataNotifier cellDataNotifier,
) { ) {
return TextField( return BlocSelector<URLCellBloc, URLCellState, bool>(
controller: textEditingController, selector: (state) => state.wrap,
focusNode: focusNode, builder: (context, wrap) => TextField(
maxLines: null, controller: textEditingController,
style: Theme.of(context).textTheme.bodyMedium?.copyWith( focusNode: focusNode,
color: Theme.of(context).colorScheme.primary, maxLines: wrap ? null : 1,
decoration: TextDecoration.underline, style: Theme.of(context).textTheme.bodyMedium?.copyWith(
), color: Theme.of(context).colorScheme.primary,
decoration: InputDecoration( decoration: TextDecoration.underline,
contentPadding: GridSize.cellContentInsets, ),
border: InputBorder.none, decoration: InputDecoration(
focusedBorder: InputBorder.none, contentPadding: GridSize.cellContentInsets,
enabledBorder: InputBorder.none, border: InputBorder.none,
errorBorder: InputBorder.none, focusedBorder: InputBorder.none,
disabledBorder: InputBorder.none, enabledBorder: InputBorder.none,
hintStyle: Theme.of(context) errorBorder: InputBorder.none,
.textTheme disabledBorder: InputBorder.none,
.bodyMedium hintStyle: Theme.of(context)
?.copyWith(color: Theme.of(context).hintColor), .textTheme
isDense: true, .bodyMedium
?.copyWith(color: Theme.of(context).hintColor),
isDense: true,
),
onTapOutside: (_) => focusNode.unfocus(),
), ),
onTapOutside: (_) => focusNode.unfocus(),
); );
} }
@ -179,8 +183,7 @@ class _VisitURLAccessoryState extends State<_VisitURLAccessory>
bool enable() => widget.cellDataNotifier.value.isNotEmpty; bool enable() => widget.cellDataNotifier.value.isNotEmpty;
@override @override
void onTap() => void onTap() => openUrlCellLink(widget.cellDataNotifier.value);
openUrlCellLink(widget.cellDataNotifier.value);
} }
class _URLAccessoryIconContainer extends StatelessWidget { class _URLAccessoryIconContainer extends StatelessWidget {
@ -190,9 +193,8 @@ class _URLAccessoryIconContainer extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SizedBox( return SizedBox.square(
width: 26, dimension: 26,
height: 26,
child: Padding( child: Padding(
padding: const EdgeInsets.all(3.0), padding: const EdgeInsets.all(3.0),
child: child, child: child,

View File

@ -57,7 +57,7 @@ class _DateCellState extends GridCellState<EditableDateCell> {
widget.databaseController, widget.databaseController,
widget.cellContext, widget.cellContext,
).as(), ).as(),
)..add(const DateCellEvent.initial()); );
@override @override
void dispose() { void dispose() {

View File

@ -58,7 +58,7 @@ class _NumberCellState extends GridEditableTextCell<EditableNumberCell> {
widget.databaseController, widget.databaseController,
widget.cellContext, widget.cellContext,
).as(), ).as(),
)..add(const NumberCellEvent.initial()); );
@override @override
void initState() { void initState() {
@ -81,12 +81,16 @@ class _NumberCellState extends GridEditableTextCell<EditableNumberCell> {
child: BlocListener<NumberCellBloc, NumberCellState>( child: BlocListener<NumberCellBloc, NumberCellState>(
listener: (context, state) => listener: (context, state) =>
_textEditingController.text = state.content, _textEditingController.text = state.content,
child: widget.skin.build( child: Builder(
context, builder: (context) {
widget.cellContainerNotifier, return widget.skin.build(
cellBloc, context,
focusNode, widget.cellContainerNotifier,
_textEditingController, cellBloc,
focusNode,
_textEditingController,
);
},
), ),
), ),
); );

View File

@ -64,7 +64,7 @@ class _SelectOptionCellState extends GridCellState<EditableSelectOptionCell> {
widget.databaseController, widget.databaseController,
widget.cellContext, widget.cellContext,
).as(), ).as(),
)..add(const SelectOptionCellEvent.initial()); );
@override @override
void dispose() { void dispose() {

View File

@ -58,7 +58,7 @@ class _TextCellState extends GridEditableTextCell<EditableTextCell> {
widget.databaseController, widget.databaseController,
widget.cellContext, widget.cellContext,
).as(), ).as(),
)..add(const TextCellEvent.initial()); );
@override @override
void initState() { void initState() {
@ -82,12 +82,16 @@ class _TextCellState extends GridEditableTextCell<EditableTextCell> {
listener: (context, state) { listener: (context, state) {
_textEditingController.text = state.content; _textEditingController.text = state.content;
}, },
child: widget.skin.build( child: Builder(
context, builder: (context) {
widget.cellContainerNotifier, return widget.skin.build(
cellBloc, context,
focusNode, widget.cellContainerNotifier,
_textEditingController, cellBloc,
focusNode,
_textEditingController,
);
},
), ),
), ),
); );

View File

@ -57,7 +57,7 @@ class _TimestampCellState extends GridCellState<EditableTimestampCell> {
widget.databaseController, widget.databaseController,
widget.cellContext, widget.cellContext,
).as(), ).as(),
)..add(const TimestampCellEvent.initial()); );
@override @override
void dispose() { void dispose() {

View File

@ -85,7 +85,7 @@ class _GridURLCellState extends GridEditableTextCell<EditableURLCell> {
widget.databaseController, widget.databaseController,
widget.cellContext, widget.cellContext,
).as(), ).as(),
)..add(const URLCellEvent.initial()); );
@override @override
SingleListenerFocusNode focusNode = SingleListenerFocusNode(); SingleListenerFocusNode focusNode = SingleListenerFocusNode();

View File

@ -42,7 +42,7 @@ class _DateCellEditor extends State<DateCellEditor> {
create: (context) => DateCellEditorBloc( create: (context) => DateCellEditorBloc(
reminderBloc: getIt<ReminderBloc>(), reminderBloc: getIt<ReminderBloc>(),
cellController: widget.cellController, cellController: widget.cellController,
)..add(const DateCellEditorEvent.initial()), ),
), ),
], ],
child: BlocBuilder<DateCellEditorBloc, DateCellEditorState>( child: BlocBuilder<DateCellEditorBloc, DateCellEditorState>(

View File

@ -1,7 +1,5 @@
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database/application/field/field_controller.dart'; import 'package:appflowy/plugins/database/application/field/field_controller.dart';
@ -12,12 +10,14 @@ import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart';
import 'package:appflowy/plugins/database/grid/presentation/widgets/common/type_option_separator.dart'; import 'package:appflowy/plugins/database/grid/presentation/widgets/common/type_option_separator.dart';
import 'package:appflowy/util/field_type_extension.dart'; import 'package:appflowy/util/field_type_extension.dart';
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart'; import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart';
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle_style.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:styled_widget/styled_widget.dart';
import 'field_type_list.dart'; import 'field_type_list.dart';
import 'type_option_editor/builder.dart'; import 'type_option_editor/builder.dart';
@ -87,10 +87,12 @@ class _FieldEditorState extends State<FieldEditor> {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
FieldNameTextField( FieldNameTextField(
padding: const EdgeInsets.fromLTRB(4, 4, 4, 8), padding: const EdgeInsets.fromLTRB(12, 12, 12, 8),
textEditingController: textController, textEditingController: textController,
), ),
VSpace(GridSize.typeOptionSeparatorHeight),
_EditFieldButton( _EditFieldButton(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
onTap: () { onTap: () {
setState(() => _currentPage = FieldEditorPage.details); setState(() => _currentPage = FieldEditorPage.details);
}, },
@ -107,8 +109,11 @@ class _FieldEditorState extends State<FieldEditor> {
_actionCell(FieldAction.clearData), _actionCell(FieldAction.clearData),
VSpace(GridSize.typeOptionSeparatorHeight), VSpace(GridSize.typeOptionSeparatorHeight),
_actionCell(FieldAction.delete), _actionCell(FieldAction.delete),
const TypeOptionSeparator(spacing: 8.0),
_actionCell(FieldAction.wrap),
const VSpace(8.0),
], ],
).padding(all: 8.0), ),
); );
} }
@ -124,24 +129,32 @@ class _FieldEditorState extends State<FieldEditor> {
Widget _actionCell(FieldAction action) { Widget _actionCell(FieldAction action) {
return BlocBuilder<FieldEditorBloc, FieldEditorState>( return BlocBuilder<FieldEditorBloc, FieldEditorState>(
builder: (context, state) => FieldActionCell( builder: (context, state) => Padding(
viewId: widget.viewId, padding: const EdgeInsets.symmetric(horizontal: 8.0),
fieldInfo: state.field, child: FieldActionCell(
action: action, viewId: widget.viewId,
fieldInfo: state.field,
action: action,
),
), ),
); );
} }
} }
class _EditFieldButton extends StatelessWidget { class _EditFieldButton extends StatelessWidget {
const _EditFieldButton({this.onTap}); const _EditFieldButton({
required this.padding,
this.onTap,
});
final EdgeInsetsGeometry padding;
final void Function()? onTap; final void Function()? onTap;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SizedBox( return Container(
height: GridSize.popoverItemHeight, height: GridSize.popoverItemHeight,
padding: padding,
child: FlowyButton( child: FlowyButton(
leftIcon: const FlowySvg(FlowySvgs.edit_s), leftIcon: const FlowySvg(FlowySvgs.edit_s),
text: FlowyText.medium( text: FlowyText.medium(
@ -184,10 +197,11 @@ class FieldActionCell extends StatelessWidget {
), ),
onHover: (_) => popoverMutex?.close(), onHover: (_) => popoverMutex?.close(),
onTap: () => action.run(context, viewId, fieldInfo), onTap: () => action.run(context, viewId, fieldInfo),
leftIcon: action.icon( leftIcon: action.leading(
fieldInfo, fieldInfo,
enable ? null : Theme.of(context).disabledColor, enable ? null : Theme.of(context).disabledColor,
), ),
rightIcon: action.trailing(context, fieldInfo),
); );
} }
} }
@ -198,10 +212,11 @@ enum FieldAction {
toggleVisibility, toggleVisibility,
duplicate, duplicate,
clearData, clearData,
delete; delete,
wrap;
Widget icon(FieldInfo fieldInfo, Color? color) { Widget? leading(FieldInfo fieldInfo, Color? color) {
late final FlowySvgData svgData; FlowySvgData? svgData;
switch (this) { switch (this) {
case FieldAction.insertLeft: case FieldAction.insertLeft:
svgData = FlowySvgs.arrow_s; svgData = FlowySvgs.arrow_s;
@ -220,6 +235,11 @@ enum FieldAction {
svgData = FlowySvgs.reload_s; svgData = FlowySvgs.reload_s;
case FieldAction.delete: case FieldAction.delete:
svgData = FlowySvgs.delete_s; svgData = FlowySvgs.delete_s;
default:
}
if (svgData == null) {
return null;
} }
final icon = FlowySvg( final icon = FlowySvg(
svgData, svgData,
@ -231,6 +251,21 @@ enum FieldAction {
: icon; : icon;
} }
Widget? trailing(BuildContext context, FieldInfo fieldInfo) {
if (this == FieldAction.wrap) {
return Toggle(
value: fieldInfo.wrapCellContent ?? false,
onChanged: (_) => context
.read<FieldEditorBloc>()
.add(const FieldEditorEvent.toggleWrapCellContent()),
style: ToggleStyle.big,
padding: EdgeInsets.zero,
);
}
return null;
}
String title(FieldInfo fieldInfo) { String title(FieldInfo fieldInfo) {
switch (this) { switch (this) {
case FieldAction.insertLeft: case FieldAction.insertLeft:
@ -250,6 +285,8 @@ enum FieldAction {
return LocaleKeys.grid_field_clear.tr(); return LocaleKeys.grid_field_clear.tr();
case FieldAction.delete: case FieldAction.delete:
return LocaleKeys.grid_field_delete.tr(); return LocaleKeys.grid_field_delete.tr();
case FieldAction.wrap:
return LocaleKeys.grid_field_wrap.tr();
} }
} }
@ -308,6 +345,11 @@ enum FieldAction {
).show(context); ).show(context);
PopoverContainer.of(context).close(); PopoverContainer.of(context).close();
break; break;
case FieldAction.wrap:
context
.read<FieldEditorBloc>()
.add(const FieldEditorEvent.toggleWrapCellContent());
break;
} }
} }
} }

View File

@ -117,7 +117,7 @@ class _GridCellEnterRegion extends StatelessWidget {
onExit: (p) => onExit: (p) =>
CellContainerNotifier.of(context, listen: false).isHover = false, CellContainerNotifier.of(context, listen: false).isHover = false,
child: Stack( child: Stack(
alignment: AlignmentDirectional.center, alignment: Alignment.center,
fit: StackFit.expand, fit: StackFit.expand,
children: children, children: children,
), ),

View File

@ -653,6 +653,7 @@
"insertRight": "Insert Right", "insertRight": "Insert Right",
"duplicate": "Duplicate", "duplicate": "Duplicate",
"delete": "Delete", "delete": "Delete",
"wrap": "Wrap",
"clear": "Clear cells", "clear": "Clear cells",
"textFieldName": "Text", "textFieldName": "Text",
"checkboxFieldName": "Checkbox", "checkboxFieldName": "Checkbox",
@ -1474,4 +1475,4 @@
"betaTooltip": "We currently only support searching for pages", "betaTooltip": "We currently only support searching for pages",
"fromTrashHint": "From trash" "fromTrashHint": "From trash"
} }
} }

View File

@ -1,11 +1,13 @@
use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_error::ErrorCode; use flowy_error::ErrorCode;
use lib_infra::validator_fn::required_not_empty_str;
use std::ops::Deref; use std::ops::Deref;
use validator::Validate;
use crate::entities::parser::NotEmptyStr; use crate::entities::parser::NotEmptyStr;
use crate::entities::RepeatedFieldIdPB; use crate::entities::RepeatedFieldIdPB;
use crate::impl_into_field_visibility; use crate::impl_into_field_visibility;
use crate::services::field_settings::{FieldSettings, FieldSettingsChangesetParams}; use crate::services::field_settings::FieldSettings;
/// Defines the field settings for a field in a view. /// Defines the field settings for a field in a view.
#[derive(Debug, Default, Clone, ProtoBuf, Eq, PartialEq)] #[derive(Debug, Default, Clone, ProtoBuf, Eq, PartialEq)]
@ -18,6 +20,9 @@ pub struct FieldSettingsPB {
#[pb(index = 3)] #[pb(index = 3)]
pub width: i32, pub width: i32,
#[pb(index = 4)]
pub wrap_cell_content: bool,
} }
impl From<FieldSettings> for FieldSettingsPB { impl From<FieldSettings> for FieldSettingsPB {
@ -26,6 +31,7 @@ impl From<FieldSettings> for FieldSettingsPB {
field_id: value.field_id, field_id: value.field_id,
visibility: value.visibility, visibility: value.visibility,
width: value.width, width: value.width,
wrap_cell_content: value.wrap_cell_content,
} }
} }
} }
@ -93,11 +99,13 @@ impl std::convert::From<Vec<FieldSettingsPB>> for RepeatedFieldSettingsPB {
} }
} }
#[derive(Debug, Default, Clone, ProtoBuf)] #[derive(Debug, Default, Clone, ProtoBuf, Validate)]
pub struct FieldSettingsChangesetPB { pub struct FieldSettingsChangesetPB {
#[validate(custom = "required_not_empty_str")]
#[pb(index = 1)] #[pb(index = 1)]
pub view_id: String, pub view_id: String,
#[validate(custom = "required_not_empty_str")]
#[pb(index = 2)] #[pb(index = 2)]
pub field_id: String, pub field_id: String,
@ -106,28 +114,7 @@ pub struct FieldSettingsChangesetPB {
#[pb(index = 4, one_of)] #[pb(index = 4, one_of)]
pub width: Option<i32>, pub width: Option<i32>,
}
impl From<FieldSettingsChangesetParams> for FieldSettingsChangesetPB { #[pb(index = 5, one_of)]
fn from(value: FieldSettingsChangesetParams) -> Self { pub wrap_cell_content: Option<bool>,
Self {
view_id: value.view_id,
field_id: value.field_id,
visibility: value.visibility,
width: value.width,
}
}
}
impl TryFrom<FieldSettingsChangesetPB> for FieldSettingsChangesetParams {
type Error = ErrorCode;
fn try_from(value: FieldSettingsChangesetPB) -> Result<Self, Self::Error> {
Ok(FieldSettingsChangesetParams {
view_id: value.view_id,
field_id: value.field_id,
visibility: value.visibility,
width: value.width,
})
}
} }

View File

@ -14,7 +14,6 @@ use crate::services::field::{
type_option_data_from_pb, ChecklistCellChangeset, DateCellChangeset, RelationCellChangeset, type_option_data_from_pb, ChecklistCellChangeset, DateCellChangeset, RelationCellChangeset,
SelectOptionCellChangeset, SelectOptionCellChangeset,
}; };
use crate::services::field_settings::FieldSettingsChangesetParams;
use crate::services::group::GroupChangeset; use crate::services::group::GroupChangeset;
use crate::services::share::csv::CSVFormat; use crate::services::share::csv::CSVFormat;
@ -944,7 +943,7 @@ pub(crate) async fn update_field_settings_handler(
manager: AFPluginState<Weak<DatabaseManager>>, manager: AFPluginState<Weak<DatabaseManager>>,
) -> FlowyResult<()> { ) -> FlowyResult<()> {
let manager = upgrade_manager(manager)?; let manager = upgrade_manager(manager)?;
let params: FieldSettingsChangesetParams = data.into_inner().try_into()?; let params = data.try_into_inner()?;
let database_editor = manager.get_database_with_view_id(&params.view_id).await?; let database_editor = manager.get_database_with_view_id(&params.view_id).await?;
database_editor database_editor
.update_field_settings_with_changeset(params) .update_field_settings_with_changeset(params)

View File

@ -12,9 +12,7 @@ use crate::services::field::{
type_option_data_from_pb, ChecklistCellChangeset, RelationTypeOption, SelectOptionCellChangeset, type_option_data_from_pb, ChecklistCellChangeset, RelationTypeOption, SelectOptionCellChangeset,
StrCellData, TimestampCellData, TypeOptionCellDataHandler, TypeOptionCellExt, StrCellData, TimestampCellData, TypeOptionCellDataHandler, TypeOptionCellExt,
}; };
use crate::services::field_settings::{ use crate::services::field_settings::{default_field_settings_by_layout_map, FieldSettings};
default_field_settings_by_layout_map, FieldSettings, FieldSettingsChangesetParams,
};
use crate::services::filter::{Filter, FilterChangeset}; use crate::services::filter::{Filter, FilterChangeset};
use crate::services::group::{default_group_setting, GroupChangeset, GroupSetting, RowChangeset}; use crate::services::group::{default_group_setting, GroupChangeset, GroupSetting, RowChangeset};
use crate::services::share::csv::{CSVExport, CSVFormat}; use crate::services::share::csv::{CSVExport, CSVFormat};
@ -1296,17 +1294,10 @@ impl DatabaseEditor {
pub async fn update_field_settings_with_changeset( pub async fn update_field_settings_with_changeset(
&self, &self,
params: FieldSettingsChangesetParams, params: FieldSettingsChangesetPB,
) -> FlowyResult<()> { ) -> FlowyResult<()> {
let view = self.database_views.get_view_editor(&params.view_id).await?; let view = self.database_views.get_view_editor(&params.view_id).await?;
view view.v_update_field_settings(params).await?;
.v_update_field_settings(
&params.view_id,
&params.field_id,
params.visibility,
params.width,
)
.await?;
Ok(()) Ok(())
} }
@ -1735,45 +1726,43 @@ impl DatabaseViewOperation for DatabaseViewOperationImpl {
field_settings field_settings
} }
fn update_field_settings( fn update_field_settings(&self, params: FieldSettingsChangesetPB) {
&self, let field_settings_map = self.get_field_settings(&params.view_id, &[params.field_id.clone()]);
view_id: &str,
field_id: &str,
visibility: Option<FieldVisibility>,
width: Option<i32>,
) {
let field_settings_map = self.get_field_settings(view_id, &[field_id.to_string()]);
let new_field_settings = if let Some(field_settings) = field_settings_map.get(field_id) { let field_settings = field_settings_map
FieldSettings { .get(&params.field_id)
field_id: field_settings.field_id.clone(), .cloned()
visibility: visibility.unwrap_or(field_settings.visibility.clone()), .unwrap_or_else(|| {
width: width.unwrap_or(field_settings.width), let layout_type = self.get_layout_for_view(&params.view_id);
} let default_field_settings = default_field_settings_by_layout_map();
} else { let default_field_settings = default_field_settings.get(&layout_type).unwrap();
let layout_type = self.get_layout_for_view(view_id);
let default_field_settings = default_field_settings_by_layout_map() FieldSettings::from_any_map(&params.field_id, layout_type, default_field_settings)
.get(&layout_type) });
.unwrap()
.to_owned(); let new_field_settings = FieldSettings {
let field_settings = visibility: params
FieldSettings::from_any_map(field_id, layout_type, &default_field_settings); .visibility
FieldSettings { .unwrap_or_else(|| field_settings.visibility.clone()),
field_id: field_settings.field_id.clone(), width: params.width.unwrap_or(field_settings.width),
visibility: visibility.unwrap_or(field_settings.visibility), wrap_cell_content: params
width: width.unwrap_or(field_settings.width), .wrap_cell_content
} .unwrap_or(field_settings.wrap_cell_content),
..field_settings
}; };
self.database.lock().update_field_settings( self.database.lock().update_field_settings(
view_id, &params.view_id,
Some(vec![field_id.to_string()]), Some(vec![params.field_id]),
new_field_settings.clone(), new_field_settings.clone(),
); );
send_notification(view_id, DatabaseNotification::DidUpdateFieldSettings) send_notification(
.payload(FieldSettingsPB::from(new_field_settings)) &params.view_id,
.send() DatabaseNotification::DidUpdateFieldSettings,
)
.payload(FieldSettingsPB::from(new_field_settings))
.send()
} }
fn update_calculation(&self, view_id: &str, calculation: Calculation) { fn update_calculation(&self, view_id: &str, calculation: Calculation) {

View File

@ -15,10 +15,10 @@ use lib_dispatch::prelude::af_spawn;
use crate::entities::{ use crate::entities::{
CalendarEventPB, CreateRowParams, CreateRowPayloadPB, DatabaseLayoutMetaPB, CalendarEventPB, CreateRowParams, CreateRowPayloadPB, DatabaseLayoutMetaPB,
DatabaseLayoutSettingPB, DeleteSortPayloadPB, FieldType, FieldVisibility, GroupChangesPB, DatabaseLayoutSettingPB, DeleteSortPayloadPB, FieldSettingsChangesetPB, FieldType,
GroupPB, LayoutSettingChangeset, LayoutSettingParams, RemoveCalculationChangesetPB, GroupChangesPB, GroupPB, LayoutSettingChangeset, LayoutSettingParams,
ReorderSortPayloadPB, RowMetaPB, RowsChangePB, SortChangesetNotificationPB, SortPB, RemoveCalculationChangesetPB, ReorderSortPayloadPB, RowMetaPB, RowsChangePB,
UpdateCalculationChangesetPB, UpdateSortPayloadPB, SortChangesetNotificationPB, SortPB, UpdateCalculationChangesetPB, UpdateSortPayloadPB,
}; };
use crate::notification::{send_notification, DatabaseNotification}; use crate::notification::{send_notification, DatabaseNotification};
use crate::services::calculations::{Calculation, CalculationChangeset, CalculationsController}; use crate::services::calculations::{Calculation, CalculationChangeset, CalculationsController};
@ -1034,20 +1034,8 @@ impl DatabaseViewEditor {
self.delegate.get_field_settings(&self.view_id, field_ids) self.delegate.get_field_settings(&self.view_id, field_ids)
} }
// pub async fn v_get_all_field_settings(&self) -> HashMap<String, FieldSettings> { pub async fn v_update_field_settings(&self, params: FieldSettingsChangesetPB) -> FlowyResult<()> {
// self.delegate.get_all_field_settings(&self.view_id) self.delegate.update_field_settings(params);
// }
pub async fn v_update_field_settings(
&self,
view_id: &str,
field_id: &str,
visibility: Option<FieldVisibility>,
width: Option<i32>,
) -> FlowyResult<()> {
self
.delegate
.update_field_settings(view_id, field_id, visibility, width);
Ok(()) Ok(())
} }

View File

@ -11,7 +11,7 @@ use flowy_error::FlowyError;
use lib_infra::future::{Fut, FutureResult}; use lib_infra::future::{Fut, FutureResult};
use lib_infra::priority_task::TaskDispatcher; use lib_infra::priority_task::TaskDispatcher;
use crate::entities::{FieldType, FieldVisibility}; use crate::entities::{FieldSettingsChangesetPB, FieldType};
use crate::services::calculations::Calculation; use crate::services::calculations::Calculation;
use crate::services::field::TypeOptionCellDataHandler; use crate::services::field::TypeOptionCellDataHandler;
use crate::services::field_settings::FieldSettings; use crate::services::field_settings::FieldSettings;
@ -126,11 +126,5 @@ pub trait DatabaseViewOperation: Send + Sync + 'static {
field_ids: &[String], field_ids: &[String],
) -> HashMap<String, FieldSettings>; ) -> HashMap<String, FieldSettings>;
fn update_field_settings( fn update_field_settings(&self, params: FieldSettingsChangesetPB);
&self,
view_id: &str,
field_id: &str,
visibility: Option<FieldVisibility>,
width: Option<i32>,
);
} }

View File

@ -10,12 +10,13 @@ pub struct FieldSettings {
pub field_id: String, pub field_id: String,
pub visibility: FieldVisibility, pub visibility: FieldVisibility,
pub width: i32, pub width: i32,
pub wrap_cell_content: bool,
} }
pub const VISIBILITY: &str = "visibility"; pub const VISIBILITY: &str = "visibility";
pub const WIDTH: &str = "width"; pub const WIDTH: &str = "width";
pub const DEFAULT_WIDTH: i32 = 150; pub const DEFAULT_WIDTH: i32 = 150;
pub const WRAP_CELL_CONTENT: &str = "wrap";
impl FieldSettings { impl FieldSettings {
pub fn from_any_map( pub fn from_any_map(
@ -31,11 +32,15 @@ impl FieldSettings {
.get_i64_value(WIDTH) .get_i64_value(WIDTH)
.map(|value| value as i32) .map(|value| value as i32)
.unwrap_or(DEFAULT_WIDTH); .unwrap_or(DEFAULT_WIDTH);
let wrap_cell_content = field_settings
.get_bool_value(WRAP_CELL_CONTENT)
.unwrap_or(false);
Self { Self {
field_id: field_id.to_string(), field_id: field_id.to_string(),
visibility, visibility,
width, width,
wrap_cell_content,
} }
} }
} }
@ -45,15 +50,7 @@ impl From<FieldSettings> for FieldSettingsMap {
FieldSettingsMapBuilder::new() FieldSettingsMapBuilder::new()
.insert_i64_value(VISIBILITY, field_settings.visibility.into()) .insert_i64_value(VISIBILITY, field_settings.visibility.into())
.insert_i64_value(WIDTH, field_settings.width as i64) .insert_i64_value(WIDTH, field_settings.width as i64)
.insert_bool_value(WRAP_CELL_CONTENT, field_settings.wrap_cell_content)
.build() .build()
} }
} }
/// Contains the changeset to a field's settings.
/// A `Some` value constitutes a change in that particular setting
pub struct FieldSettingsChangesetParams {
pub view_id: String,
pub field_id: String,
pub visibility: Option<FieldVisibility>,
pub width: Option<i32>,
}

View File

@ -7,7 +7,7 @@ use collab_database::views::{
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use crate::entities::FieldVisibility; use crate::entities::FieldVisibility;
use crate::services::field_settings::{FieldSettings, VISIBILITY}; use crate::services::field_settings::{FieldSettings, DEFAULT_WIDTH, VISIBILITY};
/// Helper struct to create a new field setting /// Helper struct to create a new field setting
pub struct FieldSettingsBuilder { pub struct FieldSettingsBuilder {
@ -19,8 +19,10 @@ impl FieldSettingsBuilder {
let field_settings = FieldSettings { let field_settings = FieldSettings {
field_id: field_id.to_string(), field_id: field_id.to_string(),
visibility: FieldVisibility::AlwaysShown, visibility: FieldVisibility::AlwaysShown,
width: 150, width: DEFAULT_WIDTH,
wrap_cell_content: false,
}; };
Self { Self {
inner: field_settings, inner: field_settings,
} }

View File

@ -1,5 +1,4 @@
use flowy_database2::entities::FieldVisibility; use flowy_database2::entities::{FieldSettingsChangesetPB, FieldVisibility};
use flowy_database2::services::field_settings::FieldSettingsChangesetParams;
use crate::database::database_editor::DatabaseEditorTest; use crate::database::database_editor::DatabaseEditorTest;
@ -60,11 +59,12 @@ impl FieldSettingsTest {
visibility: Option<FieldVisibility>, visibility: Option<FieldVisibility>,
width: Option<i32>, width: Option<i32>,
) { ) {
let params = FieldSettingsChangesetParams { let params = FieldSettingsChangesetPB {
view_id: self.view_id.clone(), view_id: self.view_id.clone(),
field_id, field_id,
visibility, visibility,
width, width,
wrap_cell_content: None,
}; };
let _ = self let _ = self
.editor .editor