Subscriptions

This commit is contained in:
Hillel Coren 2021-03-24 21:51:52 +02:00
parent 9dd186cfca
commit ffc558903d
19 changed files with 234 additions and 198 deletions

View File

@ -129,9 +129,9 @@ part 'serializers.g.dart';
TaxRateItemResponse,
TaxRateListResponse,
// STARTER: serializers - do not remove comment
SubscriptionEntity,
SubscriptionListResponse,
SubscriptionItemResponse,
SubscriptionEntity,
SubscriptionListResponse,
SubscriptionItemResponse,
TaskStatusEntity,
TaskStatusListResponse,

View File

@ -8,7 +8,6 @@ import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/data/web_client.dart';
class SubscriptionRepository {
const SubscriptionRepository({
this.webClient = const WebClient(),
});
@ -20,52 +19,53 @@ class SubscriptionRepository {
final dynamic response = await webClient.get(
'${credentials.url}/subscriptions/$entityId', credentials.token);
final SubscriptionItemResponse subscriptionResponse =
serializers.deserializeWith(SubscriptionItemResponse.serializer, response);
final SubscriptionItemResponse subscriptionResponse = serializers
.deserializeWith(SubscriptionItemResponse.serializer, response);
return subscriptionResponse.data;
}
Future<BuiltList<SubscriptionEntity>> loadList(Credentials credentials) async {
Future<BuiltList<SubscriptionEntity>> loadList(
Credentials credentials) async {
final String url = credentials.url + '/subscriptions?';
final dynamic response = await webClient.get(url, credentials.token);
final SubscriptionListResponse subscriptionResponse =
serializers.deserializeWith(SubscriptionListResponse.serializer, response);
final SubscriptionListResponse subscriptionResponse = serializers
.deserializeWith(SubscriptionListResponse.serializer, response);
return subscriptionResponse.data;
}
Future<List<SubscriptionEntity>> bulkAction(
Credentials credentials, List<String> ids, EntityAction action) async {
final url = credentials.url + '/subscriptions/bulk';
final dynamic response = await webClient.post(url, credentials.token,
data: json.encode({'ids': ids, 'action': action.toApiParam()}));
final SubscriptionListResponse subscriptionResponse =
serializers.deserializeWith(SubscriptionListResponse.serializer, response);
return subscriptionResponse.data.toList();
}
Future<List<SubscriptionEntity>> bulkAction(
Credentials credentials, List<String> ids, EntityAction action) async {
final url = credentials.url + '/subscriptions/bulk';
final dynamic response = await webClient.post(url, credentials.token,
data: json.encode({'ids': ids, 'action': action.toApiParam()}));
final SubscriptionListResponse subscriptionResponse = serializers
.deserializeWith(SubscriptionListResponse.serializer, response);
return subscriptionResponse.data.toList();
}
Future<SubscriptionEntity> saveData(
Credentials credentials, SubscriptionEntity subscription) async {
final data = serializers.serializeWith(SubscriptionEntity.serializer, subscription);
final data =
serializers.serializeWith(SubscriptionEntity.serializer, subscription);
dynamic response;
if (subscription.isNew) {
response = await webClient.post(
credentials.url + '/subscriptions',
credentials.token,
credentials.url + '/subscriptions', credentials.token,
data: json.encode(data));
} else {
final url = '${credentials.url}/subscriptions/${subscription.id}';
response = await webClient.put(url, credentials.token, data: json.encode(data));
response =
await webClient.put(url, credentials.token, data: json.encode(data));
}
final SubscriptionItemResponse subscriptionResponse =
serializers.deserializeWith(SubscriptionItemResponse.serializer, response);
final SubscriptionItemResponse subscriptionResponse = serializers
.deserializeWith(SubscriptionItemResponse.serializer, response);
return subscriptionResponse.data;
}

View File

@ -69,8 +69,7 @@ void main({bool isTesting = false}) async {
..addAll(createStoreSettingsMiddleware())
..addAll(createStoreReportsMiddleware())
// STARTER: middleware - do not remove comment
..addAll(createStoreSubscriptionsMiddleware())
..addAll(createStoreSubscriptionsMiddleware())
..addAll(createStoreTaskStatusesMiddleware())
..addAll(createStoreExpenseCategoriesMiddleware())
..addAll(createStoreRecurringInvoicesMiddleware())

View File

@ -343,9 +343,12 @@ class InvoiceNinjaAppState extends State<InvoiceNinjaApp> {
QuoteEmailScreen.route: (context) => QuoteEmailScreen(),
QuotePdfScreen.route: (context) => QuotePdfScreen(),
// STARTER: routes - do not remove comment
SubscriptionScreen.route: (context) => SubscriptionScreenBuilder(),
SubscriptionViewScreen.route: (context) => SubscriptionViewScreen(),
SubscriptionEditScreen.route: (context) => SubscriptionEditScreen(),
SubscriptionScreen.route: (context) =>
SubscriptionScreenBuilder(),
SubscriptionViewScreen.route: (context) =>
SubscriptionViewScreen(),
SubscriptionEditScreen.route: (context) =>
SubscriptionEditScreen(),
TaskStatusScreen.route: (context) =>
TaskStatusScreenBuilder(),

View File

@ -100,7 +100,9 @@ final lastErrorReducer = combineReducers<String>([
return '${action.error}';
}),
// STARTER: errors - do not remove comment
TypedReducer<String, LoadSubscriptionsFailure>((state, action) { return '${action.error}'; }),
TypedReducer<String, LoadSubscriptionsFailure>((state, action) {
return '${action.error}';
}),
TypedReducer<String, LoadTaskStatusesFailure>((state, action) {
return '${action.error}';

View File

@ -51,7 +51,9 @@ import 'package:invoiceninja_flutter/ui/group/edit/group_edit_vm.dart';
import 'package:invoiceninja_flutter/ui/product/edit/product_edit_vm.dart';
// STARTER: import - do not remove comment
import 'package:invoiceninja_flutter/redux/subscription/subscription_state.dart';import 'package:invoiceninja_flutter/ui/subscription/edit/subscription_edit_vm.dart';import 'package:invoiceninja_flutter/redux/subscription/subscription_selectors.dart';
import 'package:invoiceninja_flutter/redux/subscription/subscription_state.dart';
import 'package:invoiceninja_flutter/ui/subscription/edit/subscription_edit_vm.dart';
import 'package:invoiceninja_flutter/redux/subscription/subscription_selectors.dart';
import 'package:invoiceninja_flutter/redux/task_status/task_status_state.dart';
import 'package:invoiceninja_flutter/ui/recurring_invoice/recurring_invoice_screen.dart';
@ -275,8 +277,8 @@ abstract class AppState implements Built<AppState, AppStateBuilder> {
case EntityType.invoice:
return invoiceState.map;
// STARTER: states switch map - do not remove comment
case EntityType.subscription:
return subscriptionState.map;
case EntityType.subscription:
return subscriptionState.map;
case EntityType.taskStatus:
return taskStatusState.map;
@ -349,8 +351,8 @@ return subscriptionState.map;
case EntityType.invoice:
return invoiceState.list;
// STARTER: states switch list - do not remove comment
case EntityType.subscription:
return subscriptionState.list;
case EntityType.subscription:
return subscriptionState.list;
case EntityType.taskStatus:
return taskStatusState.list;
@ -414,8 +416,8 @@ return subscriptionState.list;
case EntityType.invoice:
return invoiceUIState;
// STARTER: states switch - do not remove comment
case EntityType.subscription:
return subscriptionUIState;
case EntityType.subscription:
return subscriptionUIState;
case EntityType.taskStatus:
return taskStatusUIState;
@ -483,10 +485,10 @@ return subscriptionUIState;
ListUIState get invoiceListState => uiState.invoiceUIState.listUIState;
// STARTER: state getters - do not remove comment
SubscriptionState get subscriptionState => userCompanyState.subscriptionState;
ListUIState get subscriptionListState => uiState.subscriptionUIState.listUIState;
SubscriptionUIState get subscriptionUIState => uiState.subscriptionUIState;
SubscriptionState get subscriptionState => userCompanyState.subscriptionState;
ListUIState get subscriptionListState =>
uiState.subscriptionUIState.listUIState;
SubscriptionUIState get subscriptionUIState => uiState.subscriptionUIState;
TaskStatusState get taskStatusState => userCompanyState.taskStatusState;
@ -644,7 +646,9 @@ SubscriptionUIState get subscriptionUIState => uiState.subscriptionUIState;
case CreditEditScreen.route:
return hasCreditChanges(creditUIState.editing, creditState.map);
// STARTER: has changes - do not remove comment
case SubscriptionEditScreen.route: return hasSubscriptionChanges(subscriptionUIState.editing, subscriptionState.map);
case SubscriptionEditScreen.route:
return hasSubscriptionChanges(
subscriptionUIState.editing, subscriptionState.map);
case TaskStatusEditScreen.route:
return hasTaskStatusChanges(

View File

@ -52,8 +52,8 @@ UserCompanyState companyReducer(UserCompanyState state, dynamic action) {
..vendorState.replace(vendorsReducer(state.vendorState, action))
..taskState.replace(tasksReducer(state.taskState, action))
// STARTER: reducer - do not remove comment
..subscriptionState.replace(subscriptionsReducer(state.subscriptionState, action))
..subscriptionState
.replace(subscriptionsReducer(state.subscriptionState, action))
..taskStatusState
.replace(taskStatusesReducer(state.taskStatusState, action))
..expenseCategoryState

View File

@ -52,7 +52,7 @@ abstract class UserCompanyState
paymentState: PaymentState(),
quoteState: QuoteState(),
// STARTER: constructor - do not remove comment
subscriptionState: SubscriptionState(),
subscriptionState: SubscriptionState(),
taskStatusState: TaskStatusState(),
@ -103,7 +103,7 @@ subscriptionState: SubscriptionState(),
QuoteState get quoteState;
// STARTER: fields - do not remove comment
SubscriptionState get subscriptionState;
SubscriptionState get subscriptionState;
TaskStatusState get taskStatusState;

View File

@ -9,7 +9,8 @@ import 'package:invoiceninja_flutter/utils/completers.dart';
import 'package:invoiceninja_flutter/utils/localization.dart';
import 'package:invoiceninja_flutter/ui/app/entities/entity_actions_dialog.dart';
class ViewSubscriptionList extends AbstractNavigatorAction implements PersistUI, StopLoading {
class ViewSubscriptionList extends AbstractNavigatorAction
implements PersistUI, StopLoading {
ViewSubscriptionList({
@required NavigatorState navigator,
this.force = false,
@ -120,7 +121,6 @@ class LoadSubscriptionsSuccess implements StopLoading {
}
}
class SaveSubscriptionRequest implements StartSaving {
SaveSubscriptionRequest({this.completer, this.subscription});
@ -141,7 +141,7 @@ class AddSubscriptionSuccess implements StopSaving, PersistData, PersistUI {
}
class SaveSubscriptionFailure implements StopSaving {
SaveSubscriptionFailure (this.error);
SaveSubscriptionFailure(this.error);
final Object error;
}
@ -203,7 +203,6 @@ class RestoreSubscriptionsFailure implements StopSaving {
final List<SubscriptionEntity> subscriptions;
}
class FilterSubscriptions implements PersistUI {
FilterSubscriptions(this.filter);
@ -274,7 +273,6 @@ class UpdateSubscriptionTab implements PersistUI {
void handleSubscriptionAction(
BuildContext context, List<BaseEntity> subscriptions, EntityAction action) {
if (subscriptions.isEmpty) {
return;
}
@ -283,7 +281,8 @@ void handleSubscriptionAction(
final state = store.state;
final localization = AppLocalization.of(context);
final subscription = subscriptions.first as SubscriptionEntity;
final subscriptionIds = subscriptions.map((subscription) => subscription.id).toList();
final subscriptionIds =
subscriptions.map((subscription) => subscription.id).toList();
switch (action) {
case EntityAction.edit:
@ -291,15 +290,18 @@ void handleSubscriptionAction(
break;
case EntityAction.restore:
store.dispatch(RestoreSubscriptionsRequest(
snackBarCompleter<Null>(context, localization.restoredSubscription), subscriptionIds));
snackBarCompleter<Null>(context, localization.restoredSubscription),
subscriptionIds));
break;
case EntityAction.archive:
store.dispatch(ArchiveSubscriptionsRequest(
snackBarCompleter<Null>(context, localization.archivedSubscription), subscriptionIds));
snackBarCompleter<Null>(context, localization.archivedSubscription),
subscriptionIds));
break;
case EntityAction.delete:
store.dispatch(DeleteSubscriptionsRequest(
snackBarCompleter<Null>(context, localization.deletedSubscription), subscriptionIds));
snackBarCompleter<Null>(context, localization.deletedSubscription),
subscriptionIds));
break;
case EntityAction.toggleMultiselect:
if (!store.state.subscriptionListState.isInMultiselect()) {
@ -312,23 +314,21 @@ void handleSubscriptionAction(
for (final subscription in subscriptions) {
if (!store.state.subscriptionListState.isSelected(subscription.id)) {
store.dispatch(
AddToSubscriptionMultiselect(entity: subscription));
store.dispatch(AddToSubscriptionMultiselect(entity: subscription));
} else {
store.dispatch(
RemoveFromSubscriptionMultiselect(entity: subscription));
}
}
break;
case EntityAction.more:
showEntityActionsDialog(
entities: [subscription],
context: context,
);
break;
default:
print('Error: unhandled action $action in subscription_actions');
break;
case EntityAction.more:
showEntityActionsDialog(
entities: [subscription],
context: context,
);
break;
default:
print('Error: unhandled action $action in subscription_actions');
break;
}
}

View File

@ -41,7 +41,6 @@ List<Middleware<AppState>> createStoreSubscriptionsMiddleware([
Middleware<AppState> _editSubscription() {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as EditSubscription;
next(action);
@ -49,22 +48,22 @@ Middleware<AppState> _editSubscription() {
store.dispatch(UpdateCurrentRoute(SubscriptionEditScreen.route));
if (isMobile(action.context)) {
action.navigator.pushNamed(SubscriptionEditScreen.route);
action.navigator.pushNamed(SubscriptionEditScreen.route);
}
};
}
Middleware<AppState> _viewSubscription() {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) async {
final action = dynamicAction as ViewSubscription;
return (Store<AppState> store, dynamic dynamicAction,
NextDispatcher next) async {
final action = dynamicAction as ViewSubscription;
next(action);
store.dispatch(UpdateCurrentRoute(SubscriptionViewScreen.route));
if (isMobile(action.context)) {
Navigator.of(action.context).pushNamed(SubscriptionViewScreen.route);
Navigator.of(action.context).pushNamed(SubscriptionViewScreen.route);
}
};
}
@ -91,11 +90,12 @@ Middleware<AppState> _viewSubscriptionList() {
Middleware<AppState> _archiveSubscription(SubscriptionRepository repository) {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as ArchiveSubscriptionsRequest;
final prevSubscriptions =
action.subscriptionIds.map((id) => store.state.subscriptionState.map[id]).toList();
final prevSubscriptions = action.subscriptionIds
.map((id) => store.state.subscriptionState.map[id])
.toList();
repository
.bulkAction(
store.state.credentials, action.subscriptionIds, EntityAction.archive)
.bulkAction(store.state.credentials, action.subscriptionIds,
EntityAction.archive)
.then((List<SubscriptionEntity> subscriptions) {
store.dispatch(ArchiveSubscriptionsSuccess(subscriptions));
if (action.completer != null) {
@ -116,11 +116,12 @@ Middleware<AppState> _archiveSubscription(SubscriptionRepository repository) {
Middleware<AppState> _deleteSubscription(SubscriptionRepository repository) {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as DeleteSubscriptionsRequest;
final prevSubscriptions =
action.subscriptionIds.map((id) => store.state.subscriptionState.map[id]).toList();
final prevSubscriptions = action.subscriptionIds
.map((id) => store.state.subscriptionState.map[id])
.toList();
repository
.bulkAction(
store.state.credentials, action.subscriptionIds, EntityAction.delete)
.bulkAction(store.state.credentials, action.subscriptionIds,
EntityAction.delete)
.then((List<SubscriptionEntity> subscriptions) {
store.dispatch(DeleteSubscriptionsSuccess(subscriptions));
if (action.completer != null) {
@ -141,11 +142,12 @@ Middleware<AppState> _deleteSubscription(SubscriptionRepository repository) {
Middleware<AppState> _restoreSubscription(SubscriptionRepository repository) {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as RestoreSubscriptionsRequest;
final prevSubscriptions =
action.subscriptionIds.map((id) => store.state.subscriptionState.map[id]).toList();
final prevSubscriptions = action.subscriptionIds
.map((id) => store.state.subscriptionState.map[id])
.toList();
repository
.bulkAction(
store.state.credentials, action.subscriptionIds, EntityAction.restore)
.bulkAction(store.state.credentials, action.subscriptionIds,
EntityAction.restore)
.then((List<SubscriptionEntity> subscriptions) {
store.dispatch(RestoreSubscriptionsSuccess(subscriptions));
if (action.completer != null) {
@ -167,8 +169,7 @@ Middleware<AppState> _saveSubscription(SubscriptionRepository repository) {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as SaveSubscriptionRequest;
repository
.saveData(
store.state.credentials, action.subscription)
.saveData(store.state.credentials, action.subscription)
.then((SubscriptionEntity subscription) {
if (action.subscription.isNew) {
store.dispatch(AddSubscriptionSuccess(subscription));
@ -177,7 +178,6 @@ Middleware<AppState> _saveSubscription(SubscriptionRepository repository) {
}
action.completer.complete(subscription);
}).catchError((Object error) {
print(error);
store.dispatch(SaveSubscriptionFailure(error));
@ -190,10 +190,10 @@ Middleware<AppState> _saveSubscription(SubscriptionRepository repository) {
Middleware<AppState> _loadSubscription(SubscriptionRepository repository) {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as LoadSubscription;
final action = dynamicAction as LoadSubscription;
final AppState state = store.state;
store.dispatch(LoadSubscriptionRequest());
store.dispatch(LoadSubscriptionRequest());
repository
.loadItem(state.credentials, action.subscriptionId)
.then((subscription) {
@ -216,14 +216,11 @@ store.dispatch(LoadSubscriptionRequest());
Middleware<AppState> _loadSubscriptions(SubscriptionRepository repository) {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as LoadSubscriptions;
final action = dynamicAction as LoadSubscriptions;
final AppState state = store.state;
store.dispatch(LoadSubscriptionsRequest());
repository
.loadList(state.credentials)
.then((data) {
repository.loadList(state.credentials).then((data) {
store.dispatch(LoadSubscriptionsSuccess(data));
if (action.completer != null) {

View File

@ -14,8 +14,7 @@ EntityUIState subscriptionUIReducer(SubscriptionUIState state, dynamic action) {
..listUIState.replace(subscriptionListReducer(state.listUIState, action))
..editing.replace(editingReducer(state.editing, action))
..selectedId = selectedIdReducer(state.selectedId, action)
..tabIndex = tabIndexReducer(state.tabIndex, action)
);
..tabIndex = tabIndexReducer(state.tabIndex, action));
}
final tabIndexReducer = combineReducers<int>([
@ -36,55 +35,66 @@ Reducer<String> selectedIdReducer = combineReducers([
(String selectedId, dynamic action) => action.subscriptionId),
TypedReducer<String, AddSubscriptionSuccess>(
(String selectedId, dynamic action) => action.subscription.id),
TypedReducer<String, SelectCompany>((selectedId, action) => action.clearSelection ? '' : selectedId),
TypedReducer<String, SelectCompany>(
(selectedId, action) => action.clearSelection ? '' : selectedId),
TypedReducer<String, ClearEntityFilter>((selectedId, action) => ''),
TypedReducer<String, FilterByEntity>((selectedId, action) =>
action
.clearSelection
? ''
: action.entityType == EntityType.subscription ? action.entityId : selectedId),
TypedReducer<String, FilterByEntity>(
(selectedId, action) => action.clearSelection
? ''
: action.entityType == EntityType.subscription
? action.entityId
: selectedId),
]);
final editingReducer = combineReducers<SubscriptionEntity>([
TypedReducer<SubscriptionEntity, SaveSubscriptionSuccess>(_updateEditing),
TypedReducer<SubscriptionEntity, AddSubscriptionSuccess>(_updateEditing),
TypedReducer<SubscriptionEntity, RestoreSubscriptionsSuccess>((subscriptions, action) {
return action.subscriptions[0];
}),
TypedReducer<SubscriptionEntity, ArchiveSubscriptionsSuccess>((subscriptions, action) {
return action.subscriptions[0];
}),
TypedReducer<SubscriptionEntity, DeleteSubscriptionsSuccess>((subscriptions, action) {
return action.subscriptions[0];
}),
TypedReducer<SubscriptionEntity, RestoreSubscriptionsSuccess>(
(subscriptions, action) {
return action.subscriptions[0];
}),
TypedReducer<SubscriptionEntity, ArchiveSubscriptionsSuccess>(
(subscriptions, action) {
return action.subscriptions[0];
}),
TypedReducer<SubscriptionEntity, DeleteSubscriptionsSuccess>(
(subscriptions, action) {
return action.subscriptions[0];
}),
TypedReducer<SubscriptionEntity, EditSubscription>(_updateEditing),
TypedReducer<SubscriptionEntity, UpdateSubscription>((subscription, action) {
return action.subscription.rebuild((b) => b..isChanged = true);
}),
return action.subscription.rebuild((b) => b..isChanged = true);
}),
TypedReducer<SubscriptionEntity, DiscardChanges>(_clearEditing),
]);
SubscriptionEntity _clearEditing(SubscriptionEntity subscription, dynamic action) {
SubscriptionEntity _clearEditing(
SubscriptionEntity subscription, dynamic action) {
return SubscriptionEntity();
}
SubscriptionEntity _updateEditing(SubscriptionEntity subscription, dynamic action) {
SubscriptionEntity _updateEditing(
SubscriptionEntity subscription, dynamic action) {
return action.subscription;
}
final subscriptionListReducer = combineReducers<ListUIState>([
TypedReducer<ListUIState, SortSubscriptions>(_sortSubscriptions),
TypedReducer<ListUIState, FilterSubscriptionsByState>(_filterSubscriptionsByState),
TypedReducer<ListUIState, FilterSubscriptionsByState>(
_filterSubscriptionsByState),
TypedReducer<ListUIState, FilterSubscriptions>(_filterSubscriptions),
TypedReducer<ListUIState, FilterSubscriptionsByCustom1>(_filterSubscriptionsByCustom1),
TypedReducer<ListUIState, FilterSubscriptionsByCustom2>(_filterSubscriptionsByCustom2),
TypedReducer<ListUIState, StartSubscriptionMultiselect>(_startListMultiselect),
TypedReducer<ListUIState, AddToSubscriptionMultiselect>(_addToListMultiselect),
TypedReducer<ListUIState, FilterSubscriptionsByCustom1>(
_filterSubscriptionsByCustom1),
TypedReducer<ListUIState, FilterSubscriptionsByCustom2>(
_filterSubscriptionsByCustom2),
TypedReducer<ListUIState, StartSubscriptionMultiselect>(
_startListMultiselect),
TypedReducer<ListUIState, AddToSubscriptionMultiselect>(
_addToListMultiselect),
TypedReducer<ListUIState, RemoveFromSubscriptionMultiselect>(
_removeFromListMultiselect),
TypedReducer<ListUIState, ClearSubscriptionMultiselect>(_clearListMultiselect),
TypedReducer<ListUIState, ClearSubscriptionMultiselect>(
_clearListMultiselect),
]);
ListUIState _filterSubscriptionsByCustom1(
@ -93,7 +103,8 @@ ListUIState _filterSubscriptionsByCustom1(
return subscriptionListState
.rebuild((b) => b..custom1Filters.remove(action.value));
} else {
return subscriptionListState.rebuild((b) => b..custom1Filters.add(action.value));
return subscriptionListState
.rebuild((b) => b..custom1Filters.add(action.value));
}
}
@ -103,27 +114,33 @@ ListUIState _filterSubscriptionsByCustom2(
return subscriptionListState
.rebuild((b) => b..custom2Filters.remove(action.value));
} else {
return subscriptionListState.rebuild((b) => b..custom2Filters.add(action.value));
return subscriptionListState
.rebuild((b) => b..custom2Filters.add(action.value));
}
}
ListUIState _filterSubscriptionsByState(
ListUIState subscriptionListState, FilterSubscriptionsByState action) {
if (subscriptionListState.stateFilters.contains(action.state)) {
return subscriptionListState.rebuild((b) => b..stateFilters.remove(action.state));
return subscriptionListState
.rebuild((b) => b..stateFilters.remove(action.state));
} else {
return subscriptionListState.rebuild((b) => b..stateFilters.add(action.state));
return subscriptionListState
.rebuild((b) => b..stateFilters.add(action.state));
}
}
ListUIState _filterSubscriptions(ListUIState subscriptionListState, FilterSubscriptions action) {
return subscriptionListState.rebuild((b) => b..filter = action.filter
..filterClearedAt = action.filter == null
? DateTime.now().millisecondsSinceEpoch
: subscriptionListState.filterClearedAt);
ListUIState _filterSubscriptions(
ListUIState subscriptionListState, FilterSubscriptions action) {
return subscriptionListState.rebuild((b) => b
..filter = action.filter
..filterClearedAt = action.filter == null
? DateTime.now().millisecondsSinceEpoch
: subscriptionListState.filterClearedAt);
}
ListUIState _sortSubscriptions(ListUIState subscriptionListState, SortSubscriptions action) {
ListUIState _sortSubscriptions(
ListUIState subscriptionListState, SortSubscriptions action) {
return subscriptionListState.rebuild((b) => b
..sortAscending = b.sortField != action.field || !b.sortAscending
..sortField = action.field);
@ -136,8 +153,7 @@ ListUIState _startListMultiselect(
ListUIState _addToListMultiselect(
ListUIState productListState, AddToSubscriptionMultiselect action) {
return productListState
.rebuild((b) => b..selectedIds.add(action.entity.id));
return productListState.rebuild((b) => b..selectedIds.add(action.entity.id));
}
ListUIState _removeFromListMultiselect(
@ -154,12 +170,17 @@ ListUIState _clearListMultiselect(
final subscriptionsReducer = combineReducers<SubscriptionState>([
TypedReducer<SubscriptionState, SaveSubscriptionSuccess>(_updateSubscription),
TypedReducer<SubscriptionState, AddSubscriptionSuccess>(_addSubscription),
TypedReducer<SubscriptionState, LoadSubscriptionsSuccess>(_setLoadedSubscriptions),
TypedReducer<SubscriptionState, LoadSubscriptionSuccess>(_setLoadedSubscription),
TypedReducer<SubscriptionState, LoadSubscriptionsSuccess>(
_setLoadedSubscriptions),
TypedReducer<SubscriptionState, LoadSubscriptionSuccess>(
_setLoadedSubscription),
TypedReducer<SubscriptionState, LoadCompanySuccess>(_setLoadedCompany),
TypedReducer<SubscriptionState, ArchiveSubscriptionsSuccess>(_archiveSubscriptionSuccess),
TypedReducer<SubscriptionState, DeleteSubscriptionsSuccess>(_deleteSubscriptionSuccess),
TypedReducer<SubscriptionState, RestoreSubscriptionsSuccess>(_restoreSubscriptionSuccess),
TypedReducer<SubscriptionState, ArchiveSubscriptionsSuccess>(
_archiveSubscriptionSuccess),
TypedReducer<SubscriptionState, DeleteSubscriptionsSuccess>(
_deleteSubscriptionSuccess),
TypedReducer<SubscriptionState, RestoreSubscriptionsSuccess>(
_restoreSubscriptionSuccess),
]);
SubscriptionState _archiveSubscriptionSuccess(
@ -189,21 +210,23 @@ SubscriptionState _restoreSubscriptionSuccess(
});
}
SubscriptionState _addSubscription(SubscriptionState subscriptionState, AddSubscriptionSuccess action) {
SubscriptionState _addSubscription(
SubscriptionState subscriptionState, AddSubscriptionSuccess action) {
return subscriptionState.rebuild((b) => b
..map[action.subscription.id] = action.subscription
..list.add(action.subscription.id));
}
SubscriptionState _updateSubscription(SubscriptionState subscriptionState, SaveSubscriptionSuccess action) {
return subscriptionState.rebuild((b) => b
..map[action.subscription.id] = action.subscription);
SubscriptionState _updateSubscription(
SubscriptionState subscriptionState, SaveSubscriptionSuccess action) {
return subscriptionState
.rebuild((b) => b..map[action.subscription.id] = action.subscription);
}
SubscriptionState _setLoadedSubscription(
SubscriptionState subscriptionState, LoadSubscriptionSuccess action) {
return subscriptionState.rebuild((b) => b
..map[action.subscription.id] = action.subscription);
return subscriptionState
.rebuild((b) => b..map[action.subscription.id] = action.subscription);
}
SubscriptionState _setLoadedSubscriptions(
@ -211,7 +234,7 @@ SubscriptionState _setLoadedSubscriptions(
subscriptionState.loadSubscriptions(action.subscriptions);
SubscriptionState _setLoadedCompany(
SubscriptionState subscriptionState, LoadCompanySuccess action) {
final company = action.userCompany.company;
return subscriptionState.loadSubscriptions(company.subscriptions);
SubscriptionState subscriptionState, LoadCompanySuccess action) {
final company = action.userCompany.company;
return subscriptionState.loadSubscriptions(company.subscriptions);
}

View File

@ -329,7 +329,17 @@ Reducer<BuiltList<HistoryRecord>> historyReducer = combineReducers([
_addToHistory(historyList,
HistoryRecord(id: action.group.id, entityType: EntityType.group))),
// STARTER: history - do not remove comment
TypedReducer<BuiltList<HistoryRecord>, ViewSubscription>((historyList, action) => _addToHistory(historyList, HistoryRecord(id: action.subscriptionId, entityType: EntityType.subscription))),TypedReducer<BuiltList<HistoryRecord>, EditSubscription>((historyList, action) => _addToHistory(historyList, HistoryRecord(id: action.subscription.id, entityType: EntityType.subscription))),
TypedReducer<BuiltList<HistoryRecord>, ViewSubscription>(
(historyList, action) => _addToHistory(
historyList,
HistoryRecord(
id: action.subscriptionId, entityType: EntityType.subscription))),
TypedReducer<BuiltList<HistoryRecord>, EditSubscription>(
(historyList, action) => _addToHistory(
historyList,
HistoryRecord(
id: action.subscription.id,
entityType: EntityType.subscription))),
TypedReducer<BuiltList<HistoryRecord>, ViewTaskStatus>(
(historyList, action) => _addToHistory(

View File

@ -88,8 +88,8 @@ UIState uiReducer(UIState state, dynamic action) {
.replace(dashboardUIReducer(state.dashboardUIState, action))
..reportsUIState.replace(reportsUIReducer(state.reportsUIState, action))
// STARTER: reducer - do not remove comment
..subscriptionUIState.replace(subscriptionUIReducer(state.subscriptionUIState, action))
..subscriptionUIState
.replace(subscriptionUIReducer(state.subscriptionUIState, action))
..taskStatusUIState
.replace(taskStatusUIReducer(state.taskStatusUIState, action))
..expenseCategoryUIState

View File

@ -52,7 +52,7 @@ abstract class UIState implements Built<UIState, UIStateBuilder> {
clientUIState: ClientUIState(),
invoiceUIState: InvoiceUIState(),
// STARTER: constructor - do not remove comment
subscriptionUIState: SubscriptionUIState(),
subscriptionUIState: SubscriptionUIState(),
taskStatusUIState: TaskStatusUIState(),
expenseCategoryUIState: ExpenseCategoryUIState(),
@ -112,7 +112,7 @@ subscriptionUIState: SubscriptionUIState(),
InvoiceUIState get invoiceUIState;
// STARTER: properties - do not remove comment
SubscriptionUIState get subscriptionUIState;
SubscriptionUIState get subscriptionUIState;
TaskStatusUIState get taskStatusUIState;

View File

@ -386,13 +386,6 @@ class MenuDrawer extends StatelessWidget {
iconTooltip: localization.newExpense,
),
// STARTER: menu - do not remove comment
DrawerTile(
company: company,
entityType: EntityType.subscription,
icon: getEntityIcon(EntityType.subscription),
title: localization.subscriptions,
),
DrawerTile(
company: company,
icon: getEntityIcon(EntityType.reports),

View File

@ -115,8 +115,7 @@ class LoginVM {
}) async {
try {
await GoogleOAuth.signOut();
final signedIn = await GoogleOAuth.signIn(
(idToken, accessToken) {
final signedIn = await GoogleOAuth.signIn((idToken, accessToken) {
if (idToken.isEmpty || accessToken.isEmpty) {
GoogleOAuth.signOut();
completer.completeError(

View File

@ -63,26 +63,31 @@ class SubscriptionEditVM {
onChanged: (SubscriptionEntity subscription) {
store.dispatch(UpdateSubscription(subscription));
},
onCancelPressed: (BuildContext context) {
createEntity(context: context, entity: SubscriptionEntity(), force: true);
},
onCancelPressed: (BuildContext context) {
createEntity(
context: context, entity: SubscriptionEntity(), force: true);
},
onSavePressed: (BuildContext context) {
final localization = AppLocalization.of(context);
final Completer<SubscriptionEntity> completer = new Completer<SubscriptionEntity>();
store.dispatch(SaveSubscriptionRequest(completer: completer, subscription: subscription));
final Completer<SubscriptionEntity> completer =
new Completer<SubscriptionEntity>();
store.dispatch(SaveSubscriptionRequest(
completer: completer, subscription: subscription));
return completer.future.then((savedSubscription) {
showToast(subscription.isNew
? localization.createdSubscription
: localization.updatedSubscription);
? localization.createdSubscription
: localization.updatedSubscription);
if (isMobile(context)) {
store.dispatch(UpdateCurrentRoute(SubscriptionViewScreen.route));
if (subscription.isNew) {
Navigator.of(context).pushReplacementNamed(SubscriptionViewScreen.route);
} else {
Navigator.of(context).pop(savedSubscription);
}
store.dispatch(UpdateCurrentRoute(SubscriptionViewScreen.route));
if (subscription.isNew) {
Navigator.of(context)
.pushReplacementNamed(SubscriptionViewScreen.route);
} else {
Navigator.of(context).pop(savedSubscription);
}
} else {
viewEntity(context: context, entity: savedSubscription, force: true);
viewEntity(
context: context, entity: savedSubscription, force: true);
}
}).catchError((Object error) {
showDialog<ErrorDialog>(

View File

@ -3,11 +3,8 @@ import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/ui/app/presenters/entity_presenter.dart';
class SubscriptionPresenter extends EntityPresenter {
static List<String> getDefaultTableFields(UserCompanyEntity userCompany) {
return [
];
return [];
}
static List<String> getAllTableFields(UserCompanyEntity userCompany) {

View File

@ -13,7 +13,10 @@ import 'package:invoiceninja_flutter/ui/subscription/view/subscription_view.dart
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
class SubscriptionViewScreen extends StatelessWidget {
const SubscriptionViewScreen({Key key, this.isFilter = false,}) : super(key: key);
const SubscriptionViewScreen({
Key key,
this.isFilter = false,
}) : super(key: key);
static const String route = '/subscription/view';
final bool isFilter;
@ -34,7 +37,6 @@ class SubscriptionViewScreen extends StatelessWidget {
}
class SubscriptionViewVM {
SubscriptionViewVM({
@required this.state,
@required this.subscription,
@ -48,27 +50,29 @@ class SubscriptionViewVM {
factory SubscriptionViewVM.fromStore(Store<AppState> store) {
final state = store.state;
final subscription = state.subscriptionState.map[state.subscriptionUIState.selectedId] ??
SubscriptionEntity(id: state.subscriptionUIState.selectedId);
final subscription =
state.subscriptionState.map[state.subscriptionUIState.selectedId] ??
SubscriptionEntity(id: state.subscriptionUIState.selectedId);
Future<Null> _handleRefresh(BuildContext context) {
final completer = snackBarCompleter<Null>(
context, AppLocalization.of(context).refreshComplete);
store.dispatch(LoadSubscription(completer: completer, subscriptionId: subscription.id));
store.dispatch(LoadSubscription(
completer: completer, subscriptionId: subscription.id));
return completer.future;
}
return SubscriptionViewVM(
state: state,
company: state.company,
isSaving: state.isSaving,
isLoading: state.isLoading,
isDirty: subscription.isNew,
subscription: subscription,
onRefreshed: (context) => _handleRefresh(context),
state: state,
company: state.company,
isSaving: state.isSaving,
isLoading: state.isLoading,
isDirty: subscription.isNew,
subscription: subscription,
onRefreshed: (context) => _handleRefresh(context),
onEntityAction: (BuildContext context, EntityAction action) =>
handleEntitiesActions(context, [subscription], action, autoPop: true),
);
);
}
final AppState state;