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(
reminderBloc: getIt<ReminderBloc>(),
cellController: widget.controller,
)..add(const DateCellEditorEvent.initial()),
),
),
],
child: BlocBuilder<DateCellEditorBloc, DateCellEditorState>(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,7 @@
import 'dart:async';
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:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@ -9,9 +10,11 @@ part 'select_option_cell_bloc.freezed.dart';
class SelectOptionCellBloc
extends Bloc<SelectOptionCellEvent, SelectOptionCellState> {
SelectOptionCellBloc({required this.cellController})
: super(SelectOptionCellState.initial(cellController)) {
SelectOptionCellBloc({
required this.cellController,
}) : super(SelectOptionCellState.initial(cellController)) {
_dispatch();
_startListening();
}
final SelectOptionCellController cellController;
@ -20,8 +23,10 @@ class SelectOptionCellBloc
@override
Future<void> close() async {
if (_onCellChangedFn != null) {
cellController.removeListener(_onCellChangedFn!);
_onCellChangedFn = null;
cellController.removeListener(
onCellChanged: _onCellChangedFn!,
onFieldChanged: _onFieldChangedListener,
);
}
await cellController.dispose();
return super.close();
@ -29,11 +34,8 @@ class SelectOptionCellBloc
void _dispatch() {
on<SelectOptionCellEvent>(
(event, emit) async {
await event.when(
initial: () async {
_startListening();
},
(event, emit) {
event.when(
didReceiveOptions: (List<SelectOptionPB> selectedOptions) {
emit(
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
class SelectOptionCellEvent with _$SelectOptionCellEvent {
const factory SelectOptionCellEvent.initial() = _InitialCell;
const factory SelectOptionCellEvent.didReceiveOptions(
List<SelectOptionPB> selectedOptions,
) = _DidReceiveOptions;
const factory SelectOptionCellEvent.didUpdateField(FieldInfo fieldInfo) =
_DidUpdateField;
}
@freezed
class SelectOptionCellState with _$SelectOptionCellState {
const factory SelectOptionCellState({
required List<SelectOptionPB> selectedOptions,
required bool wrap,
}) = _SelectOptionCellState;
factory SelectOptionCellState.initial(
SelectOptionCellController cellController,
) {
final data = cellController.getCellData();
final wrap = cellController.fieldInfo.wrapCellContent;
return SelectOptionCellState(
selectedOptions: data?.selectOptions ?? [],
wrap: wrap ?? true,
);
}
}

View File

@ -1,6 +1,7 @@
import 'dart:async';
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/type_option_data_parser.dart';
import 'package:appflowy/plugins/database/domain/field_service.dart';
@ -163,8 +164,10 @@ class SelectOptionCellEditorBloc
@override
Future<void> close() async {
if (_onCellChangedFn != null) {
cellController.removeListener(_onCellChangedFn!);
_onCellChangedFn = null;
cellController.removeListener(
onCellChanged: _onCellChangedFn!,
onFieldChanged: _onFieldChangedListener,
);
}
return super.close();
}
@ -172,27 +175,25 @@ class SelectOptionCellEditorBloc
void _startListening() {
_onCellChangedFn = cellController.addListener(
onCellChanged: (cellData) {
if (isClosed) {
Log.warn("Unexpecteded closing the bloc");
return;
if (!isClosed) {
add(
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({
required String name,
required SelectOptionColorPB color,

View File

@ -1,6 +1,7 @@
import 'dart:async';
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:freezed_annotation/freezed_annotation.dart';
@ -10,6 +11,7 @@ class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
TextCellBloc({required this.cellController})
: super(TextCellState.initial(cellController)) {
_dispatch();
_startListening();
}
final TextCellController cellController;
@ -18,8 +20,10 @@ class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
@override
Future<void> close() async {
if (_onCellChangedFn != null) {
cellController.removeListener(_onCellChangedFn!);
_onCellChangedFn = null;
cellController.removeListener(
onCellChanged: _onCellChangedFn!,
onFieldChanged: _onFieldChangedListener,
);
}
await cellController.dispose();
return super.close();
@ -29,12 +33,15 @@ class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
on<TextCellEvent>(
(event, emit) {
event.when(
initial: () {
_startListening();
},
didReceiveCellUpdate: (String content) {
emit(state.copyWith(content: content));
},
didUpdateField: (fieldInfo) {
final wrap = fieldInfo.wrapCellContent;
if (wrap != null) {
emit(state.copyWith(wrap: wrap));
}
},
didUpdateEmoji: (String emoji) {
emit(state.copyWith(emoji: emoji));
},
@ -58,20 +65,30 @@ class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
add(TextCellEvent.didReceiveCellUpdate(cellContent ?? ""));
}
},
onRowMetaChanged: () {
if (!isClosed && cellController.fieldInfo.isPrimary) {
add(TextCellEvent.didUpdateEmoji(cellController.icon ?? ""));
}
},
onFieldChanged: _onFieldChangedListener,
onRowMetaChanged: cellController.fieldInfo.isPrimary
? () {
if (!isClosed) {
add(TextCellEvent.didUpdateEmoji(cellController.icon ?? ""));
}
}
: null,
);
}
void _onFieldChangedListener(FieldInfo fieldInfo) {
if (!isClosed) {
add(TextCellEvent.didUpdateField(fieldInfo));
}
}
}
@freezed
class TextCellEvent with _$TextCellEvent {
const factory TextCellEvent.initial() = _InitialCell;
const factory TextCellEvent.didReceiveCellUpdate(String cellContent) =
_DidReceiveCellUpdate;
const factory TextCellEvent.didUpdateField(FieldInfo fieldInfo) =
_DidUpdateField;
const factory TextCellEvent.updateText(String text) = _UpdateText;
const factory TextCellEvent.enableEdit(bool enabled) = _EnableEdit;
const factory TextCellEvent.didUpdateEmoji(String emoji) = _UpdateEmoji;
@ -83,13 +100,20 @@ class TextCellState with _$TextCellState {
required String content,
required String emoji,
required bool enableEdit,
required bool wrap,
}) = _TextCellState;
factory TextCellState.initial(TextCellController cellController) =>
TextCellState(
content: cellController.getCellData() ?? "",
emoji:
cellController.fieldInfo.isPrimary ? cellController.icon ?? "" : "",
enableEdit: false,
);
factory TextCellState.initial(TextCellController cellController) {
final cellData = cellController.getCellData() ?? "";
final wrap = cellController.fieldInfo.wrapCellContent ?? false;
final emoji =
cellController.fieldInfo.isPrimary ? cellController.icon ?? "" : "";
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';
class TimestampCellBloc extends Bloc<TimestampCellEvent, TimestampCellState> {
TimestampCellBloc({required this.cellController})
: super(TimestampCellState.initial(cellController)) {
TimestampCellBloc({
required this.cellController,
}) : super(TimestampCellState.initial(cellController)) {
_dispatch();
_startListening();
}
final TimestampCellController cellController;
@ -20,8 +22,10 @@ class TimestampCellBloc extends Bloc<TimestampCellEvent, TimestampCellState> {
@override
Future<void> close() async {
if (_onCellChangedFn != null) {
cellController.removeListener(_onCellChangedFn!);
_onCellChangedFn = null;
cellController.removeListener(
onCellChanged: _onCellChangedFn!,
onFieldChanged: _onFieldChangedListener,
);
}
await cellController.dispose();
return super.close();
@ -31,7 +35,6 @@ class TimestampCellBloc extends Bloc<TimestampCellEvent, TimestampCellState> {
on<TimestampCellEvent>(
(event, emit) async {
event.when(
initial: () => _startListening(),
didReceiveCellUpdate: (TimestampCellDataPB? cellData) {
emit(
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));
}
},
onFieldChanged: _onFieldChangedListener,
);
}
void _onFieldChangedListener(FieldInfo fieldInfo) {
if (!isClosed) {
add(TimestampCellEvent.didUpdateField(fieldInfo));
}
}
}
@freezed
class TimestampCellEvent with _$TimestampCellEvent {
const factory TimestampCellEvent.initial() = _InitialCell;
const factory TimestampCellEvent.didReceiveCellUpdate(
TimestampCellDataPB? data,
) = _DidReceiveCellUpdate;
const factory TimestampCellEvent.didUpdateField(FieldInfo fieldInfo) =
_DidUpdateField;
}
@freezed
@ -70,15 +87,18 @@ class TimestampCellState with _$TimestampCellState {
required TimestampCellDataPB? data,
required String dateStr,
required FieldInfo fieldInfo,
required bool wrap,
}) = _TimestampCellState;
factory TimestampCellState.initial(TimestampCellController context) {
final cellData = context.getCellData();
factory TimestampCellState.initial(TimestampCellController cellController) {
final cellData = cellController.getCellData();
final wrap = cellController.fieldInfo.wrapCellContent;
return TimestampCellState(
fieldInfo: context.fieldInfo,
fieldInfo: cellController.fieldInfo,
data: cellData,
dateStr: cellData?.dateTime ?? "",
wrap: wrap ?? true,
);
}
}

View File

@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:io';
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:flutter_bloc/flutter_bloc.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';
class URLCellBloc extends Bloc<URLCellEvent, URLCellState> {
URLCellBloc({required this.cellController})
: super(URLCellState.initial(cellController)) {
URLCellBloc({
required this.cellController,
}) : super(URLCellState.initial(cellController)) {
_dispatch();
_startListening();
}
final URLCellController cellController;
@ -20,8 +23,10 @@ class URLCellBloc extends Bloc<URLCellEvent, URLCellState> {
@override
Future<void> close() async {
if (_onCellChangedFn != null) {
cellController.removeListener(_onCellChangedFn!);
_onCellChangedFn = null;
cellController.removeListener(
onCellChanged: _onCellChangedFn!,
onFieldChanged: _onFieldChangedListener,
);
}
await cellController.dispose();
return super.close();
@ -31,12 +36,9 @@ class URLCellBloc extends Bloc<URLCellEvent, URLCellState> {
on<URLCellEvent>(
(event, emit) async {
await event.when(
initial: () {
_startListening();
},
didReceiveCellUpdate: (cellData) async {
didUpdateCell: (cellData) async {
final content = cellData?.content ?? "";
final isValid = await isUrlValid(content);
final isValid = await _isUrlValid(content);
emit(
state.copyWith(
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) {
cellController.saveCellData(url, debounce: true);
},
@ -56,13 +64,20 @@ class URLCellBloc extends Bloc<URLCellEvent, URLCellState> {
_onCellChangedFn = cellController.addListener(
onCellChanged: (cellData) {
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) {
return true;
}
@ -90,10 +105,11 @@ class URLCellBloc extends Bloc<URLCellEvent, URLCellState> {
@freezed
class URLCellEvent with _$URLCellEvent {
const factory URLCellEvent.initial() = _InitialCell;
const factory URLCellEvent.updateURL(String url) = _UpdateURL;
const factory URLCellEvent.didReceiveCellUpdate(URLCellDataPB? cell) =
_DidReceiveCellUpdate;
const factory URLCellEvent.didUpdateCell(URLCellDataPB? cell) =
_DidUpdateCell;
const factory URLCellEvent.didUpdateField(FieldInfo fieldInfo) =
_DidUpdateField;
}
@freezed
@ -101,13 +117,16 @@ class URLCellState with _$URLCellState {
const factory URLCellState({
required String content,
required bool isValid,
required bool wrap,
}) = _URLCellState;
factory URLCellState.initial(URLCellController context) {
final cellData = context.getCellData();
factory URLCellState.initial(URLCellController cellController) {
final cellData = cellController.getCellData();
final wrap = cellController.fieldInfo.wrapCellContent;
return URLCellState(
content: cellData?.content ?? "",
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;
CellDataNotifier<T?>? _cellDataNotifier;
void Function(FieldInfo field)? _onCellFieldChanged;
VoidCallback? _onRowMetaChanged;
Timer? _loadDataOperation;
Timer? _saveDataOperation;
@ -104,16 +103,7 @@ class CellController<T, D> {
// 2. Listen on the field event and load the cell data if needed.
_fieldController.addSingleFieldListener(
fieldId,
onFieldChanged: (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();
}
_onCellFieldChanged?.call(fieldInfo);
},
onFieldChanged: _onFieldChangedListener,
);
// 3. If the field is primary listen to row meta changes.
@ -130,22 +120,49 @@ class CellController<T, D> {
/// Add a new listener
VoidCallback? addListener({
required void Function(T?) onCellChanged,
void Function(FieldInfo field)? onCellFieldChanged,
void Function(FieldInfo fieldInfo)? onFieldChanged,
VoidCallback? onRowMetaChanged,
}) {
_onCellFieldChanged = onCellFieldChanged;
_onRowMetaChanged = onRowMetaChanged;
/// Notify the listener, the cell data was changed.
/// an adaptor for the onCellChanged listener
void onCellChangedFn() => onCellChanged(_cellDataNotifier?.value);
_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 onCellChangedFn;
}
void removeListener(VoidCallback fn) {
_cellDataNotifier?.removeListener(fn);
void removeListener({
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,
@ -218,6 +235,11 @@ class CellController<T, D> {
await _cellListener?.stop();
_cellListener = null;
_fieldController.removeSingleFieldListener(
fieldId: fieldId,
onFieldChanged: _onFieldChangedListener,
);
_loadDataOperation?.cancel();
_saveDataOperation?.cancel();
_cellDataNotifier?.dispose();

View File

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

View File

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

View File

@ -99,6 +99,14 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
);
_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.toggleFieldVisibility() =
_ToggleFieldVisiblity;
const factory FieldEditorEvent.toggleWrapCellContent() =
_ToggleWrapCellContent;
}
@freezed

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -27,27 +27,15 @@ class DesktopGridDateCellSkin extends IEditableDateCellSkin {
direction: PopoverDirection.bottomWithLeftAligned,
constraints: BoxConstraints.loose(const Size(260, 620)),
margin: EdgeInsets.zero,
child: Container(
child: Align(
alignment: AlignmentDirectional.centerStart,
padding: GridSize.cellContentInsets,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Flexible(
child: FlowyText.medium(
state.dateStr,
overflow: TextOverflow.ellipsis,
child: state.fieldInfo.wrapCellContent ?? false
? _buildCellContent(state)
: SingleChildScrollView(
physics: const NeverScrollableScrollPhysics(),
scrollDirection: Axis.horizontal,
child: _buildCellContent(state),
),
),
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) {
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/application/cell/bloc/number_cell_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../editable_cell_skeleton/number.dart';
@ -19,7 +20,7 @@ class DesktopGridNumberCellSkin extends IEditableNumberCellSkin {
focusNode: focusNode,
onEditingComplete: () => focusNode.unfocus(),
onSubmitted: (_) => focusNode.unfocus(),
maxLines: null,
maxLines: context.watch<NumberCellBloc>().state.wrap ? null : 1,
style: Theme.of(context).textTheme.bodyMedium,
textInputAction: TextInputAction.done,
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/cell_editor/relation_cell_editor.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:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
@ -32,13 +33,52 @@ class DesktopGridRelationCellSkin extends IEditableRelationCellSkin {
child: const RelationCellEditor(),
);
},
child: Container(
child: Align(
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,
child: Wrap(
runSpacing: 4.0,
spacing: 4.0,
children: state.rows.map(
child: SeparatedRow(
separatorBuilder: () => const HSpace(4.0),
mainAxisSize: MainAxisSize.min,
children: rows.map(
(row) {
final isEmpty = row.name.isEmpty;
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/application/cell/bloc/select_option_cell_bloc.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:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/widgets.dart';
@ -23,6 +23,7 @@ class DesktopGridSelectOptionCellSkin extends IEditableSelectOptionCellSkin {
controller: popoverController,
constraints: const BoxConstraints.tightFor(width: 300),
margin: EdgeInsets.zero,
triggerActions: PopoverTriggerFlags.none,
direction: PopoverDirection.bottomWithLeftAligned,
popupBuilder: (BuildContext popoverContext) {
return SelectOptionCellEditor(
@ -32,35 +33,67 @@ class DesktopGridSelectOptionCellSkin extends IEditableSelectOptionCellSkin {
onClose: () => cellContainerNotifier.isFocus = false,
child: BlocBuilder<SelectOptionCellBloc, SelectOptionCellState>(
builder: (context, state) {
return Container(
return Align(
alignment: AlignmentDirectional.centerStart,
padding: GridSize.cellContentInsets,
child: state.selectedOptions.isEmpty
? const SizedBox.shrink()
: _buildOptions(context, state.selectedOptions),
child: state.wrap
? _buildWrapOptions(context, state.selectedOptions)
: _buildNoWrapOptions(context, state.selectedOptions),
);
},
),
);
}
Widget _buildOptions(context, List<SelectOptionPB> options) {
return Wrap(
runSpacing: 4,
children: options.map(
(option) {
return Padding(
padding: const EdgeInsets.only(right: 4),
child: SelectOptionTag(
option: option,
padding: const EdgeInsets.symmetric(
vertical: 1,
horizontal: 8,
Widget _buildWrapOptions(BuildContext context, List<SelectOptionPB> options) {
return Padding(
padding: GridSize.cellContentInsets,
child: Wrap(
runSpacing: 4,
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(),
);
},
).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(
controller: textEditingController,
focusNode: focusNode,
maxLines: null,
maxLines: context.watch<TextCellBloc>().state.wrap ? null : 1,
style: Theme.of(context).textTheme.bodyMedium,
decoration: const InputDecoration(
border: InputBorder.none,

View File

@ -16,10 +16,23 @@ class DesktopGridTimestampCellSkin extends IEditableTimestampCellSkin {
) {
return Container(
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,
child: FlowyText.medium(
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:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../editable_cell_skeleton/url.dart';
@ -24,28 +25,31 @@ class DesktopGridURLSkin extends IEditableURLCellSkin {
TextEditingController textEditingController,
URLCellDataNotifier cellDataNotifier,
) {
return TextField(
controller: textEditingController,
focusNode: focusNode,
maxLines: null,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.primary,
decoration: TextDecoration.underline,
),
decoration: InputDecoration(
contentPadding: GridSize.cellContentInsets,
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
errorBorder: InputBorder.none,
disabledBorder: InputBorder.none,
hintStyle: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(color: Theme.of(context).hintColor),
isDense: true,
return BlocSelector<URLCellBloc, URLCellState, bool>(
selector: (state) => state.wrap,
builder: (context, wrap) => TextField(
controller: textEditingController,
focusNode: focusNode,
maxLines: wrap ? null : 1,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.primary,
decoration: TextDecoration.underline,
),
decoration: InputDecoration(
contentPadding: GridSize.cellContentInsets,
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
errorBorder: InputBorder.none,
disabledBorder: InputBorder.none,
hintStyle: Theme.of(context)
.textTheme
.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;
@override
void onTap() =>
openUrlCellLink(widget.cellDataNotifier.value);
void onTap() => openUrlCellLink(widget.cellDataNotifier.value);
}
class _URLAccessoryIconContainer extends StatelessWidget {
@ -190,9 +193,8 @@ class _URLAccessoryIconContainer extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SizedBox(
width: 26,
height: 26,
return SizedBox.square(
dimension: 26,
child: Padding(
padding: const EdgeInsets.all(3.0),
child: child,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,11 +1,13 @@
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_error::ErrorCode;
use lib_infra::validator_fn::required_not_empty_str;
use std::ops::Deref;
use validator::Validate;
use crate::entities::parser::NotEmptyStr;
use crate::entities::RepeatedFieldIdPB;
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.
#[derive(Debug, Default, Clone, ProtoBuf, Eq, PartialEq)]
@ -18,6 +20,9 @@ pub struct FieldSettingsPB {
#[pb(index = 3)]
pub width: i32,
#[pb(index = 4)]
pub wrap_cell_content: bool,
}
impl From<FieldSettings> for FieldSettingsPB {
@ -26,6 +31,7 @@ impl From<FieldSettings> for FieldSettingsPB {
field_id: value.field_id,
visibility: value.visibility,
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 {
#[validate(custom = "required_not_empty_str")]
#[pb(index = 1)]
pub view_id: String,
#[validate(custom = "required_not_empty_str")]
#[pb(index = 2)]
pub field_id: String,
@ -106,28 +114,7 @@ pub struct FieldSettingsChangesetPB {
#[pb(index = 4, one_of)]
pub width: Option<i32>,
}
impl From<FieldSettingsChangesetParams> for FieldSettingsChangesetPB {
fn from(value: FieldSettingsChangesetParams) -> Self {
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,
})
}
#[pb(index = 5, one_of)]
pub wrap_cell_content: Option<bool>,
}

View File

@ -14,7 +14,6 @@ use crate::services::field::{
type_option_data_from_pb, ChecklistCellChangeset, DateCellChangeset, RelationCellChangeset,
SelectOptionCellChangeset,
};
use crate::services::field_settings::FieldSettingsChangesetParams;
use crate::services::group::GroupChangeset;
use crate::services::share::csv::CSVFormat;
@ -944,7 +943,7 @@ pub(crate) async fn update_field_settings_handler(
manager: AFPluginState<Weak<DatabaseManager>>,
) -> FlowyResult<()> {
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?;
database_editor
.update_field_settings_with_changeset(params)

View File

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

View File

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

View File

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

View File

@ -10,12 +10,13 @@ pub struct FieldSettings {
pub field_id: String,
pub visibility: FieldVisibility,
pub width: i32,
pub wrap_cell_content: bool,
}
pub const VISIBILITY: &str = "visibility";
pub const WIDTH: &str = "width";
pub const DEFAULT_WIDTH: i32 = 150;
pub const WRAP_CELL_CONTENT: &str = "wrap";
impl FieldSettings {
pub fn from_any_map(
@ -31,11 +32,15 @@ impl FieldSettings {
.get_i64_value(WIDTH)
.map(|value| value as i32)
.unwrap_or(DEFAULT_WIDTH);
let wrap_cell_content = field_settings
.get_bool_value(WRAP_CELL_CONTENT)
.unwrap_or(false);
Self {
field_id: field_id.to_string(),
visibility,
width,
wrap_cell_content,
}
}
}
@ -45,15 +50,7 @@ impl From<FieldSettings> for FieldSettingsMap {
FieldSettingsMapBuilder::new()
.insert_i64_value(VISIBILITY, field_settings.visibility.into())
.insert_i64_value(WIDTH, field_settings.width as i64)
.insert_bool_value(WRAP_CELL_CONTENT, field_settings.wrap_cell_content)
.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 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
pub struct FieldSettingsBuilder {
@ -19,8 +19,10 @@ impl FieldSettingsBuilder {
let field_settings = FieldSettings {
field_id: field_id.to_string(),
visibility: FieldVisibility::AlwaysShown,
width: 150,
width: DEFAULT_WIDTH,
wrap_cell_content: false,
};
Self {
inner: field_settings,
}

View File

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