mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: disable moving page into the database (#3107)
This commit is contained in:
@ -2,7 +2,9 @@ import 'package:appflowy/plugins/database_view/board/presentation/board_page.dar
|
|||||||
import 'package:appflowy/plugins/database_view/calendar/presentation/calendar_page.dart';
|
import 'package:appflowy/plugins/database_view/calendar/presentation/calendar_page.dart';
|
||||||
import 'package:appflowy/plugins/database_view/grid/presentation/grid_page.dart';
|
import 'package:appflowy/plugins/database_view/grid/presentation/grid_page.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/menu/view/draggable_view_item.dart';
|
import 'package:appflowy/workspace/presentation/home/menu/view/draggable_view_item.dart';
|
||||||
|
import 'package:appflowy/workspace/presentation/home/menu/view/view_add_button.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
|
import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
|
||||||
|
import 'package:appflowy/workspace/presentation/home/menu/view/view_more_action_button.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -138,5 +140,70 @@ void main() {
|
|||||||
.id,
|
.id,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('unable to move a document into a database', (tester) async {
|
||||||
|
await tester.initializeAppFlowy();
|
||||||
|
await tester.tapGoButton();
|
||||||
|
|
||||||
|
const document = 'document';
|
||||||
|
await tester.createNewPageWithName(
|
||||||
|
name: document,
|
||||||
|
openAfterCreated: false,
|
||||||
|
);
|
||||||
|
tester.expectToSeePageName(document, layout: ViewLayoutPB.Document);
|
||||||
|
|
||||||
|
const grid = 'grid';
|
||||||
|
await tester.createNewPageWithName(
|
||||||
|
name: grid,
|
||||||
|
layout: ViewLayoutPB.Grid,
|
||||||
|
openAfterCreated: false,
|
||||||
|
);
|
||||||
|
tester.expectToSeePageName(grid, layout: ViewLayoutPB.Grid);
|
||||||
|
|
||||||
|
// move the document to the grid page
|
||||||
|
await tester.movePageToOtherPage(
|
||||||
|
name: document,
|
||||||
|
parentName: grid,
|
||||||
|
layout: ViewLayoutPB.Document,
|
||||||
|
parentLayout: ViewLayoutPB.Grid,
|
||||||
|
);
|
||||||
|
|
||||||
|
// it should not be moved
|
||||||
|
final childViews = tester
|
||||||
|
.widget<SingleInnerViewItem>(tester.findPageName(gettingStated))
|
||||||
|
.view
|
||||||
|
.childViews;
|
||||||
|
expect(
|
||||||
|
childViews[0].name,
|
||||||
|
document,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
childViews[1].name,
|
||||||
|
grid,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('unable to create a new database inside the existing one',
|
||||||
|
(tester) async {
|
||||||
|
await tester.initializeAppFlowy();
|
||||||
|
await tester.tapGoButton();
|
||||||
|
|
||||||
|
const grid = 'grid';
|
||||||
|
await tester.createNewPageWithName(
|
||||||
|
name: grid,
|
||||||
|
layout: ViewLayoutPB.Grid,
|
||||||
|
openAfterCreated: true,
|
||||||
|
);
|
||||||
|
tester.expectToSeePageName(grid, layout: ViewLayoutPB.Grid);
|
||||||
|
|
||||||
|
await tester.hoverOnPageName(
|
||||||
|
grid,
|
||||||
|
layout: ViewLayoutPB.Grid,
|
||||||
|
onHover: () async {
|
||||||
|
expect(find.byType(ViewAddButton), findsNothing);
|
||||||
|
expect(find.byType(ViewMoreActionButton), findsOneWidget);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -351,7 +351,7 @@ extension CommonOperations on WidgetTester {
|
|||||||
await hoverOnPageName(
|
await hoverOnPageName(
|
||||||
name,
|
name,
|
||||||
layout: layout,
|
layout: layout,
|
||||||
useLast: false,
|
useLast: true,
|
||||||
onHover: () async {
|
onHover: () async {
|
||||||
await tapFavoritePageButton();
|
await tapFavoritePageButton();
|
||||||
await pumpAndSettle();
|
await pumpAndSettle();
|
||||||
@ -366,7 +366,7 @@ extension CommonOperations on WidgetTester {
|
|||||||
await hoverOnPageName(
|
await hoverOnPageName(
|
||||||
name,
|
name,
|
||||||
layout: layout,
|
layout: layout,
|
||||||
useLast: false,
|
useLast: true,
|
||||||
onHover: () async {
|
onHover: () async {
|
||||||
await tapUnfavoritePageButton();
|
await tapUnfavoritePageButton();
|
||||||
await pumpAndSettle();
|
await pumpAndSettle();
|
||||||
@ -397,7 +397,7 @@ extension CommonOperations on WidgetTester {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
await gesture.moveTo(offset);
|
await gesture.moveTo(offset, timeStamp: const Duration(milliseconds: 400));
|
||||||
await gesture.up();
|
await gesture.up();
|
||||||
await pumpAndSettle();
|
await pumpAndSettle();
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ class MenuBloc extends Bloc<MenuEvent, MenuState> {
|
|||||||
final result = await _workspaceService.createApp(
|
final result = await _workspaceService.createApp(
|
||||||
name: event.name,
|
name: event.name,
|
||||||
desc: event.desc,
|
desc: event.desc,
|
||||||
index: 0, // default to the first index
|
index: event.index,
|
||||||
);
|
);
|
||||||
result.fold(
|
result.fold(
|
||||||
(app) => emit(state.copyWith(plugin: app.plugin())),
|
(app) => emit(state.copyWith(plugin: app.plugin())),
|
||||||
@ -111,7 +111,8 @@ class MenuBloc extends Bloc<MenuEvent, MenuState> {
|
|||||||
class MenuEvent with _$MenuEvent {
|
class MenuEvent with _$MenuEvent {
|
||||||
const factory MenuEvent.initial() = _Initial;
|
const factory MenuEvent.initial() = _Initial;
|
||||||
const factory MenuEvent.openPage(Plugin plugin) = _OpenPage;
|
const factory MenuEvent.openPage(Plugin plugin) = _OpenPage;
|
||||||
const factory MenuEvent.createApp(String name, {String? desc}) = _CreateApp;
|
const factory MenuEvent.createApp(String name, {String? desc, int? index}) =
|
||||||
|
_CreateApp;
|
||||||
const factory MenuEvent.moveApp(int fromIndex, int toIndex) = _MoveApp;
|
const factory MenuEvent.moveApp(int fromIndex, int toIndex) = _MoveApp;
|
||||||
const factory MenuEvent.didReceiveApps(
|
const factory MenuEvent.didReceiveApps(
|
||||||
Either<List<ViewPB>, FlowyError> appsOrFail,
|
Either<List<ViewPB>, FlowyError> appsOrFail,
|
||||||
|
@ -125,4 +125,17 @@ extension ViewLayoutExtension on ViewLayoutPB {
|
|||||||
throw Exception('Unknown layout type');
|
throw Exception('Unknown layout type');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool get isDatabaseView {
|
||||||
|
switch (this) {
|
||||||
|
case ViewLayoutPB.Grid:
|
||||||
|
case ViewLayoutPB.Board:
|
||||||
|
case ViewLayoutPB.Calendar:
|
||||||
|
return true;
|
||||||
|
case ViewLayoutPB.Document:
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
throw Exception('Unknown layout type');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,7 @@ class PersonalFolder extends StatelessWidget {
|
|||||||
isFirstChild: view.id == views.first.id,
|
isFirstChild: view.id == views.first.id,
|
||||||
view: view,
|
view: view,
|
||||||
level: 0,
|
level: 0,
|
||||||
|
leftPadding: 16,
|
||||||
onSelected: (view) {
|
onSelected: (view) {
|
||||||
getIt<TabsBloc>().add(
|
getIt<TabsBloc>().add(
|
||||||
TabsEvent.openPlugin(
|
TabsEvent.openPlugin(
|
||||||
@ -114,6 +115,7 @@ class _PersonalFolderHeaderState extends State<PersonalFolderHeader> {
|
|||||||
context.read<MenuBloc>().add(
|
context.read<MenuBloc>().add(
|
||||||
MenuEvent.createApp(
|
MenuEvent.createApp(
|
||||||
LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||||
|
index: 0,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
widget.onAdded();
|
widget.onAdded();
|
||||||
|
@ -48,7 +48,12 @@ class SidebarNewPageButton extends StatelessWidget {
|
|||||||
value: '',
|
value: '',
|
||||||
confirm: (value) {
|
confirm: (value) {
|
||||||
if (value.isNotEmpty) {
|
if (value.isNotEmpty) {
|
||||||
context.read<MenuBloc>().add(MenuEvent.createApp(value, desc: ''));
|
context.read<MenuBloc>().add(
|
||||||
|
MenuEvent.createApp(
|
||||||
|
value,
|
||||||
|
desc: '',
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
).show(context);
|
).show(context);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||||
|
import 'package:appflowy/workspace/application/view/view_ext.dart';
|
||||||
import 'package:appflowy/workspace/presentation/widgets/draggable_item/draggable_item.dart';
|
import 'package:appflowy/workspace/presentation/widgets/draggable_item/draggable_item.dart';
|
||||||
import 'package:appflowy_backend/log.dart';
|
import 'package:appflowy_backend/log.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||||
@ -70,16 +71,17 @@ class _DraggableViewItemState extends State<DraggableViewItem> {
|
|||||||
data: widget.view,
|
data: widget.view,
|
||||||
onWillAccept: (data) => true,
|
onWillAccept: (data) => true,
|
||||||
onMove: (data) {
|
onMove: (data) {
|
||||||
if (!_shouldAccept(data.data)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final renderBox = context.findRenderObject() as RenderBox;
|
final renderBox = context.findRenderObject() as RenderBox;
|
||||||
final offset = renderBox.globalToLocal(data.offset);
|
final offset = renderBox.globalToLocal(data.offset);
|
||||||
|
final position = _computeHoverPosition(offset, renderBox.size);
|
||||||
|
if (!_shouldAccept(data.data, position)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
position = _computeHoverPosition(offset, renderBox.size);
|
|
||||||
Log.debug(
|
Log.debug(
|
||||||
'offset: $offset, position: $position, size: ${renderBox.size}',
|
'offset: $offset, position: $position, size: ${renderBox.size}',
|
||||||
);
|
);
|
||||||
|
this.position = position;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onLeave: (_) => setState(
|
onLeave: (_) => setState(
|
||||||
@ -102,6 +104,12 @@ class _DraggableViewItemState extends State<DraggableViewItem> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _move(ViewPB from, ViewPB to) {
|
void _move(ViewPB from, ViewPB to) {
|
||||||
|
if (position == DraggableHoverPosition.center &&
|
||||||
|
to.layout != ViewLayoutPB.Document) {
|
||||||
|
// not support moving into a database
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch (position) {
|
switch (position) {
|
||||||
case DraggableHoverPosition.top:
|
case DraggableHoverPosition.top:
|
||||||
context.read<ViewBloc>().add(
|
context.read<ViewBloc>().add(
|
||||||
@ -136,7 +144,7 @@ class _DraggableViewItemState extends State<DraggableViewItem> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DraggableHoverPosition _computeHoverPosition(Offset offset, Size size) {
|
DraggableHoverPosition _computeHoverPosition(Offset offset, Size size) {
|
||||||
final threshold = size.height / 4.0;
|
final threshold = size.height / 3.0;
|
||||||
if (widget.isFirstChild && offset.dy < -5.0) {
|
if (widget.isFirstChild && offset.dy < -5.0) {
|
||||||
return DraggableHoverPosition.top;
|
return DraggableHoverPosition.top;
|
||||||
}
|
}
|
||||||
@ -146,7 +154,13 @@ class _DraggableViewItemState extends State<DraggableViewItem> {
|
|||||||
return DraggableHoverPosition.center;
|
return DraggableHoverPosition.center;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _shouldAccept(ViewPB data) {
|
bool _shouldAccept(ViewPB data, DraggableHoverPosition position) {
|
||||||
|
// could not move the view to a database
|
||||||
|
if (widget.view.layout.isDatabaseView &&
|
||||||
|
position == DraggableHoverPosition.center) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// ignore moving the view to itself
|
// ignore moving the view to itself
|
||||||
if (data.id == widget.view.id) {
|
if (data.id == widget.view.id) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -23,6 +23,7 @@ class ViewItem extends StatelessWidget {
|
|||||||
const ViewItem({
|
const ViewItem({
|
||||||
super.key,
|
super.key,
|
||||||
required this.view,
|
required this.view,
|
||||||
|
this.parentView,
|
||||||
required this.categoryType,
|
required this.categoryType,
|
||||||
required this.level,
|
required this.level,
|
||||||
this.leftPadding = 10,
|
this.leftPadding = 10,
|
||||||
@ -32,6 +33,7 @@ class ViewItem extends StatelessWidget {
|
|||||||
});
|
});
|
||||||
|
|
||||||
final ViewPB view;
|
final ViewPB view;
|
||||||
|
final ViewPB? parentView;
|
||||||
|
|
||||||
final FolderCategoryType categoryType;
|
final FolderCategoryType categoryType;
|
||||||
|
|
||||||
@ -60,6 +62,7 @@ class ViewItem extends StatelessWidget {
|
|||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return InnerViewItem(
|
return InnerViewItem(
|
||||||
view: state.view,
|
view: state.view,
|
||||||
|
parentView: parentView,
|
||||||
childViews: state.childViews,
|
childViews: state.childViews,
|
||||||
categoryType: categoryType,
|
categoryType: categoryType,
|
||||||
level: level,
|
level: level,
|
||||||
@ -80,18 +83,20 @@ class InnerViewItem extends StatelessWidget {
|
|||||||
const InnerViewItem({
|
const InnerViewItem({
|
||||||
super.key,
|
super.key,
|
||||||
required this.view,
|
required this.view,
|
||||||
|
required this.parentView,
|
||||||
required this.childViews,
|
required this.childViews,
|
||||||
required this.categoryType,
|
required this.categoryType,
|
||||||
this.isDraggable = true,
|
this.isDraggable = true,
|
||||||
this.isExpanded = true,
|
this.isExpanded = true,
|
||||||
required this.level,
|
required this.level,
|
||||||
this.leftPadding = 10,
|
required this.leftPadding,
|
||||||
required this.showActions,
|
required this.showActions,
|
||||||
required this.onSelected,
|
required this.onSelected,
|
||||||
this.isFirstChild = false,
|
this.isFirstChild = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
final ViewPB view;
|
final ViewPB view;
|
||||||
|
final ViewPB? parentView;
|
||||||
final List<ViewPB> childViews;
|
final List<ViewPB> childViews;
|
||||||
final FolderCategoryType categoryType;
|
final FolderCategoryType categoryType;
|
||||||
|
|
||||||
@ -109,10 +114,13 @@ class InnerViewItem extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Widget child = SingleInnerViewItem(
|
Widget child = SingleInnerViewItem(
|
||||||
view: view,
|
view: view,
|
||||||
|
parentView: parentView,
|
||||||
level: level,
|
level: level,
|
||||||
showActions: showActions,
|
showActions: showActions,
|
||||||
onSelected: onSelected,
|
onSelected: onSelected,
|
||||||
isExpanded: isExpanded,
|
isExpanded: isExpanded,
|
||||||
|
isDraggable: isDraggable,
|
||||||
|
leftPadding: leftPadding,
|
||||||
);
|
);
|
||||||
|
|
||||||
// if the view is expanded and has child views, render its child views
|
// if the view is expanded and has child views, render its child views
|
||||||
@ -120,12 +128,14 @@ class InnerViewItem extends StatelessWidget {
|
|||||||
final children = childViews.map((childView) {
|
final children = childViews.map((childView) {
|
||||||
return ViewItem(
|
return ViewItem(
|
||||||
key: ValueKey('${categoryType.name} ${childView.id}'),
|
key: ValueKey('${categoryType.name} ${childView.id}'),
|
||||||
|
parentView: view,
|
||||||
categoryType: categoryType,
|
categoryType: categoryType,
|
||||||
isFirstChild: childView.id == childViews.first.id,
|
isFirstChild: childView.id == childViews.first.id,
|
||||||
view: childView,
|
view: childView,
|
||||||
level: level + 1,
|
level: level + 1,
|
||||||
onSelected: onSelected,
|
onSelected: onSelected,
|
||||||
isDraggable: isDraggable,
|
isDraggable: isDraggable,
|
||||||
|
leftPadding: leftPadding,
|
||||||
);
|
);
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|
||||||
@ -139,7 +149,7 @@ class InnerViewItem extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// wrap the child with DraggableItem if isDraggable is true
|
// wrap the child with DraggableItem if isDraggable is true
|
||||||
if (isDraggable) {
|
if (isDraggable && !isReferencedDatabaseView(view, parentView)) {
|
||||||
child = DraggableViewItem(
|
child = DraggableViewItem(
|
||||||
isFirstChild: isFirstChild,
|
isFirstChild: isFirstChild,
|
||||||
view: view,
|
view: view,
|
||||||
@ -147,10 +157,12 @@ class InnerViewItem extends StatelessWidget {
|
|||||||
feedback: (context) {
|
feedback: (context) {
|
||||||
return ViewItem(
|
return ViewItem(
|
||||||
view: view,
|
view: view,
|
||||||
|
parentView: parentView,
|
||||||
categoryType: categoryType,
|
categoryType: categoryType,
|
||||||
level: level,
|
level: level,
|
||||||
onSelected: onSelected,
|
onSelected: onSelected,
|
||||||
isDraggable: false,
|
isDraggable: false,
|
||||||
|
leftPadding: leftPadding,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -170,19 +182,23 @@ class SingleInnerViewItem extends StatefulWidget {
|
|||||||
const SingleInnerViewItem({
|
const SingleInnerViewItem({
|
||||||
super.key,
|
super.key,
|
||||||
required this.view,
|
required this.view,
|
||||||
|
required this.parentView,
|
||||||
required this.isExpanded,
|
required this.isExpanded,
|
||||||
required this.level,
|
required this.level,
|
||||||
this.leftPadding = 10,
|
required this.leftPadding,
|
||||||
|
this.isDraggable = true,
|
||||||
required this.showActions,
|
required this.showActions,
|
||||||
required this.onSelected,
|
required this.onSelected,
|
||||||
});
|
});
|
||||||
|
|
||||||
final ViewPB view;
|
final ViewPB view;
|
||||||
|
final ViewPB? parentView;
|
||||||
final bool isExpanded;
|
final bool isExpanded;
|
||||||
|
|
||||||
final int level;
|
final int level;
|
||||||
final double leftPadding;
|
final double leftPadding;
|
||||||
|
|
||||||
|
final bool isDraggable;
|
||||||
final bool showActions;
|
final bool showActions;
|
||||||
final void Function(ViewPB) onSelected;
|
final void Function(ViewPB) onSelected;
|
||||||
|
|
||||||
@ -200,16 +216,16 @@ class _SingleInnerViewItemState extends State<SingleInnerViewItem> {
|
|||||||
buildWhenOnHover: () => !widget.showActions,
|
buildWhenOnHover: () => !widget.showActions,
|
||||||
builder: (_, onHover) => _buildViewItem(onHover),
|
builder: (_, onHover) => _buildViewItem(onHover),
|
||||||
isSelected: () =>
|
isSelected: () =>
|
||||||
widget.showActions ||
|
widget.isDraggable &&
|
||||||
getIt<MenuSharedState>().latestOpenView?.id == widget.view.id,
|
(widget.showActions ||
|
||||||
|
getIt<MenuSharedState>().latestOpenView?.id == widget.view.id),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildViewItem(bool onHover) {
|
Widget _buildViewItem(bool onHover) {
|
||||||
final children = [
|
final children = [
|
||||||
// expand icon
|
// expand icon
|
||||||
_buildExpandedIcon(),
|
_buildLeftIcon(),
|
||||||
const HSpace(7),
|
|
||||||
// icon
|
// icon
|
||||||
SizedBox.square(
|
SizedBox.square(
|
||||||
dimension: 16,
|
dimension: 16,
|
||||||
@ -229,12 +245,15 @@ class _SingleInnerViewItemState extends State<SingleInnerViewItem> {
|
|||||||
if (widget.showActions || onHover) {
|
if (widget.showActions || onHover) {
|
||||||
// ··· more action button
|
// ··· more action button
|
||||||
children.add(_buildViewMoreActionButton(context));
|
children.add(_buildViewMoreActionButton(context));
|
||||||
// + button
|
// only support add button for document layout
|
||||||
children.add(_buildViewAddButton(context));
|
if (widget.view.layout == ViewLayoutPB.Document) {
|
||||||
|
// + button
|
||||||
|
children.add(_buildViewAddButton(context));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't use GestureDetector here, because it doesn't response to the tap event sometimes.
|
return GestureDetector(
|
||||||
return InkWell(
|
behavior: HitTestBehavior.translucent,
|
||||||
onTap: () => widget.onSelected(widget.view),
|
onTap: () => widget.onSelected(widget.view),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 26,
|
height: 26,
|
||||||
@ -248,8 +267,14 @@ class _SingleInnerViewItemState extends State<SingleInnerViewItem> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// > button
|
// > button or · button
|
||||||
Widget _buildExpandedIcon() {
|
// show > if the view is expandable.
|
||||||
|
// show · if the view can't contain child views.
|
||||||
|
Widget _buildLeftIcon() {
|
||||||
|
if (isReferencedDatabaseView(widget.view, widget.parentView)) {
|
||||||
|
return const _DotIconWidget();
|
||||||
|
}
|
||||||
|
|
||||||
final name =
|
final name =
|
||||||
widget.isExpanded ? 'home/drop_down_show' : 'home/drop_down_hide';
|
widget.isExpanded ? 'home/drop_down_show' : 'home/drop_down_hide';
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
@ -343,3 +368,30 @@ class _SingleInnerViewItemState extends State<SingleInnerViewItem> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _DotIconWidget extends StatelessWidget {
|
||||||
|
const _DotIconWidget();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(6.0),
|
||||||
|
child: Container(
|
||||||
|
width: 4,
|
||||||
|
height: 4,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).iconTheme.color,
|
||||||
|
borderRadius: BorderRadius.circular(2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// workaround: we should use view.isEndPoint or something to check if the view can contain child views. But currently, we don't have that field.
|
||||||
|
bool isReferencedDatabaseView(ViewPB view, ViewPB? parentView) {
|
||||||
|
if (parentView == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return view.layout.isDatabaseView && parentView.layout.isDatabaseView;
|
||||||
|
}
|
||||||
|
@ -32,8 +32,8 @@ void main() {
|
|||||||
menuBloc.add(const MenuEvent.createApp("App 3"));
|
menuBloc.add(const MenuEvent.createApp("App 3"));
|
||||||
await blocResponseFuture();
|
await blocResponseFuture();
|
||||||
|
|
||||||
assert(menuBloc.state.views[0].name == 'App 3');
|
assert(menuBloc.state.views[1].name == 'App 1');
|
||||||
assert(menuBloc.state.views[1].name == 'App 2');
|
assert(menuBloc.state.views[2].name == 'App 2');
|
||||||
assert(menuBloc.state.views[2].name == 'App 1');
|
assert(menuBloc.state.views[3].name == 'App 3');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user