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