Skip to content

Commit

Permalink
feat: open a mobile grid row as a page (#4000)
Browse files Browse the repository at this point in the history
* chore: restore text cursor color

* chore: open row as a card in mobile

* refactor: clean up code

* chore: code review

* chore: restore c++ shared library
  • Loading branch information
richardshiue authored Nov 27, 2023
1 parent 771dd99 commit cac3acd
Show file tree
Hide file tree
Showing 5 changed files with 353 additions and 92 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter/material.dart';
import 'package:linked_scroll_controller/linked_scroll_controller.dart';
import '../../application/field/field_controller.dart';
import '../../application/row/row_cache.dart';
import '../../application/row/row_controller.dart';
import '../application/grid_bloc.dart';
Expand Down Expand Up @@ -297,11 +296,14 @@ class _GridRows extends StatelessWidget {
final rowMeta = rowCache.getRow(rowId)?.rowMeta;

/// Return placeholder widget if the rowMeta is null.
if (rowMeta == null) return const SizedBox.shrink();
if (rowMeta == null) {
Log.warn('RowMeta is null for rowId: $rowId');
return const SizedBox.shrink();
}

final fieldController =
context.read<GridBloc>().databaseController.fieldController;
final dataController = RowController(
final rowController = RowController(
viewId: viewId,
rowMeta: rowMeta,
rowCache: rowCache,
Expand All @@ -313,15 +315,18 @@ class _GridRows extends StatelessWidget {
viewId: viewId,
index: index,
isDraggable: isDraggable,
dataController: dataController,
cellBuilder: GridCellBuilder(cellCache: dataController.cellCache),
dataController: rowController,
cellBuilder: GridCellBuilder(cellCache: rowController.cellCache),
openDetailPage: (context, cellBuilder) {
_openRowDetailPage(
context,
rowId,
fieldController,
rowCache,
cellBuilder,
FlowyOverlay.show(
context: context,
builder: (BuildContext context) {
return RowDetailPage(
cellBuilder: cellBuilder,
rowController: rowController,
fieldController: fieldController,
);
},
);
},
);
Expand All @@ -335,37 +340,6 @@ class _GridRows extends StatelessWidget {

return child;
}

void _openRowDetailPage(
BuildContext context,
RowId rowId,
FieldController fieldController,
RowCache rowCache,
GridCellBuilder cellBuilder,
) {
final rowMeta = rowCache.getRow(rowId)?.rowMeta;
// Most of the cases, the rowMeta should not be null.
if (rowMeta != null) {
final dataController = RowController(
viewId: viewId,
rowMeta: rowMeta,
rowCache: rowCache,
);

FlowyOverlay.show(
context: context,
builder: (BuildContext context) {
return RowDetailPage(
cellBuilder: cellBuilder,
rowController: dataController,
fieldController: fieldController,
);
},
);
} else {
Log.warn('RowMeta is null for rowId: $rowId');
}
}
}

class _WrapScrollView extends StatelessWidget {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/database/card/card_detail/mobile_card_detail_screen.dart';
import 'package:appflowy/plugins/database_view/application/database_controller.dart';
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
import 'package:appflowy/plugins/database_view/application/row/row_cache.dart';
import 'package:appflowy/plugins/database_view/application/row/row_controller.dart';
import 'package:appflowy/plugins/database_view/application/row/row_service.dart';
import 'package:appflowy/plugins/database_view/grid/application/grid_bloc.dart';
import 'package:appflowy/plugins/database_view/tab_bar/tab_bar_view.dart';
import 'package:appflowy/plugins/database_view/widgets/row/cell_builder.dart';
import 'package:appflowy/plugins/database_view/widgets/row/row_detail.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
import 'package:collection/collection.dart';
Expand All @@ -18,6 +17,7 @@ import 'package:flowy_infra_ui/style_widget/scrolling/styled_scrollview.dart';
import 'package:flowy_infra_ui/widget/error_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:linked_scroll_controller/linked_scroll_controller.dart';

import 'grid_page.dart';
Expand All @@ -26,7 +26,7 @@ import 'layout/layout.dart';
import 'layout/sizes.dart';
import 'widgets/footer/grid_footer.dart';
import 'widgets/header/grid_header.dart';
import 'widgets/row/row.dart';
import 'widgets/row/mobile_row.dart';
import 'widgets/shortcuts.dart';
import '../../widgets/setting/mobile_database_settings_button.dart';

Expand Down Expand Up @@ -299,32 +299,33 @@ class _GridRows extends StatelessWidget {
final rowCache = context.read<GridBloc>().getRowCache(rowId);
final rowMeta = rowCache.getRow(rowId)?.rowMeta;

/// Return placeholder widget if the rowMeta is null.
if (rowMeta == null) return const SizedBox.shrink();
if (rowMeta == null) {
Log.warn('RowMeta is null for rowId: $rowId');
return const SizedBox.shrink();
}

final fieldController =
context.read<GridBloc>().databaseController.fieldController;
final dataController = RowController(
final rowController = RowController(
viewId: viewId,
rowMeta: rowMeta,
rowCache: rowCache,
);

final child = GridRow(
final child = MobileGridRow(
key: ValueKey(rowMeta.id),
rowId: rowId,
viewId: viewId,
index: index,
isDraggable: isDraggable,
dataController: dataController,
cellBuilder: GridCellBuilder(cellCache: dataController.cellCache),
dataController: rowController,
cellBuilder: GridCellBuilder(cellCache: rowController.cellCache),
openDetailPage: (context, cellBuilder) {
_openRowDetailPage(
context,
rowId,
fieldController,
rowCache,
cellBuilder,
context.push(
MobileCardDetailScreen.routeName,
extra: {
MobileCardDetailScreen.argRowController: rowController,
MobileCardDetailScreen.argFieldController: fieldController,
},
);
},
);
Expand All @@ -338,37 +339,6 @@ class _GridRows extends StatelessWidget {

return child;
}

void _openRowDetailPage(
BuildContext context,
RowId rowId,
FieldController fieldController,
RowCache rowCache,
GridCellBuilder cellBuilder,
) {
final rowMeta = rowCache.getRow(rowId)?.rowMeta;
// Most of the cases, the rowMeta should not be null.
if (rowMeta != null) {
final dataController = RowController(
viewId: viewId,
rowMeta: rowMeta,
rowCache: rowCache,
);

FlowyOverlay.show(
context: context,
builder: (BuildContext context) {
return RowDetailPage(
cellBuilder: cellBuilder,
rowController: dataController,
fieldController: fieldController,
);
},
);
} else {
Log.warn('RowMeta is null for rowId: $rowId');
}
}
}

class _WrapScrollView extends StatelessWidget {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart';
import 'package:appflowy/plugins/database_view/application/row/row_controller.dart';
import 'package:appflowy/plugins/database_view/application/row/row_service.dart';
import 'package:appflowy/plugins/database_view/grid/application/row/row_bloc.dart';
import 'package:appflowy/plugins/database_view/widgets/row/accessory/cell_accessory.dart';
import 'package:appflowy/plugins/database_view/widgets/row/cell_builder.dart';
import 'package:appflowy/plugins/database_view/widgets/row/cells/mobile_cell_container.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

import '../../layout/sizes.dart';
import "package:appflowy/generated/locale_keys.g.dart";
import 'package:easy_localization/easy_localization.dart';

class MobileGridRow extends StatefulWidget {
final RowId viewId;
final RowId rowId;
final RowController dataController;
final GridCellBuilder cellBuilder;
final void Function(BuildContext, GridCellBuilder) openDetailPage;

final bool isDraggable;

const MobileGridRow({
super.key,
required this.viewId,
required this.rowId,
required this.dataController,
required this.cellBuilder,
required this.openDetailPage,
this.isDraggable = false,
});

@override
State<MobileGridRow> createState() => _MobileGridRowState();
}

class _MobileGridRowState extends State<MobileGridRow> {
late final RowBloc _rowBloc;

@override
void initState() {
super.initState();
_rowBloc = RowBloc(
rowId: widget.rowId,
dataController: widget.dataController,
viewId: widget.viewId,
)..add(const RowEvent.initial());
}

@override
Widget build(BuildContext context) {
return BlocProvider.value(
value: _rowBloc,
child: BlocBuilder<RowBloc, RowState>(
// The row need to rebuild when the cell count changes.
buildWhen: (p, c) => p.rowSource != c.rowSource,
builder: (context, state) {
return Row(
children: [
SizedBox(width: GridSize.leadingHeaderPadding),
Expanded(
child: RowContent(
builder: widget.cellBuilder,
onExpand: () => widget.openDetailPage(
context,
widget.cellBuilder,
),
),
),
],
);
},
),
);
}

@override
Future<void> dispose() async {
_rowBloc.close();
super.dispose();
}
}

class InsertRowButton extends StatelessWidget {
const InsertRowButton({super.key});

@override
Widget build(BuildContext context) {
return FlowyIconButton(
tooltipText: LocaleKeys.tooltip_addNewRow.tr(),
hoverColor: AFThemeExtension.of(context).lightGreyHover,
width: 20,
height: 30,
onPressed: () => context.read<RowBloc>().add(const RowEvent.createRow()),
iconPadding: const EdgeInsets.all(3),
icon: FlowySvg(
FlowySvgs.add_s,
color: Theme.of(context).colorScheme.tertiary,
),
);
}
}

class RowContent extends StatelessWidget {
final VoidCallback onExpand;
final GridCellBuilder builder;
const RowContent({
super.key,
required this.builder,
required this.onExpand,
});

@override
Widget build(BuildContext context) {
return BlocBuilder<RowBloc, RowState>(
buildWhen: (previous, current) =>
!listEquals(previous.cells, current.cells),
builder: (context, state) {
return IntrinsicHeight(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
..._makeCells(context, state.cellByFieldId),
_finalCellDecoration(context),
],
),
);
},
);
}

List<Widget> _makeCells(
BuildContext context,
CellContextByFieldId cellByFieldId,
) {
return cellByFieldId.values.map(
(cellId) {
final GridCellWidget child = builder.build(cellId);

return MobileCellContainer(
width: cellId.fieldInfo.fieldSettings?.width.toDouble() ?? 140,
isPrimary: cellId.fieldInfo.field.isPrimary,
accessoryBuilder: (buildContext) {
final builder = child.accessoryBuilder;
final List<GridCellAccessoryBuilder> accessories = [];
if (cellId.fieldInfo.field.isPrimary) {
accessories.add(
GridCellAccessoryBuilder(
builder: (key) => PrimaryCellAccessory(
key: key,
onTapCallback: onExpand,
isCellEditing: buildContext.isCellEditing,
),
),
);
}

if (builder != null) {
accessories.addAll(builder(buildContext));
}

return accessories;
},
child: child,
);
},
).toList();
}

Widget _finalCellDecoration(BuildContext context) {
return MouseRegion(
cursor: SystemMouseCursors.basic,
child: Container(
width: GridSize.trailHeaderPadding,
padding: GridSize.headerContentInsets,
constraints: const BoxConstraints(minHeight: 46),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(color: Theme.of(context).dividerColor),
),
),
),
);
}
}
Loading

0 comments on commit cac3acd

Please sign in to comment.