Implement desktop client layout

This commit is contained in:
Hillel Coren 2022-05-30 15:04:59 +03:00
parent 9680cf6833
commit d38307d902
8 changed files with 244 additions and 171 deletions

View File

@ -565,6 +565,10 @@ Serializers _$serializers = (new Serializers().toBuilder()
const FullType(BuiltMap, const FullType(BuiltMap,
const [const FullType(EntityType), const FullType(bool)]), const [const FullType(EntityType), const FullType(bool)]),
() => new MapBuilder<EntityType, bool>()) () => new MapBuilder<EntityType, bool>())
..addBuilderFactory(
const FullType(BuiltMap,
const [const FullType(EntityType), const FullType(bool)]),
() => new MapBuilder<EntityType, bool>())
..addBuilderFactory( ..addBuilderFactory(
const FullType( const FullType(
BuiltMap, const [const FullType(String), const FullType(String)]), BuiltMap, const [const FullType(String), const FullType(String)]),

View File

@ -1420,6 +1420,9 @@ void selectEntity({
} else if (isDesktop(context) && !state.prefState.isPreviewVisible) { } else if (isDesktop(context) && !state.prefState.isPreviewVisible) {
if (uiState.isEditing && entityUIState.editingId == entity.id) { if (uiState.isEditing && entityUIState.editingId == entity.id) {
viewEntitiesByType(entityType: entity.entityType); viewEntitiesByType(entityType: entity.entityType);
} else if (entity.entityType == EntityType.client &&
state.prefState.isViewerFullScreen(EntityType.client)) {
viewEntitiesByType(entityType: EntityType.invoice, filterEntity: entity);
} else { } else {
if (!state.prefState.isPreviewVisible) { if (!state.prefState.isPreviewVisible) {
store.dispatch(TogglePreviewSidebar()); store.dispatch(TogglePreviewSidebar());

View File

@ -20,6 +20,7 @@ abstract class PrefState implements Built<PrefState, PrefStateBuilder> {
moduleLayout: ModuleLayout.table, moduleLayout: ModuleLayout.table,
isPreviewVisible: false, isPreviewVisible: false,
useSidebarEditor: BuiltMap<EntityType, bool>(), useSidebarEditor: BuiltMap<EntityType, bool>(),
useSidebarViewer: BuiltMap<EntityType, bool>(),
menuSidebarMode: AppSidebarMode.collapse, menuSidebarMode: AppSidebarMode.collapse,
historySidebarMode: AppSidebarMode.float, historySidebarMode: AppSidebarMode.float,
rowsPerPage: 10, rowsPerPage: 10,
@ -102,6 +103,8 @@ abstract class PrefState implements Built<PrefState, PrefStateBuilder> {
BuiltMap<EntityType, bool> get useSidebarEditor; BuiltMap<EntityType, bool> get useSidebarEditor;
BuiltMap<EntityType, bool> get useSidebarViewer;
BuiltMap<String, String> get customColors; BuiltMap<String, String> get customColors;
bool get isPreviewVisible; bool get isPreviewVisible;
@ -169,6 +172,18 @@ abstract class PrefState implements Built<PrefState, PrefStateBuilder> {
return !(useSidebarEditor[entityType.baseType] ?? false); return !(useSidebarEditor[entityType.baseType] ?? false);
} }
bool isViewerFullScreen(EntityType entityType) {
if (!isDesktop) {
return false;
}
if (![EntityType.client].contains(entityType)) {
return false;
}
return !(useSidebarViewer[entityType.baseType] ?? false);
}
bool get isNotDesktop => !isDesktop; bool get isNotDesktop => !isDesktop;
bool get isMobile => appLayout == AppLayout.mobile; bool get isMobile => appLayout == AppLayout.mobile;
@ -201,6 +216,7 @@ abstract class PrefState implements Built<PrefState, PrefStateBuilder> {
// ignore: unused_element // ignore: unused_element
static void _initializeBuilder(PrefStateBuilder builder) => builder static void _initializeBuilder(PrefStateBuilder builder) => builder
..useSidebarEditor.replace(BuiltMap<EntityType, bool>()) ..useSidebarEditor.replace(BuiltMap<EntityType, bool>())
..useSidebarViewer.replace(BuiltMap<EntityType, bool>())
..sortFields.replace(BuiltMap<EntityType, PrefStateSortField>()) ..sortFields.replace(BuiltMap<EntityType, PrefStateSortField>())
..customColors.replace(builder.enableDarkMode == true ..customColors.replace(builder.enableDarkMode == true
? BuiltMap<String, String>() ? BuiltMap<String, String>()

View File

@ -129,6 +129,10 @@ class _$PrefStateSerializer implements StructuredSerializer<PrefState> {
serializers.serialize(object.useSidebarEditor, serializers.serialize(object.useSidebarEditor,
specifiedType: const FullType(BuiltMap, specifiedType: const FullType(BuiltMap,
const [const FullType(EntityType), const FullType(bool)])), const [const FullType(EntityType), const FullType(bool)])),
'useSidebarViewer',
serializers.serialize(object.useSidebarViewer,
specifiedType: const FullType(BuiltMap,
const [const FullType(EntityType), const FullType(bool)])),
'customColors', 'customColors',
serializers.serialize(object.customColors, serializers.serialize(object.customColors,
specifiedType: const FullType(BuiltMap, specifiedType: const FullType(BuiltMap,
@ -245,6 +249,11 @@ class _$PrefStateSerializer implements StructuredSerializer<PrefState> {
specifiedType: const FullType(BuiltMap, specifiedType: const FullType(BuiltMap,
const [const FullType(EntityType), const FullType(bool)]))); const [const FullType(EntityType), const FullType(bool)])));
break; break;
case 'useSidebarViewer':
result.useSidebarViewer.replace(serializers.deserialize(value,
specifiedType: const FullType(BuiltMap,
const [const FullType(EntityType), const FullType(bool)])));
break;
case 'customColors': case 'customColors':
result.customColors.replace(serializers.deserialize(value, result.customColors.replace(serializers.deserialize(value,
specifiedType: const FullType(BuiltMap, specifiedType: const FullType(BuiltMap,
@ -589,6 +598,8 @@ class _$PrefState extends PrefState {
@override @override
final BuiltMap<EntityType, bool> useSidebarEditor; final BuiltMap<EntityType, bool> useSidebarEditor;
@override @override
final BuiltMap<EntityType, bool> useSidebarViewer;
@override
final BuiltMap<String, String> customColors; final BuiltMap<String, String> customColors;
@override @override
final bool isPreviewVisible; final bool isPreviewVisible;
@ -646,6 +657,7 @@ class _$PrefState extends PrefState {
this.menuSidebarMode, this.menuSidebarMode,
this.historySidebarMode, this.historySidebarMode,
this.useSidebarEditor, this.useSidebarEditor,
this.useSidebarViewer,
this.customColors, this.customColors,
this.isPreviewVisible, this.isPreviewVisible,
this.isMenuVisible, this.isMenuVisible,
@ -680,6 +692,8 @@ class _$PrefState extends PrefState {
historySidebarMode, 'PrefState', 'historySidebarMode'); historySidebarMode, 'PrefState', 'historySidebarMode');
BuiltValueNullFieldError.checkNotNull( BuiltValueNullFieldError.checkNotNull(
useSidebarEditor, 'PrefState', 'useSidebarEditor'); useSidebarEditor, 'PrefState', 'useSidebarEditor');
BuiltValueNullFieldError.checkNotNull(
useSidebarViewer, 'PrefState', 'useSidebarViewer');
BuiltValueNullFieldError.checkNotNull( BuiltValueNullFieldError.checkNotNull(
customColors, 'PrefState', 'customColors'); customColors, 'PrefState', 'customColors');
BuiltValueNullFieldError.checkNotNull( BuiltValueNullFieldError.checkNotNull(
@ -745,6 +759,7 @@ class _$PrefState extends PrefState {
menuSidebarMode == other.menuSidebarMode && menuSidebarMode == other.menuSidebarMode &&
historySidebarMode == other.historySidebarMode && historySidebarMode == other.historySidebarMode &&
useSidebarEditor == other.useSidebarEditor && useSidebarEditor == other.useSidebarEditor &&
useSidebarViewer == other.useSidebarViewer &&
customColors == other.customColors && customColors == other.customColors &&
isPreviewVisible == other.isPreviewVisible && isPreviewVisible == other.isPreviewVisible &&
isMenuVisible == other.isMenuVisible && isMenuVisible == other.isMenuVisible &&
@ -792,7 +807,7 @@ class _$PrefState extends PrefState {
$jc( $jc(
$jc( $jc(
$jc( $jc(
$jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc(0, appLayout.hashCode), moduleLayout.hashCode), menuSidebarMode.hashCode), historySidebarMode.hashCode), useSidebarEditor.hashCode), customColors.hashCode), isPreviewVisible.hashCode), isMenuVisible.hashCode), showKanban.hashCode), showPdfPreview.hashCode), $jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc(0, appLayout.hashCode), moduleLayout.hashCode), menuSidebarMode.hashCode), historySidebarMode.hashCode), useSidebarEditor.hashCode), useSidebarViewer.hashCode), customColors.hashCode), isPreviewVisible.hashCode), isMenuVisible.hashCode), showKanban.hashCode), showPdfPreview.hashCode),
enableTouchEvents.hashCode), enableTouchEvents.hashCode),
isHistoryVisible.hashCode), isHistoryVisible.hashCode),
enableDarkMode.hashCode), enableDarkMode.hashCode),
@ -822,6 +837,7 @@ class _$PrefState extends PrefState {
..add('menuSidebarMode', menuSidebarMode) ..add('menuSidebarMode', menuSidebarMode)
..add('historySidebarMode', historySidebarMode) ..add('historySidebarMode', historySidebarMode)
..add('useSidebarEditor', useSidebarEditor) ..add('useSidebarEditor', useSidebarEditor)
..add('useSidebarViewer', useSidebarViewer)
..add('customColors', customColors) ..add('customColors', customColors)
..add('isPreviewVisible', isPreviewVisible) ..add('isPreviewVisible', isPreviewVisible)
..add('isMenuVisible', isMenuVisible) ..add('isMenuVisible', isMenuVisible)
@ -878,6 +894,12 @@ class PrefStateBuilder implements Builder<PrefState, PrefStateBuilder> {
set useSidebarEditor(MapBuilder<EntityType, bool> useSidebarEditor) => set useSidebarEditor(MapBuilder<EntityType, bool> useSidebarEditor) =>
_$this._useSidebarEditor = useSidebarEditor; _$this._useSidebarEditor = useSidebarEditor;
MapBuilder<EntityType, bool> _useSidebarViewer;
MapBuilder<EntityType, bool> get useSidebarViewer =>
_$this._useSidebarViewer ??= new MapBuilder<EntityType, bool>();
set useSidebarViewer(MapBuilder<EntityType, bool> useSidebarViewer) =>
_$this._useSidebarViewer = useSidebarViewer;
MapBuilder<String, String> _customColors; MapBuilder<String, String> _customColors;
MapBuilder<String, String> get customColors => MapBuilder<String, String> get customColors =>
_$this._customColors ??= new MapBuilder<String, String>(); _$this._customColors ??= new MapBuilder<String, String>();
@ -1007,6 +1029,7 @@ class PrefStateBuilder implements Builder<PrefState, PrefStateBuilder> {
_menuSidebarMode = $v.menuSidebarMode; _menuSidebarMode = $v.menuSidebarMode;
_historySidebarMode = $v.historySidebarMode; _historySidebarMode = $v.historySidebarMode;
_useSidebarEditor = $v.useSidebarEditor.toBuilder(); _useSidebarEditor = $v.useSidebarEditor.toBuilder();
_useSidebarViewer = $v.useSidebarViewer.toBuilder();
_customColors = $v.customColors.toBuilder(); _customColors = $v.customColors.toBuilder();
_isPreviewVisible = $v.isPreviewVisible; _isPreviewVisible = $v.isPreviewVisible;
_isMenuVisible = $v.isMenuVisible; _isMenuVisible = $v.isMenuVisible;
@ -1062,6 +1085,7 @@ class PrefStateBuilder implements Builder<PrefState, PrefStateBuilder> {
historySidebarMode: BuiltValueNullFieldError.checkNotNull( historySidebarMode: BuiltValueNullFieldError.checkNotNull(
historySidebarMode, 'PrefState', 'historySidebarMode'), historySidebarMode, 'PrefState', 'historySidebarMode'),
useSidebarEditor: useSidebarEditor.build(), useSidebarEditor: useSidebarEditor.build(),
useSidebarViewer: useSidebarViewer.build(),
customColors: customColors.build(), customColors: customColors.build(),
isPreviewVisible: BuiltValueNullFieldError.checkNotNull( isPreviewVisible: BuiltValueNullFieldError.checkNotNull(
isPreviewVisible, 'PrefState', 'isPreviewVisible'), isPreviewVisible, 'PrefState', 'isPreviewVisible'),
@ -1095,6 +1119,8 @@ class PrefStateBuilder implements Builder<PrefState, PrefStateBuilder> {
try { try {
_$failedField = 'useSidebarEditor'; _$failedField = 'useSidebarEditor';
useSidebarEditor.build(); useSidebarEditor.build();
_$failedField = 'useSidebarViewer';
useSidebarViewer.build();
_$failedField = 'customColors'; _$failedField = 'customColors';
customColors.build(); customColors.build();

View File

@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
// Package imports: // Package imports:
import 'package:flutter_redux/flutter_redux.dart'; import 'package:flutter_redux/flutter_redux.dart';
import 'package:invoiceninja_flutter/ui/app/screen_imports.dart';
import 'package:overflow_view/overflow_view.dart'; import 'package:overflow_view/overflow_view.dart';
// Project imports: // Project imports:
@ -44,7 +45,14 @@ class EntityTopFilter extends StatelessWidget {
return Material( return Material(
color: backgroundColor, color: backgroundColor,
child: AnimatedContainer( child: Column(
children: [
if (prefState.isViewerFullScreen(state.uiState.filterEntityType))
Expanded(
child: ClientViewScreen(
isTopFilter: true,
)),
AnimatedContainer(
height: show ? 46 : 0, height: show ? 46 : 0,
duration: Duration(milliseconds: kDefaultAnimationDuration), duration: Duration(milliseconds: kDefaultAnimationDuration),
curve: Curves.easeInOutCubic, curve: Curves.easeInOutCubic,
@ -66,7 +74,8 @@ class EntityTopFilter extends StatelessWidget {
isFilterVisible: !prefState.isFilterVisible)); isFilterVisible: !prefState.isFilterVisible));
}, },
onLongPress: () { onLongPress: () {
editEntity(context: context, entity: filterEntity); editEntity(
context: context, entity: filterEntity);
}, },
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
@ -84,7 +93,8 @@ class EntityTopFilter extends StatelessWidget {
.initialize(filterEntity, context) .initialize(filterEntity, context)
.title(), .title(),
style: TextStyle( style: TextStyle(
fontSize: 17, color: state.headerTextColor), fontSize: 17,
color: state.headerTextColor),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
maxLines: 1, maxLines: 1,
), ),
@ -104,15 +114,15 @@ class EntityTopFilter extends StatelessWidget {
DecoratedBox( DecoratedBox(
child: TextButton( child: TextButton(
child: Text( child: Text(
localization localization.lookup(
.lookup('${relatedTypes[i].plural}'), '${relatedTypes[i].plural}'),
style: TextStyle( style: TextStyle(
color: state.headerTextColor, color: state.headerTextColor,
), ),
), ),
style: TextButton.styleFrom( style: TextButton.styleFrom(
padding: padding: EdgeInsets.symmetric(
EdgeInsets.symmetric(horizontal: 12), horizontal: 12),
minimumSize: Size(0, 36), minimumSize: Size(0, 36),
), ),
onPressed: () { onPressed: () {
@ -132,7 +142,8 @@ class EntityTopFilter extends StatelessWidget {
border: relatedTypes[i] == routeEntityType border: relatedTypes[i] == routeEntityType
? Border( ? Border(
bottom: BorderSide( bottom: BorderSide(
color: prefState.enableDarkMode || color: prefState
.enableDarkMode ||
!state.hasAccentColor !state.hasAccentColor
? state.accentColor ? state.accentColor
: Colors.white, : Colors.white,
@ -146,8 +157,8 @@ class EntityTopFilter extends StatelessWidget {
builder: (context, remaining) { builder: (context, remaining) {
return PopupMenuButton<EntityType>( return PopupMenuButton<EntityType>(
child: Padding( child: Padding(
padding: padding: const EdgeInsets.symmetric(
const EdgeInsets.symmetric(horizontal: 4), horizontal: 4),
child: Row( child: Row(
children: [ children: [
Text( Text(
@ -176,10 +187,12 @@ class EntityTopFilter extends StatelessWidget {
}, },
itemBuilder: (BuildContext context) => itemBuilder: (BuildContext context) =>
filterEntityType.relatedTypes filterEntityType.relatedTypes
.sublist(relatedTypes.length - remaining) .sublist(
relatedTypes.length - remaining)
.where((element) => state.company .where((element) => state.company
.isModuleEnabled(element)) .isModuleEnabled(element))
.map((type) => PopupMenuItem<EntityType>( .map((type) =>
PopupMenuItem<EntityType>(
value: type, value: type,
child: ConstrainedBox( child: ConstrainedBox(
constraints: BoxConstraints( constraints: BoxConstraints(
@ -211,6 +224,8 @@ class EntityTopFilter extends StatelessWidget {
), ),
), ),
), ),
],
),
); );
} }
} }

View File

@ -687,6 +687,10 @@ class EntityScreens extends StatelessWidget {
? listWidget ? listWidget
: Column( : Column(
children: [ children: [
if (prefState.isViewerFullScreen(
state.uiState.filterEntityType))
Expanded(child: topFilterChild)
else
topFilterChild, topFilterChild,
Expanded( Expanded(
child: AppBorder( child: AppBorder(
@ -705,17 +709,10 @@ class EntityScreens extends StatelessWidget {
Expanded( Expanded(
flex: isFullScreen ? (listFlex + previewFlex) : previewFlex, flex: isFullScreen ? (listFlex + previewFlex) : previewFlex,
child: AppBorder( child: AppBorder(
child: Column(
children: [
if (isFullScreen) topFilterChild,
Expanded(
child: AppBorder( child: AppBorder(
child: child, child: child,
isTop: isFullScreen && uiState.filterEntityType != null, isTop: isFullScreen && uiState.filterEntityType != null,
), ),
),
],
),
isLeft: true, isLeft: true,
), ),
), ),

View File

@ -25,11 +25,13 @@ class ClientView extends StatefulWidget {
Key key, Key key,
@required this.viewModel, @required this.viewModel,
@required this.isFilter, @required this.isFilter,
@required this.isTopFilter,
@required this.tabIndex, @required this.tabIndex,
}) : super(key: key); }) : super(key: key);
final ClientViewVM viewModel; final ClientViewVM viewModel;
final bool isFilter; final bool isFilter;
final bool isTopFilter;
final int tabIndex; final int tabIndex;
@override @override
@ -86,6 +88,13 @@ class _ClientViewState extends State<ClientView>
final documents = client.documents; final documents = client.documents;
final userCompany = viewModel.state.userCompany; final userCompany = viewModel.state.userCompany;
if (widget.isTopFilter) {
return ViewScaffold(
body: Placeholder(),
entity: client,
);
}
return ViewScaffold( return ViewScaffold(
isFilter: widget.isFilter, isFilter: widget.isFilter,
entity: client, entity: client,

View File

@ -25,8 +25,10 @@ class ClientViewScreen extends StatelessWidget {
const ClientViewScreen({ const ClientViewScreen({
Key key, Key key,
this.isFilter = false, this.isFilter = false,
this.isTopFilter = false,
}) : super(key: key); }) : super(key: key);
final bool isFilter; final bool isFilter;
final bool isTopFilter;
static const String route = '/client/view'; static const String route = '/client/view';
@ -41,6 +43,7 @@ class ClientViewScreen extends StatelessWidget {
return ClientView( return ClientView(
viewModel: vm, viewModel: vm,
isFilter: isFilter, isFilter: isFilter,
isTopFilter: isTopFilter,
tabIndex: vm.state.clientUIState.tabIndex, tabIndex: vm.state.clientUIState.tabIndex,
); );
}, },