Projects
This commit is contained in:
parent
ec23625c60
commit
251617d0d7
|
|
@ -14,30 +14,11 @@ class ProjectList extends StatelessWidget {
|
||||||
@required this.viewModel,
|
@required this.viewModel,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
final ProjectListVM viewModel;
|
||||||
Widget build(BuildContext context) {
|
|
||||||
if (!viewModel.isLoaded) {
|
|
||||||
return LoadingIndicator();
|
|
||||||
} else if (viewModel.projectList.isEmpty) {
|
|
||||||
return Opacity(
|
|
||||||
opacity: 0.5,
|
|
||||||
child: Center(
|
|
||||||
child: Text(
|
|
||||||
AppLocalization.of(context).noRecordsFound,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _buildListView(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _showMenu(
|
void _showMenu(
|
||||||
BuildContext context, ProjectEntity project) async {
|
BuildContext context, ProjectEntity project, ClientEntity client) async {
|
||||||
if (project == null) {
|
if (project == null || client == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,9 +26,9 @@ class ProjectList extends StatelessWidget {
|
||||||
final message = await showDialog<String>(
|
final message = await showDialog<String>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext dialogContext) => SimpleDialog(
|
builder: (BuildContext dialogContext) => SimpleDialog(
|
||||||
children: project
|
children: project
|
||||||
.getEntityActions(user: user)
|
.getEntityActions(user: user, client: client)
|
||||||
.map((entityAction) {
|
.map((entityAction) {
|
||||||
if (entityAction == null) {
|
if (entityAction == null) {
|
||||||
return Divider();
|
return Divider();
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -66,42 +47,109 @@ class ProjectList extends StatelessWidget {
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
Scaffold.of(context).showSnackBar(SnackBar(
|
Scaffold.of(context).showSnackBar(SnackBar(
|
||||||
content: SnackBarRow(
|
content: SnackBarRow(
|
||||||
message: message,
|
message: message,
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildListView(BuildContext context) {
|
@override
|
||||||
return RefreshIndicator(
|
Widget build(BuildContext context) {
|
||||||
onRefresh: () => viewModel.onRefreshed(context),
|
final localization = AppLocalization.of(context);
|
||||||
child: ListView.builder(
|
final listState = viewModel.listState;
|
||||||
itemCount: viewModel.projectList.length,
|
final filteredClientId = listState.filterEntityId;
|
||||||
itemBuilder: (BuildContext context, index) {
|
final filteredClient =
|
||||||
final projectId = viewModel.projectList[index];
|
filteredClientId != null ? viewModel.clientMap[filteredClientId] : null;
|
||||||
final project = viewModel.projectMap[projectId];
|
|
||||||
return Column(children: <Widget>[
|
return Column(
|
||||||
ProjectListItem(
|
children: <Widget>[
|
||||||
user: viewModel.user,
|
filteredClient != null
|
||||||
filter: viewModel.filter,
|
? Material(
|
||||||
project: project,
|
color: Colors.orangeAccent,
|
||||||
client: viewModel.clientMap[project.clientId],
|
elevation: 6.0,
|
||||||
onEntityAction: (EntityAction action) {
|
child: InkWell(
|
||||||
if (action == EntityAction.more) {
|
onTap: () => viewModel.onViewEntityFilterPressed(context),
|
||||||
_showMenu(context, project);
|
child: Row(
|
||||||
} else {
|
children: <Widget>[
|
||||||
viewModel.onEntityAction(context, project, action);
|
SizedBox(width: 18.0),
|
||||||
}
|
Expanded(
|
||||||
},
|
child: Text(
|
||||||
onTap: () => viewModel.onProjectTap(context, project),
|
'${localization.filteredBy} ${filteredClient.listDisplayName}',
|
||||||
onLongPress: () => _showMenu(context, project),
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 16.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.close,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
onPressed: () => viewModel.onClearEntityFilterPressed(),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Container(),
|
||||||
|
Expanded(
|
||||||
|
child: !viewModel.isLoaded
|
||||||
|
? LoadingIndicator()
|
||||||
|
: RefreshIndicator(
|
||||||
|
onRefresh: () => viewModel.onRefreshed(context),
|
||||||
|
child: viewModel.projectList.isEmpty
|
||||||
|
? Opacity(
|
||||||
|
opacity: 0.5,
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
AppLocalization.of(context).noRecordsFound,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Divider(
|
)
|
||||||
height: 1.0,
|
: ListView.builder(
|
||||||
),
|
shrinkWrap: true,
|
||||||
]);
|
itemCount: viewModel.projectList.length,
|
||||||
}),
|
itemBuilder: (BuildContext context, index) {
|
||||||
|
final projectId = viewModel.projectList[index];
|
||||||
|
final project = viewModel.projectMap[projectId];
|
||||||
|
final client =
|
||||||
|
viewModel.clientMap[project.clientId] ??
|
||||||
|
ClientEntity();
|
||||||
|
return Column(
|
||||||
|
children: <Widget>[
|
||||||
|
ProjectListItem(
|
||||||
|
user: viewModel.user,
|
||||||
|
filter: viewModel.filter,
|
||||||
|
project: project,
|
||||||
|
client:
|
||||||
|
viewModel.clientMap[project.clientId] ??
|
||||||
|
ClientEntity(),
|
||||||
|
onTap: () =>
|
||||||
|
viewModel.onProjectTap(context, project),
|
||||||
|
onEntityAction: (EntityAction action) {
|
||||||
|
if (action == EntityAction.more) {
|
||||||
|
_showMenu(context, project, client);
|
||||||
|
} else {
|
||||||
|
viewModel.onEntityAction(
|
||||||
|
context, project, action);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLongPress: () =>
|
||||||
|
_showMenu(context, project, client),
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
height: 1.0,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final ProjectListVM viewModel;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'package:invoiceninja_flutter/redux/client/client_actions.dart';
|
||||||
|
import 'package:invoiceninja_flutter/redux/ui/list_ui_state.dart';
|
||||||
import 'package:redux/redux.dart';
|
import 'package:redux/redux.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
@ -35,12 +37,15 @@ class ProjectListVM {
|
||||||
@required this.projectList,
|
@required this.projectList,
|
||||||
@required this.projectMap,
|
@required this.projectMap,
|
||||||
@required this.clientMap,
|
@required this.clientMap,
|
||||||
|
@required this.listState,
|
||||||
@required this.filter,
|
@required this.filter,
|
||||||
@required this.isLoading,
|
@required this.isLoading,
|
||||||
@required this.isLoaded,
|
@required this.isLoaded,
|
||||||
@required this.onProjectTap,
|
@required this.onProjectTap,
|
||||||
@required this.onRefreshed,
|
@required this.onRefreshed,
|
||||||
@required this.onEntityAction,
|
@required this.onEntityAction,
|
||||||
|
@required this.onClearEntityFilterPressed,
|
||||||
|
@required this.onViewEntityFilterPressed,
|
||||||
});
|
});
|
||||||
|
|
||||||
static ProjectListVM fromStore(Store<AppState> store) {
|
static ProjectListVM fromStore(Store<AppState> store) {
|
||||||
|
|
@ -58,6 +63,7 @@ class ProjectListVM {
|
||||||
|
|
||||||
return ProjectListVM(
|
return ProjectListVM(
|
||||||
user: state.user,
|
user: state.user,
|
||||||
|
listState: state.projectListState,
|
||||||
projectList: memoizedFilteredProjectList(state.projectState.map,
|
projectList: memoizedFilteredProjectList(state.projectState.map,
|
||||||
state.projectState.list, state.projectListState, state.clientState.map),
|
state.projectState.list, state.projectListState, state.clientState.map),
|
||||||
projectMap: state.projectState.map,
|
projectMap: state.projectState.map,
|
||||||
|
|
@ -65,6 +71,12 @@ class ProjectListVM {
|
||||||
isLoading: state.isLoading,
|
isLoading: state.isLoading,
|
||||||
isLoaded: state.projectState.isLoaded,
|
isLoaded: state.projectState.isLoaded,
|
||||||
filter: state.projectUIState.listUIState.filter,
|
filter: state.projectUIState.listUIState.filter,
|
||||||
|
onClearEntityFilterPressed: () =>
|
||||||
|
store.dispatch(FilterProjectsByEntity()),
|
||||||
|
onViewEntityFilterPressed: (BuildContext context) => store.dispatch(
|
||||||
|
ViewClient(
|
||||||
|
clientId: state.invoiceListState.filterEntityId,
|
||||||
|
context: context)),
|
||||||
onProjectTap: (context, project) {
|
onProjectTap: (context, project) {
|
||||||
store.dispatch(ViewProject(projectId: project.id, context: context));
|
store.dispatch(ViewProject(projectId: project.id, context: context));
|
||||||
},
|
},
|
||||||
|
|
@ -103,10 +115,14 @@ class ProjectListVM {
|
||||||
final List<int> projectList;
|
final List<int> projectList;
|
||||||
final BuiltMap<int, ProjectEntity> projectMap;
|
final BuiltMap<int, ProjectEntity> projectMap;
|
||||||
final BuiltMap<int, ClientEntity> clientMap;
|
final BuiltMap<int, ClientEntity> clientMap;
|
||||||
|
final ListUIState listState;
|
||||||
final String filter;
|
final String filter;
|
||||||
final bool isLoading;
|
final bool isLoading;
|
||||||
final bool isLoaded;
|
final bool isLoaded;
|
||||||
final Function(BuildContext, ProjectEntity) onProjectTap;
|
final Function(BuildContext, ProjectEntity) onProjectTap;
|
||||||
final Function(BuildContext) onRefreshed;
|
final Function(BuildContext) onRefreshed;
|
||||||
final Function(BuildContext, ProjectEntity, EntityAction) onEntityAction;
|
final Function(BuildContext, ProjectEntity, EntityAction) onEntityAction;
|
||||||
|
final Function onClearEntityFilterPressed;
|
||||||
|
final Function(BuildContext) onViewEntityFilterPressed;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,30 +14,11 @@ class StubList extends StatelessWidget {
|
||||||
@required this.viewModel,
|
@required this.viewModel,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
final StubListVM viewModel;
|
||||||
Widget build(BuildContext context) {
|
|
||||||
if (!viewModel.isLoaded) {
|
|
||||||
return LoadingIndicator();
|
|
||||||
} else if (viewModel.stubList.isEmpty) {
|
|
||||||
return Opacity(
|
|
||||||
opacity: 0.5,
|
|
||||||
child: Center(
|
|
||||||
child: Text(
|
|
||||||
AppLocalization.of(context).noRecordsFound,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _buildListView(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _showMenu(
|
void _showMenu(
|
||||||
BuildContext context, StubEntity stub) async {
|
BuildContext context, StubEntity stub, ClientEntity client) async {
|
||||||
if (stub == null) {
|
if (stub == null || client == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,7 +27,7 @@ class StubList extends StatelessWidget {
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext dialogContext) => SimpleDialog(
|
builder: (BuildContext dialogContext) => SimpleDialog(
|
||||||
children: stub
|
children: stub
|
||||||
.getEntityActions(user: user)
|
.getEntityActions(user: user, client: client)
|
||||||
.map((entityAction) {
|
.map((entityAction) {
|
||||||
if (entityAction == null) {
|
if (entityAction == null) {
|
||||||
return Divider();
|
return Divider();
|
||||||
|
|
@ -71,36 +52,104 @@ class StubList extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildListView(BuildContext context) {
|
@override
|
||||||
return RefreshIndicator(
|
Widget build(BuildContext context) {
|
||||||
onRefresh: () => viewModel.onRefreshed(context),
|
final localization = AppLocalization.of(context);
|
||||||
child: ListView.builder(
|
final listState = viewModel.listState;
|
||||||
itemCount: viewModel.stubList.length,
|
final filteredClientId = listState.filterEntityId;
|
||||||
itemBuilder: (BuildContext context, index) {
|
final filteredClient =
|
||||||
final stubId = viewModel.stubList[index];
|
filteredClientId != null ? viewModel.clientMap[filteredClientId] : null;
|
||||||
final stub = viewModel.stubMap[stubId];
|
|
||||||
return Column(children: <Widget>[
|
return Column(
|
||||||
StubListItem(
|
children: <Widget>[
|
||||||
user: viewModel.user,
|
filteredClient != null
|
||||||
filter: viewModel.filter,
|
? Material(
|
||||||
stub: stub,
|
color: Colors.orangeAccent,
|
||||||
onEntityAction: (EntityAction action) {
|
elevation: 6.0,
|
||||||
if (action == EntityAction.more) {
|
child: InkWell(
|
||||||
_showMenu(context, stub);
|
onTap: () => viewModel.onViewEntityFilterPressed(context),
|
||||||
} else {
|
child: Row(
|
||||||
viewModel.onEntityAction(context, stub, action);
|
children: <Widget>[
|
||||||
}
|
SizedBox(width: 18.0),
|
||||||
},
|
Expanded(
|
||||||
onTap: () => viewModel.onStubTap(context, stub),
|
child: Text(
|
||||||
onLongPress: () => _showMenu(context, stub),
|
'${localization.filteredBy} ${filteredClient.listDisplayName}',
|
||||||
),
|
style: TextStyle(
|
||||||
Divider(
|
color: Colors.white,
|
||||||
height: 1.0,
|
fontSize: 16.0,
|
||||||
),
|
),
|
||||||
]);
|
),
|
||||||
}),
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.close,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
onPressed: () => viewModel.onClearEntityFilterPressed(),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Container(),
|
||||||
|
Expanded(
|
||||||
|
child: !viewModel.isLoaded
|
||||||
|
? LoadingIndicator()
|
||||||
|
: RefreshIndicator(
|
||||||
|
onRefresh: () => viewModel.onRefreshed(context),
|
||||||
|
child: viewModel.stubList.isEmpty
|
||||||
|
? Opacity(
|
||||||
|
opacity: 0.5,
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
AppLocalization.of(context).noRecordsFound,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: viewModel.stubList.length,
|
||||||
|
itemBuilder: (BuildContext context, index) {
|
||||||
|
final stubId = viewModel.stubList[index];
|
||||||
|
final stub = viewModel.stubMap[stubId];
|
||||||
|
final client =
|
||||||
|
viewModel.clientMap[stub.clientId] ??
|
||||||
|
ClientEntity();
|
||||||
|
return Column(
|
||||||
|
children: <Widget>[
|
||||||
|
StubListItem(
|
||||||
|
user: viewModel.user,
|
||||||
|
filter: viewModel.filter,
|
||||||
|
stub: stub,
|
||||||
|
client:
|
||||||
|
viewModel.clientMap[stub.clientId] ??
|
||||||
|
ClientEntity(),
|
||||||
|
onTap: () =>
|
||||||
|
viewModel.onStubTap(context, stub),
|
||||||
|
onEntityAction: (EntityAction action) {
|
||||||
|
if (action == EntityAction.more) {
|
||||||
|
_showMenu(context, stub, client);
|
||||||
|
} else {
|
||||||
|
viewModel.onEntityAction(
|
||||||
|
context, stub, action);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLongPress: () =>
|
||||||
|
_showMenu(context, stub, client),
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
height: 1.0,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final StubListVM viewModel;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_redux/flutter_redux.dart';
|
import 'package:flutter_redux/flutter_redux.dart';
|
||||||
import 'package:built_collection/built_collection.dart';
|
import 'package:built_collection/built_collection.dart';
|
||||||
|
import 'package:invoiceninja_flutter/redux/client/client_actions.dart';
|
||||||
|
import 'package:invoiceninja_flutter/redux/ui/list_ui_state.dart';
|
||||||
import 'package:invoiceninja_flutter/utils/completers.dart';
|
import 'package:invoiceninja_flutter/utils/completers.dart';
|
||||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||||
import 'package:invoiceninja_flutter/redux/stub/stub_selectors.dart';
|
import 'package:invoiceninja_flutter/redux/stub/stub_selectors.dart';
|
||||||
|
|
@ -38,8 +40,11 @@ class StubListVM {
|
||||||
@required this.isLoading,
|
@required this.isLoading,
|
||||||
@required this.isLoaded,
|
@required this.isLoaded,
|
||||||
@required this.onStubTap,
|
@required this.onStubTap,
|
||||||
|
@required this.listState,
|
||||||
@required this.onRefreshed,
|
@required this.onRefreshed,
|
||||||
@required this.onEntityAction,
|
@required this.onEntityAction,
|
||||||
|
@required this.onClearEntityFilterPressed,
|
||||||
|
@required this.onViewEntityFilterPressed,
|
||||||
});
|
});
|
||||||
|
|
||||||
static StubListVM fromStore(Store<AppState> store) {
|
static StubListVM fromStore(Store<AppState> store) {
|
||||||
|
|
@ -57,12 +62,19 @@ class StubListVM {
|
||||||
|
|
||||||
return StubListVM(
|
return StubListVM(
|
||||||
user: state.user,
|
user: state.user,
|
||||||
|
listState: state.stubListState,
|
||||||
stubList: memoizedFilteredStubList(state.stubState.map,
|
stubList: memoizedFilteredStubList(state.stubState.map,
|
||||||
state.stubState.list, state.stubListState),
|
state.stubState.list, state.stubListState),
|
||||||
stubMap: state.stubState.map,
|
stubMap: state.stubState.map,
|
||||||
isLoading: state.isLoading,
|
isLoading: state.isLoading,
|
||||||
isLoaded: state.stubState.isLoaded,
|
isLoaded: state.stubState.isLoaded,
|
||||||
filter: state.stubUIState.listUIState.filter,
|
filter: state.stubUIState.listUIState.filter,
|
||||||
|
onClearEntityFilterPressed: () =>
|
||||||
|
store.dispatch(FilterStubsByEntity()),
|
||||||
|
onViewEntityFilterPressed: (BuildContext context) => store.dispatch(
|
||||||
|
ViewClient(
|
||||||
|
clientId: state.stubListState.filterEntityId,
|
||||||
|
context: context)),
|
||||||
onStubTap: (context, stub) {
|
onStubTap: (context, stub) {
|
||||||
store.dispatch(EditStub(stub: stub, context: context));
|
store.dispatch(EditStub(stub: stub, context: context));
|
||||||
},
|
},
|
||||||
|
|
@ -100,10 +112,14 @@ class StubListVM {
|
||||||
final UserEntity user;
|
final UserEntity user;
|
||||||
final List<int> stubList;
|
final List<int> stubList;
|
||||||
final BuiltMap<int, StubEntity> stubMap;
|
final BuiltMap<int, StubEntity> stubMap;
|
||||||
|
final ListUIState listState;
|
||||||
final String filter;
|
final String filter;
|
||||||
final bool isLoading;
|
final bool isLoading;
|
||||||
final bool isLoaded;
|
final bool isLoaded;
|
||||||
final Function(BuildContext, StubEntity) onStubTap;
|
final Function(BuildContext, StubEntity) onStubTap;
|
||||||
final Function(BuildContext) onRefreshed;
|
final Function(BuildContext) onRefreshed;
|
||||||
final Function(BuildContext, StubEntity, EntityAction) onEntityAction;
|
final Function(BuildContext, StubEntity, EntityAction) onEntityAction;
|
||||||
|
final Function onClearEntityFilterPressed;
|
||||||
|
final Function(BuildContext) onViewEntityFilterPressed;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue