chore: enable relation to (#4866)

* chore: enable relation to

* chore: fix database name and improve UI

* chore: remove database view id from relation type option

* chore: add remove row id test

* chore: improve appearance of untitled rows

* chore: empty in row detail

* fix: cannot add events after closing

---------

Co-authored-by: Richard Shiue <71320345+richardshiue@users.noreply.github.com>
This commit is contained in:
Nathan.fooo
2024-03-15 22:58:55 +08:00
committed by GitHub
parent 8d01d54e7f
commit bb414c3fd6
19 changed files with 459 additions and 231 deletions

View File

@ -1,8 +1,9 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database/application/cell/cell_controller.dart';
import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart';
import 'package:appflowy/plugins/database/application/database_controller.dart';
import 'package:appflowy/plugins/database/application/cell/bloc/relation_cell_bloc.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@ -52,15 +53,19 @@ class _RelationCellState extends State<RelationCardCell> {
return const SizedBox.shrink();
}
final children = state.rows
.map(
(row) => FlowyText.medium(
row.name,
final children = state.rows.map(
(row) {
final isEmpty = row.name.isEmpty;
return Text(
isEmpty ? LocaleKeys.grid_row_titlePlaceholder.tr() : row.name,
style: widget.style.textStyle.copyWith(
color: isEmpty ? Theme.of(context).hintColor : null,
decoration: TextDecoration.underline,
overflow: TextOverflow.ellipsis,
),
)
.toList();
overflow: TextOverflow.ellipsis,
);
},
).toList();
return Container(
alignment: AlignmentDirectional.topStart,

View File

@ -1,10 +1,12 @@
import 'package:appflowy/generated/locale_keys.g.dart';
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_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../editable_cell_skeleton/relation.dart';
@ -29,10 +31,6 @@ class DesktopGridRelationCellSkin extends IEditableRelationCellSkin {
value: bloc,
child: RelationCellEditor(
selectedRowIds: state.rows.map((row) => row.rowId).toList(),
databaseId: state.relatedDatabaseId,
onSelectRow: (rowId) {
bloc.add(RelationCellEvent.selectRow(rowId));
},
),
);
},
@ -42,15 +40,17 @@ class DesktopGridRelationCellSkin extends IEditableRelationCellSkin {
child: Wrap(
runSpacing: 4.0,
spacing: 4.0,
children: state.rows
.map(
(row) => FlowyText.medium(
row.name,
decoration: TextDecoration.underline,
overflow: TextOverflow.ellipsis,
),
)
.toList(),
children: state.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(),
),
),
);

View File

@ -1,9 +1,12 @@
import 'package:appflowy/generated/locale_keys.g.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';
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../editable_cell_skeleton/relation.dart';
@ -26,36 +29,43 @@ class DesktopRowDetailRelationCellSkin extends IEditableRelationCellSkin {
popupBuilder: (context) {
return BlocProvider.value(
value: bloc,
child: BlocBuilder<RelationCellBloc, RelationCellState>(
builder: (context, state) => RelationCellEditor(
selectedRowIds: state.rows.map((row) => row.rowId).toList(),
databaseId: state.relatedDatabaseId,
onSelectRow: (rowId) {
context
.read<RelationCellBloc>()
.add(RelationCellEvent.selectRow(rowId));
},
),
child: RelationCellEditor(
selectedRowIds: state.rows.map((row) => row.rowId).toList(),
),
);
},
child: Container(
alignment: AlignmentDirectional.centerStart,
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
child: Wrap(
runSpacing: 4.0,
spacing: 4.0,
children: state.rows
.map(
(row) => FlowyText.medium(
row.name,
decoration: TextDecoration.underline,
overflow: TextOverflow.ellipsis,
),
)
.toList(),
),
child: state.rows.isEmpty
? _buildPlaceholder(context)
: _buildRows(context, state.rows),
),
);
}
Widget _buildPlaceholder(BuildContext context) {
return FlowyText(
LocaleKeys.grid_row_textPlaceholder.tr(),
color: Theme.of(context).hintColor,
);
}
Widget _buildRows(BuildContext context, List<RelatedRowDataPB> rows) {
return Wrap(
runSpacing: 4.0,
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(),
);
}
}

View File

@ -1,5 +1,6 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database/application/field/type_option/relation_type_option_cubit.dart';
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:easy_localization/easy_localization.dart';
@ -13,38 +14,24 @@ import '../../application/cell/bloc/relation_row_search_bloc.dart';
class RelationCellEditor extends StatelessWidget {
const RelationCellEditor({
super.key,
required this.databaseId,
required this.selectedRowIds,
required this.onSelectRow,
});
final String databaseId;
final List<String> selectedRowIds;
final void Function(String rowId) onSelectRow;
@override
Widget build(BuildContext context) {
if (databaseId.isEmpty) {
// no i18n here because UX needs thorough checking.
return const Center(
child: FlowyText(
'''
No database has been selected,
please select one first in the field editor.
''',
maxLines: null,
textAlign: TextAlign.center,
),
);
}
return BlocBuilder<RelationCellBloc, RelationCellState>(
builder: (context, cellState) {
if (cellState.relatedDatabaseMeta == null) {
return const _RelationCellEditorDatabaseList();
}
return BlocProvider<RelationRowSearchBloc>(
create: (context) => RelationRowSearchBloc(
databaseId: databaseId,
),
child: BlocBuilder<RelationCellBloc, RelationCellState>(
builder: (context, cellState) {
return BlocBuilder<RelationRowSearchBloc, RelationRowSearchState>(
return BlocProvider<RelationRowSearchBloc>(
create: (context) => RelationRowSearchBloc(
databaseId: cellState.relatedDatabaseMeta!.databaseId,
),
child: BlocBuilder<RelationRowSearchBloc, RelationRowSearchState>(
builder: (context, state) {
final children = state.filteredRows
.map(
@ -68,7 +55,9 @@ please select one first in the field editor.
color: Theme.of(context).primaryColor,
)
: null,
onTap: () => onSelectRow(row.rowId),
onTap: () => context
.read<RelationCellBloc>()
.add(RelationCellEvent.selectRow(row.rowId)),
),
),
)
@ -78,7 +67,6 @@ please select one first in the field editor.
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const VSpace(6.0),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 6.0) +
GridSize.typeOptionContentInsets,
@ -90,15 +78,13 @@ please select one first in the field editor.
fontSize: 11,
color: Theme.of(context).hintColor,
),
const HSpace(2.0),
FlowyButton(
useIntrinsicWidth: true,
margin: const EdgeInsets.symmetric(
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 4,
vertical: 2,
),
text: FlowyText.regular(
cellState.relatedDatabaseId,
child: FlowyText.regular(
cellState.relatedDatabaseMeta!.databaseName,
fontSize: 11,
overflow: TextOverflow.ellipsis,
),
@ -106,10 +92,16 @@ please select one first in the field editor.
],
),
),
VSpace(GridSize.typeOptionSeparatorHeight),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 6.0),
child: FlowyTextField(
hintText: LocaleKeys
.grid_relation_rowSearchTextFieldPlaceholder
.tr(),
hintStyle: Theme.of(context)
.textTheme
.bodySmall
?.copyWith(color: Theme.of(context).hintColor),
onChanged: (text) => context
.read<RelationRowSearchBloc>()
.add(RelationRowSearchEvent.updateFilter(text)),
@ -140,6 +132,62 @@ please select one first in the field editor.
],
);
},
),
);
},
);
}
}
class _RelationCellEditorDatabaseList extends StatelessWidget {
const _RelationCellEditorDatabaseList();
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => RelationDatabaseListCubit(),
child: BlocBuilder<RelationDatabaseListCubit, RelationDatabaseListState>(
builder: (context, state) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(6, 6, 6, 0),
child: FlowyText(
LocaleKeys.grid_relation_noDatabaseSelected.tr(),
maxLines: null,
fontSize: 10,
color: Theme.of(context).hintColor,
),
),
Flexible(
child: ListView.separated(
padding: const EdgeInsets.all(6),
separatorBuilder: (context, index) =>
VSpace(GridSize.typeOptionSeparatorHeight),
itemCount: state.databaseMetas.length,
shrinkWrap: true,
itemBuilder: (context, index) {
final databaseMeta = state.databaseMetas[index];
return SizedBox(
height: GridSize.popoverItemHeight,
child: FlowyButton(
onTap: () => context.read<RelationCellBloc>().add(
RelationCellEvent.selectDatabaseId(
databaseMeta.databaseId,
),
),
text: FlowyText.medium(
databaseMeta.databaseName,
overflow: TextOverflow.ellipsis,
),
),
);
},
),
),
],
);
},
),