This commit is contained in:
Hillel Coren 2020-03-03 17:10:12 +02:00
parent 0729a5a785
commit ebef3441aa
52 changed files with 3577 additions and 140 deletions

View File

@ -0,0 +1,117 @@
import 'package:built_value/built_value.dart';
import 'package:built_collection/built_collection.dart';
import 'package:built_value/serializer.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/utils/formatting.dart';
part 'design_model.g.dart';
abstract class DesignListResponse
implements Built<DesignListResponse, DesignListResponseBuilder> {
factory DesignListResponse([void updates(DesignListResponseBuilder b)]) =
_$DesignListResponse;
DesignListResponse._();
BuiltList<DesignEntity> get data;
static Serializer<DesignListResponse> get serializer =>
_$designListResponseSerializer;
}
abstract class DesignItemResponse
implements Built<DesignItemResponse, DesignItemResponseBuilder> {
factory DesignItemResponse([void updates(DesignItemResponseBuilder b)]) =
_$DesignItemResponse;
DesignItemResponse._();
DesignEntity get data;
static Serializer<DesignItemResponse> get serializer =>
_$designItemResponseSerializer;
}
class DesignFields {
static const String name = 'name';
}
abstract class DesignEntity extends Object
with BaseEntity
implements Built<DesignEntity, DesignEntityBuilder> {
factory DesignEntity() {
return _$DesignEntity._(
id: BaseEntity.nextId,
isChanged: false,
// STARTER: constructor - do not remove comment
);
}
DesignEntity._();
String get displayName {
// STARTER: display name - do not remove comment
}
int compareTo(DesignEntity design, String sortField, bool sortAscending) {
int response = 0;
DesignEntity designA = sortAscending ? this : design;
DesignEntity designB = sortAscending ? design : this;
switch (sortField) {
// STARTER: sort switch - do not remove comment
}
if (response == 0) {
// STARTER: sort default - do not remove comment
} else {
return response;
}
}
bool matchesSearch(String filter) {
if (filter == null || filter.isEmpty) {
return true;
}
filter = filter.toLowerCase();
// STARTER: filter - do not remove comment
return false;
}
@override
bool matchesFilter(String filter) {
if (filter == null || filter.isEmpty) {
return true;
}
filter = filter.toLowerCase();
return false;
}
@override
String matchesFilterValue(String filter) {
if (filter == null || filter.isEmpty) {
return null;
}
filter = filter.toLowerCase();
return null;
}
@override
String get listDisplayName => null;
@override
double get listDisplayAmount => null;
@override
FormatNumberType get listDisplayAmountType => null;
static Serializer<DesignEntity> get serializer => _$designEntitySerializer;
}

View File

@ -0,0 +1,598 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'design_model.dart';
// **************************************************************************
// BuiltValueGenerator
// **************************************************************************
Serializer<DesignListResponse> _$designListResponseSerializer =
new _$DesignListResponseSerializer();
Serializer<DesignItemResponse> _$designItemResponseSerializer =
new _$DesignItemResponseSerializer();
Serializer<DesignEntity> _$designEntitySerializer =
new _$DesignEntitySerializer();
class _$DesignListResponseSerializer
implements StructuredSerializer<DesignListResponse> {
@override
final Iterable<Type> types = const [DesignListResponse, _$DesignListResponse];
@override
final String wireName = 'DesignListResponse';
@override
Iterable<Object> serialize(Serializers serializers, DesignListResponse object,
{FullType specifiedType = FullType.unspecified}) {
final result = <Object>[
'data',
serializers.serialize(object.data,
specifiedType:
const FullType(BuiltList, const [const FullType(DesignEntity)])),
];
return result;
}
@override
DesignListResponse deserialize(
Serializers serializers, Iterable<Object> serialized,
{FullType specifiedType = FullType.unspecified}) {
final result = new DesignListResponseBuilder();
final iterator = serialized.iterator;
while (iterator.moveNext()) {
final key = iterator.current as String;
iterator.moveNext();
final dynamic value = iterator.current;
switch (key) {
case 'data':
result.data.replace(serializers.deserialize(value,
specifiedType: const FullType(
BuiltList, const [const FullType(DesignEntity)]))
as BuiltList<Object>);
break;
}
}
return result.build();
}
}
class _$DesignItemResponseSerializer
implements StructuredSerializer<DesignItemResponse> {
@override
final Iterable<Type> types = const [DesignItemResponse, _$DesignItemResponse];
@override
final String wireName = 'DesignItemResponse';
@override
Iterable<Object> serialize(Serializers serializers, DesignItemResponse object,
{FullType specifiedType = FullType.unspecified}) {
final result = <Object>[
'data',
serializers.serialize(object.data,
specifiedType: const FullType(DesignEntity)),
];
return result;
}
@override
DesignItemResponse deserialize(
Serializers serializers, Iterable<Object> serialized,
{FullType specifiedType = FullType.unspecified}) {
final result = new DesignItemResponseBuilder();
final iterator = serialized.iterator;
while (iterator.moveNext()) {
final key = iterator.current as String;
iterator.moveNext();
final dynamic value = iterator.current;
switch (key) {
case 'data':
result.data.replace(serializers.deserialize(value,
specifiedType: const FullType(DesignEntity)) as DesignEntity);
break;
}
}
return result.build();
}
}
class _$DesignEntitySerializer implements StructuredSerializer<DesignEntity> {
@override
final Iterable<Type> types = const [DesignEntity, _$DesignEntity];
@override
final String wireName = 'DesignEntity';
@override
Iterable<Object> serialize(Serializers serializers, DesignEntity object,
{FullType specifiedType = FullType.unspecified}) {
final result = <Object>[];
if (object.isChanged != null) {
result
..add('isChanged')
..add(serializers.serialize(object.isChanged,
specifiedType: const FullType(bool)));
}
if (object.createdAt != null) {
result
..add('created_at')
..add(serializers.serialize(object.createdAt,
specifiedType: const FullType(int)));
}
if (object.updatedAt != null) {
result
..add('updated_at')
..add(serializers.serialize(object.updatedAt,
specifiedType: const FullType(int)));
}
if (object.archivedAt != null) {
result
..add('archived_at')
..add(serializers.serialize(object.archivedAt,
specifiedType: const FullType(int)));
}
if (object.isDeleted != null) {
result
..add('is_deleted')
..add(serializers.serialize(object.isDeleted,
specifiedType: const FullType(bool)));
}
if (object.createdUserId != null) {
result
..add('user_id')
..add(serializers.serialize(object.createdUserId,
specifiedType: const FullType(String)));
}
if (object.assignedUserId != null) {
result
..add('assigned_user_id')
..add(serializers.serialize(object.assignedUserId,
specifiedType: const FullType(String)));
}
if (object.subEntityType != null) {
result
..add('entity_type')
..add(serializers.serialize(object.subEntityType,
specifiedType: const FullType(EntityType)));
}
if (object.id != null) {
result
..add('id')
..add(serializers.serialize(object.id,
specifiedType: const FullType(String)));
}
return result;
}
@override
DesignEntity deserialize(Serializers serializers, Iterable<Object> serialized,
{FullType specifiedType = FullType.unspecified}) {
final result = new DesignEntityBuilder();
final iterator = serialized.iterator;
while (iterator.moveNext()) {
final key = iterator.current as String;
iterator.moveNext();
final dynamic value = iterator.current;
switch (key) {
case 'isChanged':
result.isChanged = serializers.deserialize(value,
specifiedType: const FullType(bool)) as bool;
break;
case 'created_at':
result.createdAt = serializers.deserialize(value,
specifiedType: const FullType(int)) as int;
break;
case 'updated_at':
result.updatedAt = serializers.deserialize(value,
specifiedType: const FullType(int)) as int;
break;
case 'archived_at':
result.archivedAt = serializers.deserialize(value,
specifiedType: const FullType(int)) as int;
break;
case 'is_deleted':
result.isDeleted = serializers.deserialize(value,
specifiedType: const FullType(bool)) as bool;
break;
case 'user_id':
result.createdUserId = serializers.deserialize(value,
specifiedType: const FullType(String)) as String;
break;
case 'assigned_user_id':
result.assignedUserId = serializers.deserialize(value,
specifiedType: const FullType(String)) as String;
break;
case 'entity_type':
result.subEntityType = serializers.deserialize(value,
specifiedType: const FullType(EntityType)) as EntityType;
break;
case 'id':
result.id = serializers.deserialize(value,
specifiedType: const FullType(String)) as String;
break;
}
}
return result.build();
}
}
class _$DesignListResponse extends DesignListResponse {
@override
final BuiltList<DesignEntity> data;
factory _$DesignListResponse(
[void Function(DesignListResponseBuilder) updates]) =>
(new DesignListResponseBuilder()..update(updates)).build();
_$DesignListResponse._({this.data}) : super._() {
if (data == null) {
throw new BuiltValueNullFieldError('DesignListResponse', 'data');
}
}
@override
DesignListResponse rebuild(
void Function(DesignListResponseBuilder) updates) =>
(toBuilder()..update(updates)).build();
@override
DesignListResponseBuilder toBuilder() =>
new DesignListResponseBuilder()..replace(this);
@override
bool operator ==(Object other) {
if (identical(other, this)) return true;
return other is DesignListResponse && data == other.data;
}
@override
int get hashCode {
return $jf($jc(0, data.hashCode));
}
@override
String toString() {
return (newBuiltValueToStringHelper('DesignListResponse')
..add('data', data))
.toString();
}
}
class DesignListResponseBuilder
implements Builder<DesignListResponse, DesignListResponseBuilder> {
_$DesignListResponse _$v;
ListBuilder<DesignEntity> _data;
ListBuilder<DesignEntity> get data =>
_$this._data ??= new ListBuilder<DesignEntity>();
set data(ListBuilder<DesignEntity> data) => _$this._data = data;
DesignListResponseBuilder();
DesignListResponseBuilder get _$this {
if (_$v != null) {
_data = _$v.data?.toBuilder();
_$v = null;
}
return this;
}
@override
void replace(DesignListResponse other) {
if (other == null) {
throw new ArgumentError.notNull('other');
}
_$v = other as _$DesignListResponse;
}
@override
void update(void Function(DesignListResponseBuilder) updates) {
if (updates != null) updates(this);
}
@override
_$DesignListResponse build() {
_$DesignListResponse _$result;
try {
_$result = _$v ?? new _$DesignListResponse._(data: data.build());
} catch (_) {
String _$failedField;
try {
_$failedField = 'data';
data.build();
} catch (e) {
throw new BuiltValueNestedFieldError(
'DesignListResponse', _$failedField, e.toString());
}
rethrow;
}
replace(_$result);
return _$result;
}
}
class _$DesignItemResponse extends DesignItemResponse {
@override
final DesignEntity data;
factory _$DesignItemResponse(
[void Function(DesignItemResponseBuilder) updates]) =>
(new DesignItemResponseBuilder()..update(updates)).build();
_$DesignItemResponse._({this.data}) : super._() {
if (data == null) {
throw new BuiltValueNullFieldError('DesignItemResponse', 'data');
}
}
@override
DesignItemResponse rebuild(
void Function(DesignItemResponseBuilder) updates) =>
(toBuilder()..update(updates)).build();
@override
DesignItemResponseBuilder toBuilder() =>
new DesignItemResponseBuilder()..replace(this);
@override
bool operator ==(Object other) {
if (identical(other, this)) return true;
return other is DesignItemResponse && data == other.data;
}
@override
int get hashCode {
return $jf($jc(0, data.hashCode));
}
@override
String toString() {
return (newBuiltValueToStringHelper('DesignItemResponse')
..add('data', data))
.toString();
}
}
class DesignItemResponseBuilder
implements Builder<DesignItemResponse, DesignItemResponseBuilder> {
_$DesignItemResponse _$v;
DesignEntityBuilder _data;
DesignEntityBuilder get data => _$this._data ??= new DesignEntityBuilder();
set data(DesignEntityBuilder data) => _$this._data = data;
DesignItemResponseBuilder();
DesignItemResponseBuilder get _$this {
if (_$v != null) {
_data = _$v.data?.toBuilder();
_$v = null;
}
return this;
}
@override
void replace(DesignItemResponse other) {
if (other == null) {
throw new ArgumentError.notNull('other');
}
_$v = other as _$DesignItemResponse;
}
@override
void update(void Function(DesignItemResponseBuilder) updates) {
if (updates != null) updates(this);
}
@override
_$DesignItemResponse build() {
_$DesignItemResponse _$result;
try {
_$result = _$v ?? new _$DesignItemResponse._(data: data.build());
} catch (_) {
String _$failedField;
try {
_$failedField = 'data';
data.build();
} catch (e) {
throw new BuiltValueNestedFieldError(
'DesignItemResponse', _$failedField, e.toString());
}
rethrow;
}
replace(_$result);
return _$result;
}
}
class _$DesignEntity extends DesignEntity {
@override
final bool isChanged;
@override
final int createdAt;
@override
final int updatedAt;
@override
final int archivedAt;
@override
final bool isDeleted;
@override
final String createdUserId;
@override
final String assignedUserId;
@override
final EntityType subEntityType;
@override
final String id;
factory _$DesignEntity([void Function(DesignEntityBuilder) updates]) =>
(new DesignEntityBuilder()..update(updates)).build();
_$DesignEntity._(
{this.isChanged,
this.createdAt,
this.updatedAt,
this.archivedAt,
this.isDeleted,
this.createdUserId,
this.assignedUserId,
this.subEntityType,
this.id})
: super._();
@override
DesignEntity rebuild(void Function(DesignEntityBuilder) updates) =>
(toBuilder()..update(updates)).build();
@override
DesignEntityBuilder toBuilder() => new DesignEntityBuilder()..replace(this);
@override
bool operator ==(Object other) {
if (identical(other, this)) return true;
return other is DesignEntity &&
isChanged == other.isChanged &&
createdAt == other.createdAt &&
updatedAt == other.updatedAt &&
archivedAt == other.archivedAt &&
isDeleted == other.isDeleted &&
createdUserId == other.createdUserId &&
assignedUserId == other.assignedUserId &&
subEntityType == other.subEntityType &&
id == other.id;
}
@override
int get hashCode {
return $jf($jc(
$jc(
$jc(
$jc(
$jc(
$jc(
$jc(
$jc($jc(0, isChanged.hashCode),
createdAt.hashCode),
updatedAt.hashCode),
archivedAt.hashCode),
isDeleted.hashCode),
createdUserId.hashCode),
assignedUserId.hashCode),
subEntityType.hashCode),
id.hashCode));
}
@override
String toString() {
return (newBuiltValueToStringHelper('DesignEntity')
..add('isChanged', isChanged)
..add('createdAt', createdAt)
..add('updatedAt', updatedAt)
..add('archivedAt', archivedAt)
..add('isDeleted', isDeleted)
..add('createdUserId', createdUserId)
..add('assignedUserId', assignedUserId)
..add('subEntityType', subEntityType)
..add('id', id))
.toString();
}
}
class DesignEntityBuilder
implements Builder<DesignEntity, DesignEntityBuilder> {
_$DesignEntity _$v;
bool _isChanged;
bool get isChanged => _$this._isChanged;
set isChanged(bool isChanged) => _$this._isChanged = isChanged;
int _createdAt;
int get createdAt => _$this._createdAt;
set createdAt(int createdAt) => _$this._createdAt = createdAt;
int _updatedAt;
int get updatedAt => _$this._updatedAt;
set updatedAt(int updatedAt) => _$this._updatedAt = updatedAt;
int _archivedAt;
int get archivedAt => _$this._archivedAt;
set archivedAt(int archivedAt) => _$this._archivedAt = archivedAt;
bool _isDeleted;
bool get isDeleted => _$this._isDeleted;
set isDeleted(bool isDeleted) => _$this._isDeleted = isDeleted;
String _createdUserId;
String get createdUserId => _$this._createdUserId;
set createdUserId(String createdUserId) =>
_$this._createdUserId = createdUserId;
String _assignedUserId;
String get assignedUserId => _$this._assignedUserId;
set assignedUserId(String assignedUserId) =>
_$this._assignedUserId = assignedUserId;
EntityType _subEntityType;
EntityType get subEntityType => _$this._subEntityType;
set subEntityType(EntityType subEntityType) =>
_$this._subEntityType = subEntityType;
String _id;
String get id => _$this._id;
set id(String id) => _$this._id = id;
DesignEntityBuilder();
DesignEntityBuilder get _$this {
if (_$v != null) {
_isChanged = _$v.isChanged;
_createdAt = _$v.createdAt;
_updatedAt = _$v.updatedAt;
_archivedAt = _$v.archivedAt;
_isDeleted = _$v.isDeleted;
_createdUserId = _$v.createdUserId;
_assignedUserId = _$v.assignedUserId;
_subEntityType = _$v.subEntityType;
_id = _$v.id;
_$v = null;
}
return this;
}
@override
void replace(DesignEntity other) {
if (other == null) {
throw new ArgumentError.notNull('other');
}
_$v = other as _$DesignEntity;
}
@override
void update(void Function(DesignEntityBuilder) updates) {
if (updates != null) updates(this);
}
@override
_$DesignEntity build() {
final _$result = _$v ??
new _$DesignEntity._(
isChanged: isChanged,
createdAt: createdAt,
updatedAt: updatedAt,
archivedAt: archivedAt,
isDeleted: isDeleted,
createdUserId: createdUserId,
assignedUserId: assignedUserId,
subEntityType: subEntityType,
id: id);
replace(_$result);
return _$result;
}
}
// ignore_for_file: always_put_control_body_on_new_line,always_specify_types,annotate_overrides,avoid_annotating_with_dynamic,avoid_as,avoid_catches_without_on_clauses,avoid_returning_this,lines_longer_than_80_chars,omit_local_variable_types,prefer_expression_function_bodies,sort_constructors_first,test_types_in_equals,unnecessary_const,unnecessary_new

View File

@ -33,11 +33,12 @@ class EntityType extends EnumClass {
static const EntityType payment = _$payment; static const EntityType payment = _$payment;
static const EntityType group = _$group; static const EntityType group = _$group;
static const EntityType user = _$user; static const EntityType user = _$user;
static const EntityType company = _$company; static const EntityType company = _$company;
static const EntityType gateway = _$gateway; static const EntityType gateway = _$gateway;
static const EntityType gatewayToken = _$gatewayToken; static const EntityType gatewayToken = _$gatewayToken;
static const EntityType invoiceItem = _$invoiceItem; static const EntityType invoiceItem = _$invoiceItem;
static const EntityType design = _$design;
// STARTER: entity type - do not remove comment
static const EntityType quoteItem = _$quoteItem; static const EntityType quoteItem = _$quoteItem;
static const EntityType contact = _$contact; static const EntityType contact = _$contact;
static const EntityType vendorContact = _$vendorContact; static const EntityType vendorContact = _$vendorContact;

View File

@ -29,6 +29,7 @@ const EntityType _$company = const EntityType._('company');
const EntityType _$gateway = const EntityType._('gateway'); const EntityType _$gateway = const EntityType._('gateway');
const EntityType _$gatewayToken = const EntityType._('gatewayToken'); const EntityType _$gatewayToken = const EntityType._('gatewayToken');
const EntityType _$invoiceItem = const EntityType._('invoiceItem'); const EntityType _$invoiceItem = const EntityType._('invoiceItem');
const EntityType _$design = const EntityType._('design');
const EntityType _$quoteItem = const EntityType._('quoteItem'); const EntityType _$quoteItem = const EntityType._('quoteItem');
const EntityType _$contact = const EntityType._('contact'); const EntityType _$contact = const EntityType._('contact');
const EntityType _$vendorContact = const EntityType._('vendorContact'); const EntityType _$vendorContact = const EntityType._('vendorContact');
@ -92,6 +93,8 @@ EntityType _$typeValueOf(String name) {
return _$gatewayToken; return _$gatewayToken;
case 'invoiceItem': case 'invoiceItem':
return _$invoiceItem; return _$invoiceItem;
case 'design':
return _$design;
case 'quoteItem': case 'quoteItem':
return _$quoteItem; return _$quoteItem;
case 'contact': case 'contact':
@ -150,6 +153,7 @@ final BuiltSet<EntityType> _$typeValues =
_$gateway, _$gateway,
_$gatewayToken, _$gatewayToken,
_$invoiceItem, _$invoiceItem,
_$design,
_$quoteItem, _$quoteItem,
_$contact, _$contact,
_$vendorContact, _$vendorContact,

View File

@ -2,7 +2,6 @@ import 'package:built_collection/built_collection.dart';
import 'package:built_value/built_value.dart'; import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart'; import 'package:built_value/serializer.dart';
import 'package:invoiceninja_flutter/utils/strings.dart'; import 'package:invoiceninja_flutter/utils/strings.dart';
export 'package:invoiceninja_flutter/data/models/client_model.dart'; export 'package:invoiceninja_flutter/data/models/client_model.dart';
export 'package:invoiceninja_flutter/data/models/company_model.dart'; export 'package:invoiceninja_flutter/data/models/company_model.dart';
export 'package:invoiceninja_flutter/data/models/credit_model.dart'; export 'package:invoiceninja_flutter/data/models/credit_model.dart';
@ -13,7 +12,9 @@ export 'package:invoiceninja_flutter/data/models/expense_model.dart';
export 'package:invoiceninja_flutter/data/models/invoice_model.dart'; export 'package:invoiceninja_flutter/data/models/invoice_model.dart';
export 'package:invoiceninja_flutter/data/models/payment_model.dart'; export 'package:invoiceninja_flutter/data/models/payment_model.dart';
export 'package:invoiceninja_flutter/data/models/product_model.dart'; export 'package:invoiceninja_flutter/data/models/product_model.dart';
export 'package:invoiceninja_flutter/data/models/design_model.dart';
export 'package:invoiceninja_flutter/data/models/project_model.dart'; export 'package:invoiceninja_flutter/data/models/project_model.dart';
//
export 'package:invoiceninja_flutter/data/models/static/country_model.dart'; export 'package:invoiceninja_flutter/data/models/static/country_model.dart';
export 'package:invoiceninja_flutter/data/models/static/currency_model.dart'; export 'package:invoiceninja_flutter/data/models/static/currency_model.dart';
export 'package:invoiceninja_flutter/data/models/static/date_format_model.dart'; export 'package:invoiceninja_flutter/data/models/static/date_format_model.dart';

View File

@ -63,4 +63,3 @@ class QuoteFields {
static const String archivedAt = 'archivedAt'; static const String archivedAt = 'archivedAt';
static const String isDeleted = 'isDeleted'; static const String isDeleted = 'isDeleted';
} }

View File

@ -36,6 +36,9 @@ import 'package:invoiceninja_flutter/redux/payment/payment_state.dart';
import 'package:invoiceninja_flutter/redux/quote/quote_state.dart'; import 'package:invoiceninja_flutter/redux/quote/quote_state.dart';
// STARTER: import - do not remove comment // STARTER: import - do not remove comment
import 'package:invoiceninja_flutter/data/models/design_model.dart';
import 'package:invoiceninja_flutter/redux/design/design_state.dart';
import 'package:invoiceninja_flutter/data/models/credit_model.dart'; import 'package:invoiceninja_flutter/data/models/credit_model.dart';
import 'package:invoiceninja_flutter/redux/credit/credit_state.dart'; import 'package:invoiceninja_flutter/redux/credit/credit_state.dart';
@ -112,6 +115,8 @@ part 'serializers.g.dart';
TaxRateItemResponse, TaxRateItemResponse,
TaxRateListResponse, TaxRateListResponse,
// STARTER: serializers - do not remove comment // STARTER: serializers - do not remove comment
DesignEntity,
InvoiceEntity, InvoiceEntity,
PaymentableEntity, PaymentableEntity,

View File

@ -45,6 +45,9 @@ Serializers _$serializers = (new Serializers().toBuilder()
..add(DatetimeFormatEntity.serializer) ..add(DatetimeFormatEntity.serializer)
..add(DatetimeFormatItemResponse.serializer) ..add(DatetimeFormatItemResponse.serializer)
..add(DatetimeFormatListResponse.serializer) ..add(DatetimeFormatListResponse.serializer)
..add(DesignEntity.serializer)
..add(DesignState.serializer)
..add(DesignUIState.serializer)
..add(DocumentEntity.serializer) ..add(DocumentEntity.serializer)
..add(DocumentItemResponse.serializer) ..add(DocumentItemResponse.serializer)
..add(DocumentListResponse.serializer) ..add(DocumentListResponse.serializer)
@ -383,6 +386,8 @@ Serializers _$serializers = (new Serializers().toBuilder()
..addBuilderFactory(const FullType(BuiltMap, const [const FullType(String), const FullType(PaymentTypeEntity)]), () => new MapBuilder<String, PaymentTypeEntity>()) ..addBuilderFactory(const FullType(BuiltMap, const [const FullType(String), const FullType(PaymentTypeEntity)]), () => new MapBuilder<String, PaymentTypeEntity>())
..addBuilderFactory(const FullType(BuiltMap, const [const FullType(String), const FullType(CountryEntity)]), () => new MapBuilder<String, CountryEntity>()) ..addBuilderFactory(const FullType(BuiltMap, const [const FullType(String), const FullType(CountryEntity)]), () => new MapBuilder<String, CountryEntity>())
..addBuilderFactory(const FullType(BuiltMap, const [const FullType(String), const FullType(InvoiceStatusEntity)]), () => new MapBuilder<String, InvoiceStatusEntity>()) ..addBuilderFactory(const FullType(BuiltMap, const [const FullType(String), const FullType(InvoiceStatusEntity)]), () => new MapBuilder<String, InvoiceStatusEntity>())
..addBuilderFactory(const FullType(BuiltMap, const [const FullType(String), const FullType(DesignEntity)]), () => new MapBuilder<String, DesignEntity>())
..addBuilderFactory(const FullType(BuiltList, const [const FullType(String)]), () => new ListBuilder<String>())
..addBuilderFactory(const FullType(BuiltMap, const [const FullType(String), const FullType(DocumentEntity)]), () => new MapBuilder<String, DocumentEntity>()) ..addBuilderFactory(const FullType(BuiltMap, const [const FullType(String), const FullType(DocumentEntity)]), () => new MapBuilder<String, DocumentEntity>())
..addBuilderFactory(const FullType(BuiltList, const [const FullType(String)]), () => new ListBuilder<String>()) ..addBuilderFactory(const FullType(BuiltList, const [const FullType(String)]), () => new ListBuilder<String>())
..addBuilderFactory(const FullType(BuiltMap, const [const FullType(String), const FullType(ExpenseEntity)]), () => new MapBuilder<String, ExpenseEntity>()) ..addBuilderFactory(const FullType(BuiltMap, const [const FullType(String), const FullType(ExpenseEntity)]), () => new MapBuilder<String, ExpenseEntity>())

View File

@ -21,7 +21,7 @@ class CreditRepository {
'${credentials.url}/credits/$entityId?', credentials.token); '${credentials.url}/credits/$entityId?', credentials.token);
final InvoiceItemResponse creditResponse = final InvoiceItemResponse creditResponse =
serializers.deserializeWith(InvoiceItemResponse.serializer, response); serializers.deserializeWith(InvoiceItemResponse.serializer, response);
return creditResponse.data; return creditResponse.data;
} }
@ -37,7 +37,7 @@ class CreditRepository {
final dynamic response = await webClient.get(url, credentials.token); final dynamic response = await webClient.get(url, credentials.token);
final InvoiceListResponse creditResponse = final InvoiceListResponse creditResponse =
serializers.deserializeWith(InvoiceListResponse.serializer, response); serializers.deserializeWith(InvoiceListResponse.serializer, response);
return creditResponse.data; return creditResponse.data;
} }
@ -52,7 +52,7 @@ class CreditRepository {
data: json.encode({'ids': ids})); data: json.encode({'ids': ids}));
final InvoiceListResponse invoiceResponse = final InvoiceListResponse invoiceResponse =
serializers.deserializeWith(InvoiceListResponse.serializer, response); serializers.deserializeWith(InvoiceListResponse.serializer, response);
return invoiceResponse.data.toList(); return invoiceResponse.data.toList();
} }
@ -72,11 +72,11 @@ class CreditRepository {
url += '?action=' + action.toString(); url += '?action=' + action.toString();
} }
response = response =
await webClient.put(url, credentials.token, data: json.encode(data)); await webClient.put(url, credentials.token, data: json.encode(data));
} }
final InvoiceItemResponse creditResponse = final InvoiceItemResponse creditResponse =
serializers.deserializeWith(InvoiceItemResponse.serializer, response); serializers.deserializeWith(InvoiceItemResponse.serializer, response);
return creditResponse.data; return creditResponse.data;
} }

View File

@ -0,0 +1,84 @@
import 'dart:async';
import 'dart:convert';
import 'dart:core';
import 'package:invoiceninja_flutter/.env.dart';
import 'package:built_collection/built_collection.dart';
import 'package:invoiceninja_flutter/constants.dart';
import 'package:invoiceninja_flutter/data/models/serializers.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/data/web_client.dart';
class DesignRepository {
const DesignRepository({
this.webClient = const WebClient(),
});
final WebClient webClient;
Future<DesignEntity> loadItem(
Credentials credentials, String entityId) async {
final dynamic response = await webClient.get(
'${credentials.url}/designs/$entityId', credentials.token);
final DesignItemResponse designResponse =
serializers.deserializeWith(DesignItemResponse.serializer, response);
return designResponse.data;
}
Future<BuiltList<DesignEntity>> loadList(
Credentials credentials, int updatedAt) async {
String url = credentials.url + '/designs?';
if (updatedAt > 0) {
url += '&updated_at=${updatedAt - kUpdatedAtBufferSeconds}';
}
final dynamic response = await webClient.get(url, credentials.token);
final DesignListResponse designResponse =
serializers.deserializeWith(DesignListResponse.serializer, response);
return designResponse.data;
}
Future<List<DesignEntity>> bulkAction(
Credentials credentials, List<String> ids, EntityAction action) async {
var url = credentials.url + '/designs/bulk?';
if (action != null) {
url += '&action=' + action.toString();
}
final dynamic response = await webClient.post(url, credentials.token,
data: json.encode({'ids': ids}));
final DesignListResponse designResponse =
serializers.deserializeWith(DesignListResponse.serializer, response);
return designResponse.data.toList();
}
Future<DesignEntity> saveData(Credentials credentials, DesignEntity design,
[EntityAction action]) async {
final data = serializers.serializeWith(DesignEntity.serializer, design);
dynamic response;
if (design.isNew) {
response = await webClient.post(
credentials.url + '/designs', credentials.token,
data: json.encode(data));
} else {
var url = credentials.url + '/designs/' + design.id.toString();
if (action != null) {
url += '?action=' + action.toString();
}
response =
await webClient.put(url, credentials.token, data: json.encode(data));
}
final DesignItemResponse designResponse =
serializers.deserializeWith(DesignItemResponse.serializer, response);
return designResponse.data;
}
}

View File

@ -51,6 +51,12 @@ import 'package:sentry/sentry.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
// STARTER: import - do not remove comment // STARTER: import - do not remove comment
import 'package:invoiceninja_flutter/ui/design/design_screen.dart';
import 'package:invoiceninja_flutter/ui/design/edit/design_edit_vm.dart';
import 'package:invoiceninja_flutter/ui/design/view/design_view_vm.dart';
import 'package:invoiceninja_flutter/redux/design/design_actions.dart';
import 'package:invoiceninja_flutter/redux/design/design_middleware.dart';
import 'package:invoiceninja_flutter/ui/credit/credit_screen.dart'; import 'package:invoiceninja_flutter/ui/credit/credit_screen.dart';
import 'package:invoiceninja_flutter/ui/credit/edit/credit_edit_vm.dart'; import 'package:invoiceninja_flutter/ui/credit/edit/credit_edit_vm.dart';
import 'package:invoiceninja_flutter/ui/credit/view/credit_view_vm.dart'; import 'package:invoiceninja_flutter/ui/credit/view/credit_view_vm.dart';
@ -100,6 +106,7 @@ void main({bool isTesting = false}) async {
..addAll(createStoreSettingsMiddleware()) ..addAll(createStoreSettingsMiddleware())
..addAll(createStoreReportsMiddleware()) ..addAll(createStoreReportsMiddleware())
// STARTER: middleware - do not remove comment // STARTER: middleware - do not remove comment
..addAll(createStoreDesignsMiddleware())
..addAll(createStoreCreditsMiddleware()) ..addAll(createStoreCreditsMiddleware())
..addAll(createStoreUsersMiddleware()) ..addAll(createStoreUsersMiddleware())
..addAll(createStoreTaxRatesMiddleware()) ..addAll(createStoreTaxRatesMiddleware())
@ -351,6 +358,9 @@ class InvoiceNinjaAppState extends State<InvoiceNinjaApp> {
QuoteEditScreen.route: (context) => QuoteEditScreen(), QuoteEditScreen.route: (context) => QuoteEditScreen(),
QuoteEmailScreen.route: (context) => QuoteEmailScreen(), QuoteEmailScreen.route: (context) => QuoteEmailScreen(),
// STARTER: routes - do not remove comment // STARTER: routes - do not remove comment
DesignScreen.route: (context) => DesignScreenBuilder(),
DesignViewScreen.route: (context) => DesignViewScreen(),
DesignEditScreen.route: (context) => DesignEditScreen(),
CreditScreen.route: (context) => CreditScreenBuilder(), CreditScreen.route: (context) => CreditScreenBuilder(),
CreditViewScreen.route: (context) => CreditViewScreen(), CreditViewScreen.route: (context) => CreditViewScreen(),
CreditEditScreen.route: (context) => CreditEditScreen(), CreditEditScreen.route: (context) => CreditEditScreen(),

View File

@ -27,6 +27,8 @@ import 'package:invoiceninja_flutter/utils/dialogs.dart';
import 'package:invoiceninja_flutter/utils/localization.dart'; import 'package:invoiceninja_flutter/utils/localization.dart';
// STARTER: import - do not remove comment // STARTER: import - do not remove comment
import 'package:invoiceninja_flutter/redux/design/design_actions.dart';
import 'package:invoiceninja_flutter/redux/credit/credit_actions.dart'; import 'package:invoiceninja_flutter/redux/credit/credit_actions.dart';
class PersistUI {} class PersistUI {}
@ -232,6 +234,13 @@ void filterEntitiesByType({
)); ));
break; break;
// STARTER: filter - do not remove comment // STARTER: filter - do not remove comment
case EntityType.design:
store.dispatch(FilterDesignsByEntity(
entityId: filterEntity.id,
entityType: filterEntity.entityType,
));
break;
case EntityType.credit: case EntityType.credit:
store.dispatch(FilterCreditsByEntity( store.dispatch(FilterCreditsByEntity(
entityId: filterEntity.id, entityId: filterEntity.id,
@ -307,6 +316,10 @@ void viewEntitiesByType({
store.dispatch(ViewGroupList(navigator: navigator)); store.dispatch(ViewGroupList(navigator: navigator));
break; break;
// STARTER: view list - do not remove comment // STARTER: view list - do not remove comment
case EntityType.design:
store.dispatch(ViewDesignList(navigator: navigator));
break;
case EntityType.credit: case EntityType.credit:
store.dispatch(ViewCreditList(navigator: navigator)); store.dispatch(ViewCreditList(navigator: navigator));
break; break;
@ -434,6 +447,14 @@ void viewEntityById({
)); ));
break; break;
// STARTER: view - do not remove comment // STARTER: view - do not remove comment
case EntityType.design:
store.dispatch(ViewDesign(
designId: entityId,
navigator: navigator,
force: force,
));
break;
case EntityType.credit: case EntityType.credit:
store.dispatch(ViewCredit( store.dispatch(ViewCredit(
creditId: entityId, creditId: entityId,
@ -555,6 +576,14 @@ void createEntityByType(
)); ));
break; break;
// STARTER: create type - do not remove comment // STARTER: create type - do not remove comment
case EntityType.design:
store.dispatch(EditDesign(
navigator: navigator,
force: force,
design: DesignEntity(state: state),
));
break;
case EntityType.credit: case EntityType.credit:
store.dispatch(EditCredit( store.dispatch(EditCredit(
navigator: navigator, navigator: navigator,
@ -696,6 +725,15 @@ void createEntity({
)); ));
break; break;
// STARTER: create - do not remove comment // STARTER: create - do not remove comment
case EntityType.design:
store.dispatch(EditDesign(
navigator: navigator,
design: entity,
force: force,
completer: completer,
));
break;
case EntityType.credit: case EntityType.credit:
store.dispatch(EditCredit( store.dispatch(EditCredit(
navigator: navigator, navigator: navigator,
@ -886,6 +924,19 @@ void editEntityById(
)); ));
break; break;
// STARTER: edit - do not remove comment // STARTER: edit - do not remove comment
case EntityType.design:
store.dispatch(EditDesign(
design: map[entityId],
navigator: navigator,
completer: completer ??
snackBarCompleter<DesignEntity>(
context,
entity.isNew
? localization.createdDesign
: localization.updatedDesign),
));
break;
case EntityType.credit: case EntityType.credit:
store.dispatch(EditCredit( store.dispatch(EditCredit(
credit: map[entityId], credit: map[entityId],
@ -968,6 +1019,10 @@ void handleEntitiesActions(
handleDocumentAction(context, entities, action); handleDocumentAction(context, entities, action);
break; break;
// STARTER: actions - do not remove comment // STARTER: actions - do not remove comment
case EntityType.design:
handleDesignAction(context, entities, action);
break;
case EntityType.credit: case EntityType.credit:
handleCreditAction(context, entities, action); handleCreditAction(context, entities, action);
break; break;

View File

@ -18,6 +18,8 @@ import 'package:invoiceninja_flutter/redux/auth/auth_reducer.dart';
import 'package:invoiceninja_flutter/redux/company/company_reducer.dart'; import 'package:invoiceninja_flutter/redux/company/company_reducer.dart';
import 'package:invoiceninja_flutter/redux/static/static_reducer.dart'; import 'package:invoiceninja_flutter/redux/static/static_reducer.dart';
// STARTER: import - do not remove comment // STARTER: import - do not remove comment
import 'package:invoiceninja_flutter/redux/design/design_actions.dart';
import 'package:invoiceninja_flutter/redux/credit/credit_actions.dart'; import 'package:invoiceninja_flutter/redux/credit/credit_actions.dart';
// We create the State reducer by combining many smaller reducers into one! // We create the State reducer by combining many smaller reducers into one!
@ -85,6 +87,10 @@ final lastErrorReducer = combineReducers<String>([
return '${action.error}'; return '${action.error}';
}), }),
// STARTER: errors - do not remove comment // STARTER: errors - do not remove comment
TypedReducer<String, LoadDesignsFailure>((state, action) {
return '${action.error}';
}),
TypedReducer<String, LoadCreditsFailure>((state, action) { TypedReducer<String, LoadCreditsFailure>((state, action) {
return '${action.error}'; return '${action.error}';
}), }),

View File

@ -164,6 +164,9 @@ abstract class AppState implements Built<AppState, AppStateBuilder> {
case EntityType.invoice: case EntityType.invoice:
return invoiceState.map; return invoiceState.map;
// STARTER: states switch map - do not remove comment // STARTER: states switch map - do not remove comment
case EntityType.design:
return designState.map;
case EntityType.credit: case EntityType.credit:
return creditState.map; return creditState.map;
@ -224,6 +227,9 @@ abstract class AppState implements Built<AppState, AppStateBuilder> {
case EntityType.invoice: case EntityType.invoice:
return invoiceState.list; return invoiceState.list;
// STARTER: states switch list - do not remove comment // STARTER: states switch list - do not remove comment
case EntityType.design:
return designState.list;
case EntityType.credit: case EntityType.credit:
return creditState.list; return creditState.list;
@ -263,6 +269,9 @@ abstract class AppState implements Built<AppState, AppStateBuilder> {
case EntityType.invoice: case EntityType.invoice:
return invoiceUIState; return invoiceUIState;
// STARTER: states switch - do not remove comment // STARTER: states switch - do not remove comment
case EntityType.design:
return designUIState;
case EntityType.credit: case EntityType.credit:
return creditUIState; return creditUIState;
@ -316,6 +325,10 @@ abstract class AppState implements Built<AppState, AppStateBuilder> {
ListUIState get invoiceListState => uiState.invoiceUIState.listUIState; ListUIState get invoiceListState => uiState.invoiceUIState.listUIState;
// STARTER: state getters - do not remove comment // STARTER: state getters - do not remove comment
DesignState get designState => userCompanyState.designState;
ListUIState get designListState => uiState.designUIState.listUIState;
DesignUIState get designUIState => uiState.designUIState;
CreditState get creditState => userCompanyState.creditState; CreditState get creditState => userCompanyState.creditState;
ListUIState get creditListState => uiState.creditUIState.listUIState; ListUIState get creditListState => uiState.creditUIState.listUIState;
@ -423,6 +436,8 @@ abstract class AppState implements Built<AppState, AppStateBuilder> {
case CreditEditScreen.route: case CreditEditScreen.route:
return hasCreditChanges(creditUIState.editing, creditState.map); return hasCreditChanges(creditUIState.editing, creditState.map);
// STARTER: has changes - do not remove comment // STARTER: has changes - do not remove comment
case DesignEditScreen.route:
return hasDesignChanges(designUIState.editing, designState.map);
} }
if (uiState.currentRoute.startsWith('/settings')) { if (uiState.currentRoute.startsWith('/settings')) {

View File

@ -18,6 +18,8 @@ import 'package:invoiceninja_flutter/redux/payment/payment_reducer.dart';
import 'package:invoiceninja_flutter/redux/quote/quote_reducer.dart'; import 'package:invoiceninja_flutter/redux/quote/quote_reducer.dart';
// STARTER: import - do not remove comment // STARTER: import - do not remove comment
import 'package:invoiceninja_flutter/redux/design/design_reducer.dart';
import 'package:invoiceninja_flutter/redux/credit/credit_reducer.dart'; import 'package:invoiceninja_flutter/redux/credit/credit_reducer.dart';
import 'package:invoiceninja_flutter/redux/user/user_reducer.dart'; import 'package:invoiceninja_flutter/redux/user/user_reducer.dart';
@ -40,6 +42,7 @@ UserCompanyState companyReducer(UserCompanyState state, dynamic action) {
..vendorState.replace(vendorsReducer(state.vendorState, action)) ..vendorState.replace(vendorsReducer(state.vendorState, action))
..taskState.replace(tasksReducer(state.taskState, action)) ..taskState.replace(tasksReducer(state.taskState, action))
// STARTER: reducer - do not remove comment // STARTER: reducer - do not remove comment
..designState.replace(designsReducer(state.designState, action))
..creditState.replace(creditsReducer(state.creditState, action)) ..creditState.replace(creditsReducer(state.creditState, action))
..userState.replace(usersReducer(state.userState, action)) ..userState.replace(usersReducer(state.userState, action))
..taxRateState.replace(taxRatesReducer(state.taxRateState, action)) ..taxRateState.replace(taxRatesReducer(state.taxRateState, action))

View File

@ -7,6 +7,8 @@ import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:built_value/built_value.dart'; import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart'; import 'package:built_value/serializer.dart';
// STARTER: import - do not remove comment // STARTER: import - do not remove comment
import 'package:invoiceninja_flutter/redux/design/design_state.dart';
import 'package:invoiceninja_flutter/redux/credit/credit_state.dart'; import 'package:invoiceninja_flutter/redux/credit/credit_state.dart';
import 'package:invoiceninja_flutter/redux/user/user_state.dart'; import 'package:invoiceninja_flutter/redux/user/user_state.dart';
@ -39,6 +41,8 @@ abstract class UserCompanyState
paymentState: PaymentState(), paymentState: PaymentState(),
quoteState: QuoteState(), quoteState: QuoteState(),
// STARTER: constructor - do not remove comment // STARTER: constructor - do not remove comment
designState: DesignState(),
creditState: CreditState(), creditState: CreditState(),
userState: UserState(), userState: UserState(),
@ -74,6 +78,8 @@ abstract class UserCompanyState
QuoteState get quoteState; QuoteState get quoteState;
// STARTER: fields - do not remove comment // STARTER: fields - do not remove comment
DesignState get designState;
CreditState get creditState; CreditState get creditState;
UserState get userState; UserState get userState;

View File

@ -52,6 +52,9 @@ class _$UserCompanyStateSerializer
'quoteState', 'quoteState',
serializers.serialize(object.quoteState, serializers.serialize(object.quoteState,
specifiedType: const FullType(QuoteState)), specifiedType: const FullType(QuoteState)),
'designState',
serializers.serialize(object.designState,
specifiedType: const FullType(DesignState)),
'creditState', 'creditState',
serializers.serialize(object.creditState, serializers.serialize(object.creditState,
specifiedType: const FullType(CreditState)), specifiedType: const FullType(CreditState)),
@ -134,6 +137,10 @@ class _$UserCompanyStateSerializer
result.quoteState.replace(serializers.deserialize(value, result.quoteState.replace(serializers.deserialize(value,
specifiedType: const FullType(QuoteState)) as QuoteState); specifiedType: const FullType(QuoteState)) as QuoteState);
break; break;
case 'designState':
result.designState.replace(serializers.deserialize(value,
specifiedType: const FullType(DesignState)) as DesignState);
break;
case 'creditState': case 'creditState':
result.creditState.replace(serializers.deserialize(value, result.creditState.replace(serializers.deserialize(value,
specifiedType: const FullType(CreditState)) as CreditState); specifiedType: const FullType(CreditState)) as CreditState);
@ -321,6 +328,8 @@ class _$UserCompanyState extends UserCompanyState {
@override @override
final QuoteState quoteState; final QuoteState quoteState;
@override @override
final DesignState designState;
@override
final CreditState creditState; final CreditState creditState;
@override @override
final UserState userState; final UserState userState;
@ -347,6 +356,7 @@ class _$UserCompanyState extends UserCompanyState {
this.projectState, this.projectState,
this.paymentState, this.paymentState,
this.quoteState, this.quoteState,
this.designState,
this.creditState, this.creditState,
this.userState, this.userState,
this.taxRateState, this.taxRateState,
@ -383,6 +393,9 @@ class _$UserCompanyState extends UserCompanyState {
if (quoteState == null) { if (quoteState == null) {
throw new BuiltValueNullFieldError('UserCompanyState', 'quoteState'); throw new BuiltValueNullFieldError('UserCompanyState', 'quoteState');
} }
if (designState == null) {
throw new BuiltValueNullFieldError('UserCompanyState', 'designState');
}
if (creditState == null) { if (creditState == null) {
throw new BuiltValueNullFieldError('UserCompanyState', 'creditState'); throw new BuiltValueNullFieldError('UserCompanyState', 'creditState');
} }
@ -424,6 +437,7 @@ class _$UserCompanyState extends UserCompanyState {
projectState == other.projectState && projectState == other.projectState &&
paymentState == other.paymentState && paymentState == other.paymentState &&
quoteState == other.quoteState && quoteState == other.quoteState &&
designState == other.designState &&
creditState == other.creditState && creditState == other.creditState &&
userState == other.userState && userState == other.userState &&
taxRateState == other.taxRateState && taxRateState == other.taxRateState &&
@ -449,21 +463,24 @@ class _$UserCompanyState extends UserCompanyState {
$jc( $jc(
$jc( $jc(
$jc( $jc(
0, $jc(
userCompany 0,
userCompany
.hashCode),
documentState
.hashCode), .hashCode),
documentState productState
.hashCode), .hashCode),
productState clientState
.hashCode), .hashCode),
clientState.hashCode), invoiceState.hashCode),
invoiceState.hashCode), expenseState.hashCode),
expenseState.hashCode), vendorState.hashCode),
vendorState.hashCode), taskState.hashCode),
taskState.hashCode), projectState.hashCode),
projectState.hashCode), paymentState.hashCode),
paymentState.hashCode), quoteState.hashCode),
quoteState.hashCode), designState.hashCode),
creditState.hashCode), creditState.hashCode),
userState.hashCode), userState.hashCode),
taxRateState.hashCode), taxRateState.hashCode),
@ -485,6 +502,7 @@ class _$UserCompanyState extends UserCompanyState {
..add('projectState', projectState) ..add('projectState', projectState)
..add('paymentState', paymentState) ..add('paymentState', paymentState)
..add('quoteState', quoteState) ..add('quoteState', quoteState)
..add('designState', designState)
..add('creditState', creditState) ..add('creditState', creditState)
..add('userState', userState) ..add('userState', userState)
..add('taxRateState', taxRateState) ..add('taxRateState', taxRateState)
@ -563,6 +581,12 @@ class UserCompanyStateBuilder
set quoteState(QuoteStateBuilder quoteState) => set quoteState(QuoteStateBuilder quoteState) =>
_$this._quoteState = quoteState; _$this._quoteState = quoteState;
DesignStateBuilder _designState;
DesignStateBuilder get designState =>
_$this._designState ??= new DesignStateBuilder();
set designState(DesignStateBuilder designState) =>
_$this._designState = designState;
CreditStateBuilder _creditState; CreditStateBuilder _creditState;
CreditStateBuilder get creditState => CreditStateBuilder get creditState =>
_$this._creditState ??= new CreditStateBuilder(); _$this._creditState ??= new CreditStateBuilder();
@ -607,6 +631,7 @@ class UserCompanyStateBuilder
_projectState = _$v.projectState?.toBuilder(); _projectState = _$v.projectState?.toBuilder();
_paymentState = _$v.paymentState?.toBuilder(); _paymentState = _$v.paymentState?.toBuilder();
_quoteState = _$v.quoteState?.toBuilder(); _quoteState = _$v.quoteState?.toBuilder();
_designState = _$v.designState?.toBuilder();
_creditState = _$v.creditState?.toBuilder(); _creditState = _$v.creditState?.toBuilder();
_userState = _$v.userState?.toBuilder(); _userState = _$v.userState?.toBuilder();
_taxRateState = _$v.taxRateState?.toBuilder(); _taxRateState = _$v.taxRateState?.toBuilder();
@ -647,6 +672,7 @@ class UserCompanyStateBuilder
projectState: projectState.build(), projectState: projectState.build(),
paymentState: paymentState.build(), paymentState: paymentState.build(),
quoteState: quoteState.build(), quoteState: quoteState.build(),
designState: designState.build(),
creditState: creditState.build(), creditState: creditState.build(),
userState: userState.build(), userState: userState.build(),
taxRateState: taxRateState.build(), taxRateState: taxRateState.build(),
@ -677,6 +703,8 @@ class UserCompanyStateBuilder
paymentState.build(); paymentState.build();
_$failedField = 'quoteState'; _$failedField = 'quoteState';
quoteState.build(); quoteState.build();
_$failedField = 'designState';
designState.build();
_$failedField = 'creditState'; _$failedField = 'creditState';
creditState.build(); creditState.build();
_$failedField = 'userState'; _$failedField = 'userState';

View File

@ -35,10 +35,10 @@ class EditCredit extends AbstractNavigatorAction
implements PersistUI, PersistPrefs { implements PersistUI, PersistPrefs {
EditCredit( EditCredit(
{this.credit, {this.credit,
@required NavigatorState navigator, @required NavigatorState navigator,
this.creditItemIndex, this.creditItemIndex,
this.completer, this.completer,
this.force = false}) this.force = false})
: super(navigator: navigator); : super(navigator: navigator);
final InvoiceEntity credit; final InvoiceEntity credit;
@ -360,14 +360,14 @@ class FilterCreditsByCustom4 implements PersistUI {
Future handleCreditAction( Future handleCreditAction(
BuildContext context, List<BaseEntity> credits, EntityAction action) async { BuildContext context, List<BaseEntity> credits, EntityAction action) async {
assert( assert(
[ [
EntityAction.restore, EntityAction.restore,
EntityAction.archive, EntityAction.archive,
EntityAction.delete, EntityAction.delete,
EntityAction.toggleMultiselect EntityAction.toggleMultiselect
].contains(action) || ].contains(action) ||
credits.length == 1, credits.length == 1,
'Cannot perform this action on more than one credit'); 'Cannot perform this action on more than one credit');
final store = StoreProvider.of<AppState>(context); final store = StoreProvider.of<AppState>(context);
final localization = AppLocalization.of(context); final localization = AppLocalization.of(context);
@ -402,7 +402,7 @@ Future handleCreditAction(
case EntityAction.sendEmail: case EntityAction.sendEmail:
store.dispatch(ShowEmailCredit( store.dispatch(ShowEmailCredit(
completer: completer:
snackBarCompleter<Null>(context, localization.emailedCredit), snackBarCompleter<Null>(context, localization.emailedCredit),
credit: credit, credit: credit,
context: context)); context: context));
break; break;

View File

@ -120,7 +120,7 @@ Middleware<AppState> _showEmailCredit() {
if (isMobile(action.context)) { if (isMobile(action.context)) {
final emailWasSent = final emailWasSent =
await Navigator.of(action.context).pushNamed(CreditEmailScreen.route); await Navigator.of(action.context).pushNamed(CreditEmailScreen.route);
if (action.completer != null && emailWasSent != null && emailWasSent) { if (action.completer != null && emailWasSent != null && emailWasSent) {
action.completer.complete(null); action.completer.complete(null);
@ -133,10 +133,10 @@ Middleware<AppState> _archiveCredit(CreditRepository repository) {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) { return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as ArchiveCreditsRequest; final action = dynamicAction as ArchiveCreditsRequest;
final prevCredits = final prevCredits =
action.creditIds.map((id) => store.state.creditState.map[id]).toList(); action.creditIds.map((id) => store.state.creditState.map[id]).toList();
repository repository
.bulkAction( .bulkAction(
store.state.credentials, action.creditIds, EntityAction.archive) store.state.credentials, action.creditIds, EntityAction.archive)
.then((List<InvoiceEntity> credits) { .then((List<InvoiceEntity> credits) {
store.dispatch(ArchiveCreditsSuccess(credits)); store.dispatch(ArchiveCreditsSuccess(credits));
if (action.completer != null) { if (action.completer != null) {
@ -158,11 +158,11 @@ Middleware<AppState> _deleteCredit(CreditRepository repository) {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) { return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as DeleteCreditsRequest; final action = dynamicAction as DeleteCreditsRequest;
final prevCredits = final prevCredits =
action.creditIds.map((id) => store.state.creditState.map[id]).toList(); action.creditIds.map((id) => store.state.creditState.map[id]).toList();
repository repository
.bulkAction( .bulkAction(
store.state.credentials, action.creditIds, EntityAction.delete) store.state.credentials, action.creditIds, EntityAction.delete)
.then((List<InvoiceEntity> credits) { .then((List<InvoiceEntity> credits) {
store.dispatch(DeleteCreditsSuccess(credits)); store.dispatch(DeleteCreditsSuccess(credits));
if (action.completer != null) { if (action.completer != null) {
@ -184,11 +184,11 @@ Middleware<AppState> _restoreCredit(CreditRepository repository) {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) { return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as RestoreCreditsRequest; final action = dynamicAction as RestoreCreditsRequest;
final prevCredits = final prevCredits =
action.creditIds.map((id) => store.state.creditState.map[id]).toList(); action.creditIds.map((id) => store.state.creditState.map[id]).toList();
repository repository
.bulkAction( .bulkAction(
store.state.credentials, action.creditIds, EntityAction.restore) store.state.credentials, action.creditIds, EntityAction.restore)
.then((List<InvoiceEntity> credits) { .then((List<InvoiceEntity> credits) {
store.dispatch(RestoreCreditsSuccess(credits)); store.dispatch(RestoreCreditsSuccess(credits));
if (action.completer != null) { if (action.completer != null) {
@ -235,7 +235,7 @@ Middleware<AppState> _emailCredit(CreditRepository repository) {
final origCredit = store.state.creditState.map[action.creditId]; final origCredit = store.state.creditState.map[action.creditId];
repository repository
.emailCredit(store.state.credentials, origCredit, action.template, .emailCredit(store.state.credentials, origCredit, action.template,
action.subject, action.body) action.subject, action.body)
.then((void _) { .then((void _) {
store.dispatch(EmailCreditSuccess()); store.dispatch(EmailCreditSuccess());
if (action.completer != null) { if (action.completer != null) {
@ -292,7 +292,9 @@ Middleware<AppState> _loadCredit(CreditRepository repository) {
} }
store.dispatch(LoadCreditRequest()); store.dispatch(LoadCreditRequest());
repository.loadItem(store.state.credentials, action.creditId).then((credit) { repository
.loadItem(store.state.credentials, action.creditId)
.then((credit) {
store.dispatch(LoadCreditSuccess(credit)); store.dispatch(LoadCreditSuccess(credit));
if (action.completer != null) { if (action.completer != null) {

View File

@ -33,8 +33,9 @@ String filtercreditDropdownReducer(
Reducer<String> selectedIdReducer = combineReducers([ Reducer<String> selectedIdReducer = combineReducers([
TypedReducer<String, ViewCredit>((selectedId, action) => action.creditId), TypedReducer<String, ViewCredit>((selectedId, action) => action.creditId),
TypedReducer<String, AddCreditSuccess>( TypedReducer<String, AddCreditSuccess>(
(selectedId, action) => action.credit.id), (selectedId, action) => action.credit.id),
TypedReducer<String, ShowEmailCredit>((selectedId, action) => action.credit.id), TypedReducer<String, ShowEmailCredit>(
(selectedId, action) => action.credit.id),
TypedReducer<String, SelectCompany>((selectedId, action) => ''), TypedReducer<String, SelectCompany>((selectedId, action) => ''),
]); ]);
@ -97,7 +98,7 @@ InvoiceEntity _updateEditing(InvoiceEntity credit, dynamic action) {
InvoiceEntity _addCreditItem(InvoiceEntity credit, AddCreditItem action) { InvoiceEntity _addCreditItem(InvoiceEntity credit, AddCreditItem action) {
return credit.rebuild( return credit.rebuild(
(b) => b..lineItems.add(action.creditItem ?? InvoiceItemEntity())); (b) => b..lineItems.add(action.creditItem ?? InvoiceItemEntity()));
} }
InvoiceEntity _addCreditItems(InvoiceEntity credit, AddCreditItems action) { InvoiceEntity _addCreditItems(InvoiceEntity credit, AddCreditItems action) {
@ -227,7 +228,8 @@ ListUIState _addToListMultiselect(
ListUIState _removeFromListMultiselect( ListUIState _removeFromListMultiselect(
ListUIState creditListState, RemoveFromCreditMultiselect action) { ListUIState creditListState, RemoveFromCreditMultiselect action) {
return creditListState.rebuild((b) => b..selectedIds.remove(action.entity.id)); return creditListState
.rebuild((b) => b..selectedIds.remove(action.entity.id));
} }
ListUIState _clearListMultiselect( ListUIState _clearListMultiselect(
@ -368,5 +370,6 @@ CreditState _updateCredit(CreditState creditState, dynamic action) {
return creditState.rebuild((b) => b..map[action.credit.id] = action.credit); return creditState.rebuild((b) => b..map[action.credit.id] = action.credit);
} }
CreditState _setLoadedCredits(CreditState creditState, LoadCreditsSuccess action) => CreditState _setLoadedCredits(
CreditState creditState, LoadCreditsSuccess action) =>
creditState.loadCredits(action.credits); creditState.loadCredits(action.credits);

View File

@ -8,10 +8,11 @@ ClientEntity creditClientSelector(
return clientMap[credit.clientId]; return clientMap[credit.clientId];
} }
var memoizedFilteredCreditList = memo4((BuiltMap<String, InvoiceEntity> creditMap, var memoizedFilteredCreditList = memo4((BuiltMap<String, InvoiceEntity>
BuiltList<String> creditList, creditMap,
BuiltMap<String, ClientEntity> clientMap, BuiltList<String> creditList,
ListUIState creditListState) => BuiltMap<String, ClientEntity> clientMap,
ListUIState creditListState) =>
filteredCreditsSelector(creditMap, creditList, clientMap, creditListState)); filteredCreditsSelector(creditMap, creditList, clientMap, creditListState));
List<String> filteredCreditsSelector( List<String> filteredCreditsSelector(
@ -64,7 +65,7 @@ List<String> filteredCreditsSelector(
} }
var memoizedCreditStatsForClient = memo2( var memoizedCreditStatsForClient = memo2(
(String clientId, BuiltMap<String, InvoiceEntity> creditMap) => (String clientId, BuiltMap<String, InvoiceEntity> creditMap) =>
creditStatsForClient(clientId, creditMap)); creditStatsForClient(clientId, creditMap));
EntityStats creditStatsForClient( EntityStats creditStatsForClient(
@ -85,13 +86,13 @@ EntityStats creditStatsForClient(
} }
var memoizedCreditStatsForUser = memo2( var memoizedCreditStatsForUser = memo2(
(String userId, BuiltMap<String, InvoiceEntity> creditMap) => (String userId, BuiltMap<String, InvoiceEntity> creditMap) =>
creditStatsForUser(userId, creditMap)); creditStatsForUser(userId, creditMap));
EntityStats creditStatsForUser( EntityStats creditStatsForUser(
String userId, String userId,
BuiltMap<String, InvoiceEntity> creditMap, BuiltMap<String, InvoiceEntity> creditMap,
) { ) {
int countActive = 0; int countActive = 0;
int countArchived = 0; int countArchived = 0;
creditMap.forEach((creditId, credit) { creditMap.forEach((creditId, credit) {
@ -108,5 +109,5 @@ EntityStats creditStatsForUser(
} }
bool hasCreditChanges( bool hasCreditChanges(
InvoiceEntity credit, BuiltMap<String, InvoiceEntity> creditMap) => InvoiceEntity credit, BuiltMap<String, InvoiceEntity> creditMap) =>
credit.isNew ? credit.isChanged : credit != creditMap[credit.id]; credit.isNew ? credit.isChanged : credit != creditMap[credit.id];

View File

@ -0,0 +1,335 @@
import 'dart:async';
import 'package:built_collection/built_collection.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:invoiceninja_flutter/constants.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/redux/app/app_actions.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
import 'package:invoiceninja_flutter/redux/settings/settings_actions.dart';
import 'package:invoiceninja_flutter/utils/completers.dart';
import 'package:invoiceninja_flutter/utils/localization.dart';
import 'package:invoiceninja_flutter/utils/platforms.dart';
class ViewDesignList extends AbstractNavigatorAction implements PersistUI {
ViewDesignList({
@required NavigatorState navigator,
this.force = false,
}) : super(navigator: navigator);
final bool force;
}
class ViewDesign extends AbstractNavigatorAction
implements PersistUI, PersistPrefs {
ViewDesign({
@required NavigatorState navigator,
@required this.designId,
this.force = false,
}) : super(navigator: navigator);
final String designId;
final bool force;
}
class EditDesign extends AbstractNavigatorAction
implements PersistUI, PersistPrefs {
EditDesign(
{@required this.design,
@required NavigatorState navigator,
this.completer,
this.cancelCompleter,
this.force = false})
: super(navigator: navigator);
final DesignEntity design;
final Completer completer;
final Completer cancelCompleter;
final bool force;
}
class UpdateDesign implements PersistUI {
UpdateDesign(this.design);
final DesignEntity design;
}
class LoadDesign {
LoadDesign({this.completer, this.designId});
final Completer completer;
final String designId;
}
class LoadDesignActivity {
LoadDesignActivity({this.completer, this.designId});
final Completer completer;
final String designId;
}
class LoadDesigns {
LoadDesigns({this.completer, this.force = false});
final Completer completer;
final bool force;
}
class LoadDesignRequest implements StartLoading {}
class LoadDesignFailure implements StopLoading {
LoadDesignFailure(this.error);
final dynamic error;
@override
String toString() {
return 'LoadDesignFailure{error: $error}';
}
}
class LoadDesignSuccess implements StopLoading, PersistData {
LoadDesignSuccess(this.design);
final DesignEntity design;
@override
String toString() {
return 'LoadDesignSuccess{design: $design}';
}
}
class LoadDesignsRequest implements StartLoading {}
class LoadDesignsFailure implements StopLoading {
LoadDesignsFailure(this.error);
final dynamic error;
@override
String toString() {
return 'LoadDesignsFailure{error: $error}';
}
}
class LoadDesignsSuccess implements StopLoading, PersistData {
LoadDesignsSuccess(this.designs);
final BuiltList<DesignEntity> designs;
@override
String toString() {
return 'LoadDesignsSuccess{designs: $designs}';
}
}
class SaveDesignRequest implements StartSaving {
SaveDesignRequest({this.completer, this.design});
final Completer completer;
final DesignEntity design;
}
class SaveDesignSuccess implements StopSaving, PersistData, PersistUI {
SaveDesignSuccess(this.design);
final DesignEntity design;
}
class AddDesignSuccess implements StopSaving, PersistData, PersistUI {
AddDesignSuccess(this.design);
final DesignEntity design;
}
class SaveDesignFailure implements StopSaving {
SaveDesignFailure(this.error);
final Object error;
}
class ArchiveDesignsRequest implements StartSaving {
ArchiveDesignsRequest(this.completer, this.designIds);
final Completer completer;
final List<String> designIds;
}
class ArchiveDesignsSuccess implements StopSaving, PersistData {
ArchiveDesignsSuccess(this.designs);
final List<DesignEntity> designs;
}
class ArchiveDesignsFailure implements StopSaving {
ArchiveDesignsFailure(this.designs);
final List<DesignEntity> designs;
}
class DeleteDesignsRequest implements StartSaving {
DeleteDesignsRequest(this.completer, this.designIds);
final Completer completer;
final List<String> designIds;
}
class DeleteDesignsSuccess implements StopSaving, PersistData {
DeleteDesignsSuccess(this.designs);
final List<DesignEntity> designs;
}
class DeleteDesignsFailure implements StopSaving {
DeleteDesignsFailure(this.designs);
final List<DesignEntity> designs;
}
class RestoreDesignsRequest implements StartSaving {
RestoreDesignsRequest(this.completer, this.designIds);
final Completer completer;
final List<String> designIds;
}
class RestoreDesignsSuccess implements StopSaving, PersistData {
RestoreDesignsSuccess(this.design);
final List<DesignEntity> designs;
}
class RestoreDesignsFailure implements StopSaving {
RestoreDesignsFailure(this.design);
final List<DesignEntity> designs;
}
class FilterDesigns implements PersistUI {
FilterDesigns(this.filter);
final String filter;
}
class SortDesigns implements PersistUI {
SortDesigns(this.field);
final String field;
}
class FilterDesignsByState implements PersistUI {
FilterDesignsByState(this.state);
final EntityState state;
}
class FilterDesignsByCustom1 implements PersistUI {
FilterDesignsByCustom1(this.value);
final String value;
}
class FilterDesignsByCustom2 implements PersistUI {
FilterDesignsByCustom2(this.value);
final String value;
}
class FilterDesignsByCustom3 implements PersistUI {
FilterDesignsByCustom3(this.value);
final String value;
}
class FilterDesignsByCustom4 implements PersistUI {
FilterDesignsByCustom4(this.value);
final String value;
}
class FilterDesignsByEntity implements PersistUI {
FilterDesignsByEntity({this.entityId, this.entityType});
final String entityId;
final EntityType entityType;
}
void handleDesignAction(
BuildContext context, List<BaseEntity> designs, EntityAction action) {
if (designs.isEmpty) {
return;
}
final store = StoreProvider.of<AppState>(context);
final state = store.state;
final CompanyEntity company = state.company;
final localization = AppLocalization.of(context);
final design = designs.first as DesignEntity;
final designIds = designs.map((design) => design.id).toList();
switch (action) {
case EntityAction.edit:
editEntity(context: context, entity: design);
break;
case EntityAction.restore:
store.dispatch(RestoreDesignsRequest(
snackBarCompleter<Null>(context, localization.restoredDesign),
designIds));
break;
case EntityAction.archive:
store.dispatch(ArchiveDesignsRequest(
snackBarCompleter<Null>(context, localization.archivedDesign),
designIds));
break;
case EntityAction.delete:
store.dispatch(DeleteDesignsRequest(
snackBarCompleter<Null>(context, localization.deletedDesign),
designIds));
break;
case EntityAction.toggleMultiselect:
if (!store.state.designListState.isInMultiselect()) {
store.dispatch(StartDesignMultiselect(context: context));
}
if (designs.isEmpty) {
break;
}
for (final design in designs) {
if (!store.state.designListState.isSelected(design.id)) {
store.dispatch(
AddToDesignMultiselect(context: context, entity: design));
} else {
store.dispatch(
RemoveFromDesignMultiselect(context: context, entity: design));
}
}
break;
}
}
class StartDesignMultiselect {
StartDesignMultiselect({@required this.context});
final BuildContext context;
}
class AddToDesignMultiselect {
AddToDesignMultiselect({@required this.context, @required this.entity});
final BuildContext context;
final BaseEntity entity;
}
class RemoveFromDesignMultiselect {
RemoveFromDesignMultiselect({@required this.context, @required this.entity});
final BuildContext context;
final BaseEntity entity;
}
class ClearDesignMultiselect {
ClearDesignMultiselect({@required this.context});
final BuildContext context;
}

View File

@ -0,0 +1,275 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:redux/redux.dart';
import 'package:invoiceninja_flutter/utils/platforms.dart';
import 'package:invoiceninja_flutter/redux/app/app_middleware.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart';
import 'package:invoiceninja_flutter/ui/design/design_screen.dart';
import 'package:invoiceninja_flutter/ui/design/edit/design_edit_vm.dart';
import 'package:invoiceninja_flutter/ui/design/view/design_view_vm.dart';
import 'package:invoiceninja_flutter/redux/design/design_actions.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
import 'package:invoiceninja_flutter/data/repositories/design_repository.dart';
List<Middleware<AppState>> createStoreDesignsMiddleware([
DesignRepository repository = const DesignRepository(),
]) {
final viewDesignList = _viewDesignList();
final viewDesign = _viewDesign();
final editDesign = _editDesign();
final loadDesigns = _loadDesigns(repository);
final loadDesign = _loadDesign(repository);
final saveDesign = _saveDesign(repository);
final archiveDesign = _archiveDesign(repository);
final deleteDesign = _deleteDesign(repository);
final restoreDesign = _restoreDesign(repository);
return [
TypedMiddleware<AppState, ViewDesignList>(viewDesignList),
TypedMiddleware<AppState, ViewDesign>(viewDesign),
TypedMiddleware<AppState, EditDesign>(editDesign),
TypedMiddleware<AppState, LoadDesigns>(loadDesigns),
TypedMiddleware<AppState, LoadDesign>(loadDesign),
TypedMiddleware<AppState, SaveDesignRequest>(saveDesign),
TypedMiddleware<AppState, ArchiveDesignRequest>(archiveDesign),
TypedMiddleware<AppState, DeleteDesignRequest>(deleteDesign),
TypedMiddleware<AppState, RestoreDesignRequest>(restoreDesign),
];
}
Middleware<AppState> _editDesign() {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as EditDesign;
if (!action.force &&
hasChanges(store: store, context: action.context, action: action)) {
return;
}
next(action);
store.dispatch(UpdateCurrentRoute(DesignEditScreen.route));
if (isMobile(action.context)) {
action.navigator.pushNamed(DesignEditScreen.route);
}
};
}
Middleware<AppState> _viewDesign() {
return (Store<AppState> store, dynamic dynamicAction,
NextDispatcher next) async {
final action = dynamicAction as ViewDesign;
if (!action.force &&
hasChanges(store: store, context: action.context, action: action)) {
return;
}
next(action);
store.dispatch(UpdateCurrentRoute(DesignViewScreen.route));
if (isMobile(action.context)) {
Navigator.of(action.context).pushNamed(DesignViewScreen.route);
}
};
}
Middleware<AppState> _viewDesignList() {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as ViewDesignList;
if (!action.force &&
hasChanges(store: store, context: action.context, action: action)) {
return;
}
next(action);
if (store.state.designState.isStale) {
store.dispatch(LoadDesigns());
}
store.dispatch(UpdateCurrentRoute(DesignScreen.route));
if (isMobile(action.context)) {
Navigator.of(action.context).pushNamedAndRemoveUntil(
DesignScreen.route, (Route<dynamic> route) => false);
}
};
}
Middleware<AppState> _archiveDesign(DesignRepository repository) {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as ArchiveDesignsRequest;
final prevDesigns =
action.designIds.map((id) => store.state.designState.map[id]).toList();
repository
.bulkAction(
store.state.credentials, action.designIds, EntityAction.archive)
.then((List<DesignEntity> designs) {
store.dispatch(ArchiveDesignsSuccess(designs));
if (action.completer != null) {
action.completer.complete(null);
}
}).catchError((Object error) {
print(error);
store.dispatch(ArchiveDesignsFailure(prevDesigns));
if (action.completer != null) {
action.completer.completeError(error);
}
});
next(action);
};
}
Middleware<AppState> _deleteDesign(DesignRepository repository) {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as DeleteDesignsRequest;
final prevDesigns =
action.designIds.map((id) => store.state.designState.map[id]).toList();
repository
.bulkAction(
store.state.credentials, action.designIds, EntityAction.delete)
.then((List<DesignEntity> designs) {
store.dispatch(DeleteDesignsSuccess(designs));
if (action.completer != null) {
action.completer.complete(null);
}
}).catchError((Object error) {
print(error);
store.dispatch(DeleteDesignsFailure(prevDesigns));
if (action.completer != null) {
action.completer.completeError(error);
}
});
next(action);
};
}
Middleware<AppState> _restoreDesign(DesignRepository repository) {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as RestoreDesignsRequest;
final prevDesigns =
action.designIds.map((id) => store.state.designState.map[id]).toList();
repository
.bulkAction(
store.state.credentials, action.designIds, EntityAction.restore)
.then((List<DesignEntity> designs) {
store.dispatch(RestoreDesignSuccess(designs));
if (action.completer != null) {
action.completer.complete(null);
}
}).catchError((Object error) {
print(error);
store.dispatch(RestoreDesignFailure(prevDesigns));
if (action.completer != null) {
action.completer.completeError(error);
}
});
next(action);
};
}
Middleware<AppState> _saveDesign(DesignRepository repository) {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as SaveDesignRequest;
repository
.saveData(store.state.credentials, action.design)
.then((DesignEntity design) {
if (action.design.isNew) {
store.dispatch(AddDesignSuccess(design));
} else {
store.dispatch(SaveDesignSuccess(design));
}
action.completer.complete(design);
final designUIState = store.state.designUIState;
if (designUIState.saveCompleter != null) {
designUIState.saveCompleter.complete(design);
}
}).catchError((Object error) {
print(error);
store.dispatch(SaveDesignFailure(error));
action.completer.completeError(error);
});
next(action);
};
}
Middleware<AppState> _loadDesign(DesignRepository repository) {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as LoadDesign;
final AppState state = store.state;
if (state.isLoading) {
next(action);
return;
}
store.dispatch(LoadDesignRequest());
repository.loadItem(state.credentials, action.designId).then((design) {
store.dispatch(LoadDesignSuccess(design));
if (action.completer != null) {
action.completer.complete(null);
}
}).catchError((Object error) {
print(error);
store.dispatch(LoadDesignFailure(error));
if (action.completer != null) {
action.completer.completeError(error);
}
});
next(action);
};
}
Middleware<AppState> _loadDesigns(DesignRepository repository) {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as LoadDesigns;
final AppState state = store.state;
if (!state.designState.isStale && !action.force) {
next(action);
return;
}
if (state.isLoading) {
next(action);
return;
}
final int updatedAt = (state.designState.lastUpdated / 1000).round();
store.dispatch(LoadDesignsRequest());
repository.loadList(state.credentials, updatedAt).then((data) {
store.dispatch(LoadDesignsSuccess(data));
if (action.completer != null) {
action.completer.complete(null);
}
/*
if (state.productState.isStale) {
store.dispatch(LoadProducts());
}
*/
}).catchError((Object error) {
print(error);
store.dispatch(LoadDesignsFailure(error));
if (action.completer != null) {
action.completer.completeError(error);
}
});
next(action);
};
}

View File

@ -0,0 +1,275 @@
import 'package:redux/redux.dart';
import 'package:built_collection/built_collection.dart';
import 'package:invoiceninja_flutter/redux/app/app_actions.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/redux/company/company_actions.dart';
import 'package:invoiceninja_flutter/redux/ui/entity_ui_state.dart';
import 'package:invoiceninja_flutter/redux/design/design_actions.dart';
import 'package:invoiceninja_flutter/redux/ui/list_ui_state.dart';
import 'package:invoiceninja_flutter/redux/design/design_actions.dart';
import 'package:invoiceninja_flutter/redux/design/design_state.dart';
import 'package:invoiceninja_flutter/data/models/entities.dart';
EntityUIState designUIReducer(DesignUIState state, dynamic action) {
return state.rebuild((b) => b
..listUIState.replace(designListReducer(state.listUIState, action))
..editing.replace(editingReducer(state.editing, action))
..selectedId = selectedIdReducer(state.selectedId, action));
}
Reducer<String> selectedIdReducer = combineReducers([
TypedReducer<String, ViewDesign>(
(String selectedId, dynamic action) => action.designId),
TypedReducer<String, AddDesignSuccess>(
(String selectedId, dynamic action) => action.design.id),
TypedReducer<String, SelectCompany>((selectedId, action) => ''),
]);
final editingReducer = combineReducers<DesignEntity>([
TypedReducer<DesignEntity, SaveDesignSuccess>(_updateEditing),
TypedReducer<DesignEntity, AddDesignSuccess>(_updateEditing),
TypedReducer<DesignEntity, RestoreDesignSuccess>((designs, action) {
return action.designs[0];
}),
TypedReducer<DesignEntity, ArchiveDesignSuccess>((designs, action) {
return action.designs[0];
}),
TypedReducer<DesignEntity, DeleteDesignSuccess>((designs, action) {
return action.designs[0];
}),
TypedReducer<DesignEntity, EditDesign>(_updateEditing),
TypedReducer<DesignEntity, UpdateDesign>((design, action) {
return action.design.rebuild((b) => b..isChanged = true);
}),
TypedReducer<DesignEntity, SelectCompany>(_clearEditing),
TypedReducer<DesignEntity, DiscardChanges>(_clearEditing),
]);
DesignEntity _clearEditing(DesignEntity design, dynamic action) {
return DesignEntity();
}
DesignEntity _updateEditing(DesignEntity design, dynamic action) {
return action.design;
}
final designListReducer = combineReducers<ListUIState>([
TypedReducer<ListUIState, SortDesigns>(_sortDesigns),
TypedReducer<ListUIState, FilterDesignsByState>(_filterDesignsByState),
TypedReducer<ListUIState, FilterDesigns>(_filterDesigns),
TypedReducer<ListUIState, FilterDesignsByCustom1>(_filterDesignsByCustom1),
TypedReducer<ListUIState, FilterDesignsByCustom2>(_filterDesignsByCustom2),
TypedReducer<ListUIState, FilterDesignsByEntity>(_filterDesignsByClient),
TypedReducer<ListUIState, StartDesignMultiselect>(_startListMultiselect),
TypedReducer<ListUIState, AddToDesignMultiselect>(_addToListMultiselect),
TypedReducer<ListUIState, RemoveFromDesignMultiselect>(
_removeFromListMultiselect),
TypedReducer<ListUIState, ClearDesignMultiselect>(_clearListMultiselect),
]);
ListUIState _filterDesignsByClient(
ListUIState designListState, FilterDesignsByEntity action) {
return designListState.rebuild((b) => b
..filterEntityId = action.entityId
..filterEntityType = action.entityType);
}
ListUIState _filterDesignsByCustom1(
ListUIState designListState, FilterDesignsByCustom1 action) {
if (designListState.custom1Filters.contains(action.value)) {
return designListState
.rebuild((b) => b..custom1Filters.remove(action.value));
} else {
return designListState.rebuild((b) => b..custom1Filters.add(action.value));
}
}
ListUIState _filterDesignsByCustom2(
ListUIState designListState, FilterDesignsByCustom2 action) {
if (designListState.custom2Filters.contains(action.value)) {
return designListState
.rebuild((b) => b..custom2Filters.remove(action.value));
} else {
return designListState.rebuild((b) => b..custom2Filters.add(action.value));
}
}
ListUIState _filterDesignsByState(
ListUIState designListState, FilterDesignsByState action) {
if (designListState.stateFilters.contains(action.state)) {
return designListState.rebuild((b) => b..stateFilters.remove(action.state));
} else {
return designListState.rebuild((b) => b..stateFilters.add(action.state));
}
}
ListUIState _filterDesigns(ListUIState designListState, FilterDesigns action) {
return designListState.rebuild((b) => b
..filter = action.filter
..filterClearedAt = action.filter == null
? DateTime.now().millisecondsSinceEpoch
: designListState.filterClearedAt);
}
ListUIState _sortDesigns(ListUIState designListState, SortDesigns action) {
return designListState.rebuild((b) => b
..sortAscending = b.sortField != action.field || !b.sortAscending
..sortField = action.field);
}
ListUIState _startListMultiselect(
ListUIState productListState, StartDesignMultiselect action) {
return productListState.rebuild((b) => b..selectedIds = ListBuilder());
}
ListUIState _addToListMultiselect(
ListUIState productListState, AddToDesignMultiselect action) {
return productListState.rebuild((b) => b..selectedIds.add(action.entity.id));
}
ListUIState _removeFromListMultiselect(
ListUIState productListState, RemoveFromDesignMultiselect action) {
return productListState
.rebuild((b) => b..selectedIds.remove(action.entity.id));
}
ListUIState _clearListMultiselect(
ListUIState productListState, ClearDesignMultiselect action) {
return productListState.rebuild((b) => b..selectedIds = null);
}
final designsReducer = combineReducers<DesignState>([
TypedReducer<DesignState, SaveDesignSuccess>(_updateDesign),
TypedReducer<DesignState, AddDesignSuccess>(_addDesign),
TypedReducer<DesignState, LoadDesignsSuccess>(_setLoadedDesigns),
TypedReducer<DesignState, LoadDesignSuccess>(_setLoadedDesign),
TypedReducer<DesignState, ArchiveDesignRequest>(_archiveDesignRequest),
TypedReducer<DesignState, ArchiveDesignSuccess>(_archiveDesignSuccess),
TypedReducer<DesignState, ArchiveDesignFailure>(_archiveDesignFailure),
TypedReducer<DesignState, DeleteDesignRequest>(_deleteDesignRequest),
TypedReducer<DesignState, DeleteDesignSuccess>(_deleteDesignSuccess),
TypedReducer<DesignState, DeleteDesignFailure>(_deleteDesignFailure),
TypedReducer<DesignState, RestoreDesignRequest>(_restoreDesignRequest),
TypedReducer<DesignState, RestoreDesignSuccess>(_restoreDesignSuccess),
TypedReducer<DesignState, RestoreDesignFailure>(_restoreDesignFailure),
]);
DesignState _archiveDesignRequest(
DesignState designState, ArchiveDesignsRequest action) {
final designs = action.designIds.map((id) => designState.map[id]).toList();
for (int i = 0; i < designs.length; i++) {
designs[i] = designs[i]
.rebuild((b) => b..archivedAt = DateTime.now().millisecondsSinceEpoch);
}
return designState.rebuild((b) {
for (final design in designs) {
b.map[design.id] = design;
}
});
}
DesignState _archiveDesignSuccess(
DesignState designState, ArchiveDesignsSuccess action) {
return designState.rebuild((b) {
for (final design in action.designs) {
b.map[design.id] = design;
}
});
}
DesignState _archiveDesignFailure(
DesignState designState, ArchiveDesignsFailure action) {
return designState.rebuild((b) {
for (final design in action.designs) {
b.map[design.id] = design;
}
});
}
DesignState _deleteDesignRequest(
DesignState designState, DeleteDesignsRequest action) {
final designs = action.designIds.map((id) => designState.map[id]).toList();
for (int i = 0; i < designs.length; i++) {
designs[i] = designs[i].rebuild((b) => b
..archivedAt = DateTime.now().millisecondsSinceEpoch
..isDeleted = true);
}
return designState.rebuild((b) {
for (final design in designs) {
b.map[design.id] = design;
}
});
}
DesignState _deleteDesignSuccess(
DesignState designState, DeleteDesignsSuccess action) {
return designState.rebuild((b) {
for (final design in action.designs) {
b.map[design.id] = design;
}
});
}
DesignState _deleteDesignFailure(
DesignState designState, DeleteDesignsFailure action) {
return designState.rebuild((b) {
for (final design in action.designs) {
b.map[design.id] = design;
}
});
}
DesignState _restoreDesignRequest(
DesignState designState, RestoreDesignsRequest action) {
final designs = action.designIds.map((id) => designState.map[id]).toList();
for (int i = 0; i < designs.length; i++) {
designs[i] = designs[i].rebuild((b) => b
..archivedAt = null
..isDeleted = false);
}
return designState.rebuild((b) {
for (final design in designs) {
b.map[design.id] = design;
}
});
}
DesignState _restoreDesignSuccess(
DesignState designState, RestoreDesignsSuccess action) {
return designState.rebuild((b) {
for (final design in action.designs) {
b.map[design.id] = design;
}
});
}
DesignState _restoreDesignFailure(
DesignState designState, RestoreDesignsFailure action) {
return designState.rebuild((b) {
for (final design in action.designs) {
b.map[design.id] = design;
}
});
}
DesignState _addDesign(DesignState designState, AddDesignSuccess action) {
return designState.rebuild((b) => b
..map[action.design.id] = action.design
..list.add(action.design.id));
}
DesignState _updateDesign(DesignState designState, SaveDesignSuccess action) {
return designState.rebuild((b) => b..map[action.design.id] = action.design);
}
DesignState _setLoadedDesign(
DesignState designState, LoadDesignSuccess action) {
return designState.rebuild((b) => b..map[action.design.id] = action.design);
}
DesignState _setLoadedDesigns(
DesignState designState, LoadDesignsSuccess action) =>
designState.loadDesigns(action.designs);

View File

@ -0,0 +1,65 @@
import 'package:invoiceninja_flutter/data/models/design_model.dart';
import 'package:memoize/memoize.dart';
import 'package:built_collection/built_collection.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/redux/ui/list_ui_state.dart';
var memoizedDropdownDesignList = memo3(
(BuiltMap<String, DesignEntity> designMap, BuiltList<String> designList,
String clientId) =>
dropdownDesignsSelector(designMap, designList, clientId));
List<String> dropdownDesignsSelector(BuiltMap<String, DesignEntity> designMap,
BuiltList<String> designList, String clientId) {
final list = designList.where((designId) {
final design = designMap[designId];
/*
if (clientId != null && clientId > 0 && design.clientId != clientId) {
return false;
}
*/
return design.isActive;
}).toList();
list.sort((designAId, designBId) {
final designA = designMap[designAId];
final designB = designMap[designBId];
return designA.compareTo(designB, DesignFields.name, true);
});
return list;
}
var memoizedFilteredDesignList = memo3(
(BuiltMap<String, DesignEntity> designMap, BuiltList<String> designList,
ListUIState designListState) =>
filteredDesignsSelector(designMap, designList, designListState));
List<String> filteredDesignsSelector(BuiltMap<String, DesignEntity> designMap,
BuiltList<String> designList, ListUIState designListState) {
final list = designList.where((designId) {
final design = designMap[designId];
if (designListState.filterEntityId != null &&
design.entityId != designListState.filterEntityId) {
return false;
} else {}
if (!design.matchesStates(designListState.stateFilters)) {
return false;
}
return design.matchesFilter(designListState.filter);
}).toList();
list.sort((designAId, designBId) {
final designA = designMap[designAId];
final designB = designMap[designBId];
return designA.compareTo(
designB, designListState.sortField, designListState.sortAscending);
});
return list;
}
bool hasDesignChanges(
DesignEntity design, BuiltMap<String, DesignEntity> designMap) =>
design.isNew ? design.isChanged : design != designMap[design.id];

View File

@ -0,0 +1,75 @@
import 'dart:async';
import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart';
import 'package:built_collection/built_collection.dart';
import 'package:invoiceninja_flutter/constants.dart';
import 'package:invoiceninja_flutter/data/models/design_model.dart';
import 'package:invoiceninja_flutter/redux/ui/entity_ui_state.dart';
import 'package:invoiceninja_flutter/redux/ui/list_ui_state.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
part 'design_state.g.dart';
abstract class DesignState implements Built<DesignState, DesignStateBuilder> {
factory DesignState() {
return _$DesignState._(
lastUpdated: 0,
map: BuiltMap<String, DesignEntity>(),
list: BuiltList<String>(),
);
}
DesignState._();
@nullable
int get lastUpdated;
BuiltMap<String, DesignEntity> get map;
BuiltList<String> get list;
bool get isStale {
if (!isLoaded) {
return true;
}
return DateTime.now().millisecondsSinceEpoch - lastUpdated >
kMillisecondsToRefreshData;
}
bool get isLoaded => lastUpdated != null && lastUpdated > 0;
DesignState loadDesigns(BuiltList<DesignEntity> clients) {
final map = Map<String, DesignEntity>.fromIterable(
clients,
key: (dynamic item) => item.id,
value: (dynamic item) => item,
);
return rebuild((b) => b
..lastUpdated = DateTime.now().millisecondsSinceEpoch
..map.addAll(map)
..list.replace(map.keys));
}
static Serializer<DesignState> get serializer => _$designStateSerializer;
}
abstract class DesignUIState extends Object
with EntityUIState
implements Built<DesignUIState, DesignUIStateBuilder> {
factory DesignUIState() {
return _$DesignUIState._(
listUIState: ListUIState(DesignFields.name),
editing: DesignEntity(),
selectedId: '',
);
}
DesignUIState._();
@nullable
DesignEntity get editing;
@override
bool get isCreatingNew => editing.isNew;
static Serializer<DesignUIState> get serializer => _$designUIStateSerializer;
}

View File

@ -0,0 +1,406 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'design_state.dart';
// **************************************************************************
// BuiltValueGenerator
// **************************************************************************
Serializer<DesignState> _$designStateSerializer = new _$DesignStateSerializer();
Serializer<DesignUIState> _$designUIStateSerializer =
new _$DesignUIStateSerializer();
class _$DesignStateSerializer implements StructuredSerializer<DesignState> {
@override
final Iterable<Type> types = const [DesignState, _$DesignState];
@override
final String wireName = 'DesignState';
@override
Iterable<Object> serialize(Serializers serializers, DesignState object,
{FullType specifiedType = FullType.unspecified}) {
final result = <Object>[
'map',
serializers.serialize(object.map,
specifiedType: const FullType(BuiltMap,
const [const FullType(String), const FullType(DesignEntity)])),
'list',
serializers.serialize(object.list,
specifiedType:
const FullType(BuiltList, const [const FullType(String)])),
];
if (object.lastUpdated != null) {
result
..add('lastUpdated')
..add(serializers.serialize(object.lastUpdated,
specifiedType: const FullType(int)));
}
return result;
}
@override
DesignState deserialize(Serializers serializers, Iterable<Object> serialized,
{FullType specifiedType = FullType.unspecified}) {
final result = new DesignStateBuilder();
final iterator = serialized.iterator;
while (iterator.moveNext()) {
final key = iterator.current as String;
iterator.moveNext();
final dynamic value = iterator.current;
switch (key) {
case 'lastUpdated':
result.lastUpdated = serializers.deserialize(value,
specifiedType: const FullType(int)) as int;
break;
case 'map':
result.map.replace(serializers.deserialize(value,
specifiedType: const FullType(BuiltMap, const [
const FullType(String),
const FullType(DesignEntity)
])));
break;
case 'list':
result.list.replace(serializers.deserialize(value,
specifiedType:
const FullType(BuiltList, const [const FullType(String)]))
as BuiltList<Object>);
break;
}
}
return result.build();
}
}
class _$DesignUIStateSerializer implements StructuredSerializer<DesignUIState> {
@override
final Iterable<Type> types = const [DesignUIState, _$DesignUIState];
@override
final String wireName = 'DesignUIState';
@override
Iterable<Object> serialize(Serializers serializers, DesignUIState object,
{FullType specifiedType = FullType.unspecified}) {
final result = <Object>[
'listUIState',
serializers.serialize(object.listUIState,
specifiedType: const FullType(ListUIState)),
];
if (object.editing != null) {
result
..add('editing')
..add(serializers.serialize(object.editing,
specifiedType: const FullType(DesignEntity)));
}
if (object.selectedId != null) {
result
..add('selectedId')
..add(serializers.serialize(object.selectedId,
specifiedType: const FullType(String)));
}
return result;
}
@override
DesignUIState deserialize(
Serializers serializers, Iterable<Object> serialized,
{FullType specifiedType = FullType.unspecified}) {
final result = new DesignUIStateBuilder();
final iterator = serialized.iterator;
while (iterator.moveNext()) {
final key = iterator.current as String;
iterator.moveNext();
final dynamic value = iterator.current;
switch (key) {
case 'editing':
result.editing.replace(serializers.deserialize(value,
specifiedType: const FullType(DesignEntity)) as DesignEntity);
break;
case 'listUIState':
result.listUIState.replace(serializers.deserialize(value,
specifiedType: const FullType(ListUIState)) as ListUIState);
break;
case 'selectedId':
result.selectedId = serializers.deserialize(value,
specifiedType: const FullType(String)) as String;
break;
}
}
return result.build();
}
}
class _$DesignState extends DesignState {
@override
final int lastUpdated;
@override
final BuiltMap<String, DesignEntity> map;
@override
final BuiltList<String> list;
factory _$DesignState([void Function(DesignStateBuilder) updates]) =>
(new DesignStateBuilder()..update(updates)).build();
_$DesignState._({this.lastUpdated, this.map, this.list}) : super._() {
if (map == null) {
throw new BuiltValueNullFieldError('DesignState', 'map');
}
if (list == null) {
throw new BuiltValueNullFieldError('DesignState', 'list');
}
}
@override
DesignState rebuild(void Function(DesignStateBuilder) updates) =>
(toBuilder()..update(updates)).build();
@override
DesignStateBuilder toBuilder() => new DesignStateBuilder()..replace(this);
@override
bool operator ==(Object other) {
if (identical(other, this)) return true;
return other is DesignState &&
lastUpdated == other.lastUpdated &&
map == other.map &&
list == other.list;
}
@override
int get hashCode {
return $jf(
$jc($jc($jc(0, lastUpdated.hashCode), map.hashCode), list.hashCode));
}
@override
String toString() {
return (newBuiltValueToStringHelper('DesignState')
..add('lastUpdated', lastUpdated)
..add('map', map)
..add('list', list))
.toString();
}
}
class DesignStateBuilder implements Builder<DesignState, DesignStateBuilder> {
_$DesignState _$v;
int _lastUpdated;
int get lastUpdated => _$this._lastUpdated;
set lastUpdated(int lastUpdated) => _$this._lastUpdated = lastUpdated;
MapBuilder<String, DesignEntity> _map;
MapBuilder<String, DesignEntity> get map =>
_$this._map ??= new MapBuilder<String, DesignEntity>();
set map(MapBuilder<String, DesignEntity> map) => _$this._map = map;
ListBuilder<String> _list;
ListBuilder<String> get list => _$this._list ??= new ListBuilder<String>();
set list(ListBuilder<String> list) => _$this._list = list;
DesignStateBuilder();
DesignStateBuilder get _$this {
if (_$v != null) {
_lastUpdated = _$v.lastUpdated;
_map = _$v.map?.toBuilder();
_list = _$v.list?.toBuilder();
_$v = null;
}
return this;
}
@override
void replace(DesignState other) {
if (other == null) {
throw new ArgumentError.notNull('other');
}
_$v = other as _$DesignState;
}
@override
void update(void Function(DesignStateBuilder) updates) {
if (updates != null) updates(this);
}
@override
_$DesignState build() {
_$DesignState _$result;
try {
_$result = _$v ??
new _$DesignState._(
lastUpdated: lastUpdated, map: map.build(), list: list.build());
} catch (_) {
String _$failedField;
try {
_$failedField = 'map';
map.build();
_$failedField = 'list';
list.build();
} catch (e) {
throw new BuiltValueNestedFieldError(
'DesignState', _$failedField, e.toString());
}
rethrow;
}
replace(_$result);
return _$result;
}
}
class _$DesignUIState extends DesignUIState {
@override
final DesignEntity editing;
@override
final ListUIState listUIState;
@override
final String selectedId;
@override
final Completer<SelectableEntity> saveCompleter;
@override
final Completer<Null> cancelCompleter;
factory _$DesignUIState([void Function(DesignUIStateBuilder) updates]) =>
(new DesignUIStateBuilder()..update(updates)).build();
_$DesignUIState._(
{this.editing,
this.listUIState,
this.selectedId,
this.saveCompleter,
this.cancelCompleter})
: super._() {
if (listUIState == null) {
throw new BuiltValueNullFieldError('DesignUIState', 'listUIState');
}
}
@override
DesignUIState rebuild(void Function(DesignUIStateBuilder) updates) =>
(toBuilder()..update(updates)).build();
@override
DesignUIStateBuilder toBuilder() => new DesignUIStateBuilder()..replace(this);
@override
bool operator ==(Object other) {
if (identical(other, this)) return true;
return other is DesignUIState &&
editing == other.editing &&
listUIState == other.listUIState &&
selectedId == other.selectedId &&
saveCompleter == other.saveCompleter &&
cancelCompleter == other.cancelCompleter;
}
@override
int get hashCode {
return $jf($jc(
$jc(
$jc($jc($jc(0, editing.hashCode), listUIState.hashCode),
selectedId.hashCode),
saveCompleter.hashCode),
cancelCompleter.hashCode));
}
@override
String toString() {
return (newBuiltValueToStringHelper('DesignUIState')
..add('editing', editing)
..add('listUIState', listUIState)
..add('selectedId', selectedId)
..add('saveCompleter', saveCompleter)
..add('cancelCompleter', cancelCompleter))
.toString();
}
}
class DesignUIStateBuilder
implements Builder<DesignUIState, DesignUIStateBuilder> {
_$DesignUIState _$v;
DesignEntityBuilder _editing;
DesignEntityBuilder get editing =>
_$this._editing ??= new DesignEntityBuilder();
set editing(DesignEntityBuilder editing) => _$this._editing = editing;
ListUIStateBuilder _listUIState;
ListUIStateBuilder get listUIState =>
_$this._listUIState ??= new ListUIStateBuilder();
set listUIState(ListUIStateBuilder listUIState) =>
_$this._listUIState = listUIState;
String _selectedId;
String get selectedId => _$this._selectedId;
set selectedId(String selectedId) => _$this._selectedId = selectedId;
Completer<SelectableEntity> _saveCompleter;
Completer<SelectableEntity> get saveCompleter => _$this._saveCompleter;
set saveCompleter(Completer<SelectableEntity> saveCompleter) =>
_$this._saveCompleter = saveCompleter;
Completer<Null> _cancelCompleter;
Completer<Null> get cancelCompleter => _$this._cancelCompleter;
set cancelCompleter(Completer<Null> cancelCompleter) =>
_$this._cancelCompleter = cancelCompleter;
DesignUIStateBuilder();
DesignUIStateBuilder get _$this {
if (_$v != null) {
_editing = _$v.editing?.toBuilder();
_listUIState = _$v.listUIState?.toBuilder();
_selectedId = _$v.selectedId;
_saveCompleter = _$v.saveCompleter;
_cancelCompleter = _$v.cancelCompleter;
_$v = null;
}
return this;
}
@override
void replace(DesignUIState other) {
if (other == null) {
throw new ArgumentError.notNull('other');
}
_$v = other as _$DesignUIState;
}
@override
void update(void Function(DesignUIStateBuilder) updates) {
if (updates != null) updates(this);
}
@override
_$DesignUIState build() {
_$DesignUIState _$result;
try {
_$result = _$v ??
new _$DesignUIState._(
editing: _editing?.build(),
listUIState: listUIState.build(),
selectedId: selectedId,
saveCompleter: saveCompleter,
cancelCompleter: cancelCompleter);
} catch (_) {
String _$failedField;
try {
_$failedField = 'editing';
_editing?.build();
_$failedField = 'listUIState';
listUIState.build();
} catch (e) {
throw new BuiltValueNestedFieldError(
'DesignUIState', _$failedField, e.toString());
}
rethrow;
}
replace(_$result);
return _$result;
}
}
// ignore_for_file: always_put_control_body_on_new_line,always_specify_types,annotate_overrides,avoid_annotating_with_dynamic,avoid_as,avoid_catches_without_on_clauses,avoid_returning_this,lines_longer_than_80_chars,omit_local_variable_types,prefer_expression_function_bodies,sort_constructors_first,test_types_in_equals,unnecessary_const,unnecessary_new

View File

@ -23,6 +23,8 @@ import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart';
import 'package:invoiceninja_flutter/redux/user/user_actions.dart'; import 'package:invoiceninja_flutter/redux/user/user_actions.dart';
import 'package:invoiceninja_flutter/redux/vendor/vendor_actions.dart'; import 'package:invoiceninja_flutter/redux/vendor/vendor_actions.dart';
// STARTER: import - do not remove comment // STARTER: import - do not remove comment
import 'package:invoiceninja_flutter/redux/design/design_actions.dart';
import 'package:invoiceninja_flutter/redux/credit/credit_actions.dart'; import 'package:invoiceninja_flutter/redux/credit/credit_actions.dart';
import 'package:redux/redux.dart'; import 'package:redux/redux.dart';
@ -397,6 +399,13 @@ Reducer<BuiltList<HistoryRecord>> historyReducer = combineReducers([
_addToHistory(historyList, _addToHistory(historyList,
HistoryRecord(id: action.group.id, entityType: EntityType.group))), HistoryRecord(id: action.group.id, entityType: EntityType.group))),
// STARTER: history - do not remove comment // STARTER: history - do not remove comment
TypedReducer<BuiltList<HistoryRecord>, ViewDesign>((historyList, action) =>
_addToHistory(historyList,
HistoryRecord(id: action.designId, entityType: EntityType.design))),
TypedReducer<BuiltList<HistoryRecord>, EditDesign>((historyList, action) =>
_addToHistory(historyList,
HistoryRecord(id: action.design.id, entityType: EntityType.design))),
TypedReducer<BuiltList<HistoryRecord>, ViewCredit>((historyList, action) => TypedReducer<BuiltList<HistoryRecord>, ViewCredit>((historyList, action) =>
_addToHistory(historyList, _addToHistory(historyList,
HistoryRecord(id: action.creditId, entityType: EntityType.credit))), HistoryRecord(id: action.creditId, entityType: EntityType.credit))),

View File

@ -22,6 +22,8 @@ import 'package:invoiceninja_flutter/redux/quote/quote_reducer.dart';
import 'package:invoiceninja_flutter/redux/task/task_reducer.dart'; import 'package:invoiceninja_flutter/redux/task/task_reducer.dart';
import 'package:invoiceninja_flutter/redux/vendor/vendor_reducer.dart'; import 'package:invoiceninja_flutter/redux/vendor/vendor_reducer.dart';
// STARTER: import - do not remove comment // STARTER: import - do not remove comment
import 'package:invoiceninja_flutter/redux/design/design_reducer.dart';
import 'package:invoiceninja_flutter/redux/credit/credit_reducer.dart'; import 'package:invoiceninja_flutter/redux/credit/credit_reducer.dart';
import 'package:invoiceninja_flutter/redux/user/user_reducer.dart'; import 'package:invoiceninja_flutter/redux/user/user_reducer.dart';
@ -49,6 +51,7 @@ UIState uiReducer(UIState state, dynamic action) {
.replace(dashboardUIReducer(state.dashboardUIState, action)) .replace(dashboardUIReducer(state.dashboardUIState, action))
..reportsUIState.replace(reportsUIReducer(state.reportsUIState, action)) ..reportsUIState.replace(reportsUIReducer(state.reportsUIState, action))
// STARTER: reducer - do not remove comment // STARTER: reducer - do not remove comment
..designUIState.replace(designUIReducer(state.designUIState, action))
..creditUIState.replace(creditUIReducer(state.creditUIState, action)) ..creditUIState.replace(creditUIReducer(state.creditUIState, action))
..userUIState.replace(userUIReducer(state.userUIState, action)) ..userUIState.replace(userUIReducer(state.userUIState, action))
..taxRateUIState.replace(taxRateUIReducer(state.taxRateUIState, action)) ..taxRateUIState.replace(taxRateUIReducer(state.taxRateUIState, action))

View File

@ -16,6 +16,8 @@ import 'package:invoiceninja_flutter/redux/task/task_state.dart';
import 'package:invoiceninja_flutter/redux/vendor/vendor_state.dart'; import 'package:invoiceninja_flutter/redux/vendor/vendor_state.dart';
// STARTER: import - do not remove comment // STARTER: import - do not remove comment
import 'package:invoiceninja_flutter/redux/design/design_state.dart';
import 'package:invoiceninja_flutter/redux/credit/credit_state.dart'; import 'package:invoiceninja_flutter/redux/credit/credit_state.dart';
import 'package:invoiceninja_flutter/redux/user/user_state.dart'; import 'package:invoiceninja_flutter/redux/user/user_state.dart';
@ -37,6 +39,8 @@ abstract class UIState implements Built<UIState, UIStateBuilder> {
clientUIState: ClientUIState(), clientUIState: ClientUIState(),
invoiceUIState: InvoiceUIState(), invoiceUIState: InvoiceUIState(),
// STARTER: constructor - do not remove comment // STARTER: constructor - do not remove comment
designUIState: DesignUIState(),
creditUIState: CreditUIState(), creditUIState: CreditUIState(),
userUIState: UserUIState(), userUIState: UserUIState(),
@ -77,6 +81,8 @@ abstract class UIState implements Built<UIState, UIStateBuilder> {
InvoiceUIState get invoiceUIState; InvoiceUIState get invoiceUIState;
// STARTER: properties - do not remove comment // STARTER: properties - do not remove comment
DesignUIState get designUIState;
CreditUIState get creditUIState; CreditUIState get creditUIState;
UserUIState get userUIState; UserUIState get userUIState;

View File

@ -42,6 +42,9 @@ class _$UIStateSerializer implements StructuredSerializer<UIState> {
'invoiceUIState', 'invoiceUIState',
serializers.serialize(object.invoiceUIState, serializers.serialize(object.invoiceUIState,
specifiedType: const FullType(InvoiceUIState)), specifiedType: const FullType(InvoiceUIState)),
'designUIState',
serializers.serialize(object.designUIState,
specifiedType: const FullType(DesignUIState)),
'creditUIState', 'creditUIState',
serializers.serialize(object.creditUIState, serializers.serialize(object.creditUIState,
specifiedType: const FullType(CreditUIState)), specifiedType: const FullType(CreditUIState)),
@ -142,6 +145,10 @@ class _$UIStateSerializer implements StructuredSerializer<UIState> {
result.invoiceUIState.replace(serializers.deserialize(value, result.invoiceUIState.replace(serializers.deserialize(value,
specifiedType: const FullType(InvoiceUIState)) as InvoiceUIState); specifiedType: const FullType(InvoiceUIState)) as InvoiceUIState);
break; break;
case 'designUIState':
result.designUIState.replace(serializers.deserialize(value,
specifiedType: const FullType(DesignUIState)) as DesignUIState);
break;
case 'creditUIState': case 'creditUIState':
result.creditUIState.replace(serializers.deserialize(value, result.creditUIState.replace(serializers.deserialize(value,
specifiedType: const FullType(CreditUIState)) as CreditUIState); specifiedType: const FullType(CreditUIState)) as CreditUIState);
@ -228,6 +235,8 @@ class _$UIState extends UIState {
@override @override
final InvoiceUIState invoiceUIState; final InvoiceUIState invoiceUIState;
@override @override
final DesignUIState designUIState;
@override
final CreditUIState creditUIState; final CreditUIState creditUIState;
@override @override
final UserUIState userUIState; final UserUIState userUIState;
@ -269,6 +278,7 @@ class _$UIState extends UIState {
this.productUIState, this.productUIState,
this.clientUIState, this.clientUIState,
this.invoiceUIState, this.invoiceUIState,
this.designUIState,
this.creditUIState, this.creditUIState,
this.userUIState, this.userUIState,
this.taxRateUIState, this.taxRateUIState,
@ -308,6 +318,9 @@ class _$UIState extends UIState {
if (invoiceUIState == null) { if (invoiceUIState == null) {
throw new BuiltValueNullFieldError('UIState', 'invoiceUIState'); throw new BuiltValueNullFieldError('UIState', 'invoiceUIState');
} }
if (designUIState == null) {
throw new BuiltValueNullFieldError('UIState', 'designUIState');
}
if (creditUIState == null) { if (creditUIState == null) {
throw new BuiltValueNullFieldError('UIState', 'creditUIState'); throw new BuiltValueNullFieldError('UIState', 'creditUIState');
} }
@ -372,6 +385,7 @@ class _$UIState extends UIState {
productUIState == other.productUIState && productUIState == other.productUIState &&
clientUIState == other.clientUIState && clientUIState == other.clientUIState &&
invoiceUIState == other.invoiceUIState && invoiceUIState == other.invoiceUIState &&
designUIState == other.designUIState &&
creditUIState == other.creditUIState && creditUIState == other.creditUIState &&
userUIState == other.userUIState && userUIState == other.userUIState &&
taxRateUIState == other.taxRateUIState && taxRateUIState == other.taxRateUIState &&
@ -408,12 +422,12 @@ class _$UIState extends UIState {
$jc( $jc(
$jc( $jc(
$jc( $jc(
$jc($jc($jc($jc($jc(0, selectedCompanyIndex.hashCode), currentRoute.hashCode), previousRoute.hashCode), filter.hashCode), $jc($jc($jc($jc($jc($jc(0, selectedCompanyIndex.hashCode), currentRoute.hashCode), previousRoute.hashCode), filter.hashCode), filterClearedAt.hashCode),
filterClearedAt.hashCode), dashboardUIState.hashCode),
dashboardUIState.hashCode), productUIState.hashCode),
productUIState.hashCode), clientUIState.hashCode),
clientUIState.hashCode), invoiceUIState.hashCode),
invoiceUIState.hashCode), designUIState.hashCode),
creditUIState.hashCode), creditUIState.hashCode),
userUIState.hashCode), userUIState.hashCode),
taxRateUIState.hashCode), taxRateUIState.hashCode),
@ -442,6 +456,7 @@ class _$UIState extends UIState {
..add('productUIState', productUIState) ..add('productUIState', productUIState)
..add('clientUIState', clientUIState) ..add('clientUIState', clientUIState)
..add('invoiceUIState', invoiceUIState) ..add('invoiceUIState', invoiceUIState)
..add('designUIState', designUIState)
..add('creditUIState', creditUIState) ..add('creditUIState', creditUIState)
..add('userUIState', userUIState) ..add('userUIState', userUIState)
..add('taxRateUIState', taxRateUIState) ..add('taxRateUIState', taxRateUIState)
@ -510,6 +525,12 @@ class UIStateBuilder implements Builder<UIState, UIStateBuilder> {
set invoiceUIState(InvoiceUIStateBuilder invoiceUIState) => set invoiceUIState(InvoiceUIStateBuilder invoiceUIState) =>
_$this._invoiceUIState = invoiceUIState; _$this._invoiceUIState = invoiceUIState;
DesignUIStateBuilder _designUIState;
DesignUIStateBuilder get designUIState =>
_$this._designUIState ??= new DesignUIStateBuilder();
set designUIState(DesignUIStateBuilder designUIState) =>
_$this._designUIState = designUIState;
CreditUIStateBuilder _creditUIState; CreditUIStateBuilder _creditUIState;
CreditUIStateBuilder get creditUIState => CreditUIStateBuilder get creditUIState =>
_$this._creditUIState ??= new CreditUIStateBuilder(); _$this._creditUIState ??= new CreditUIStateBuilder();
@ -608,6 +629,7 @@ class UIStateBuilder implements Builder<UIState, UIStateBuilder> {
_productUIState = _$v.productUIState?.toBuilder(); _productUIState = _$v.productUIState?.toBuilder();
_clientUIState = _$v.clientUIState?.toBuilder(); _clientUIState = _$v.clientUIState?.toBuilder();
_invoiceUIState = _$v.invoiceUIState?.toBuilder(); _invoiceUIState = _$v.invoiceUIState?.toBuilder();
_designUIState = _$v.designUIState?.toBuilder();
_creditUIState = _$v.creditUIState?.toBuilder(); _creditUIState = _$v.creditUIState?.toBuilder();
_userUIState = _$v.userUIState?.toBuilder(); _userUIState = _$v.userUIState?.toBuilder();
_taxRateUIState = _$v.taxRateUIState?.toBuilder(); _taxRateUIState = _$v.taxRateUIState?.toBuilder();
@ -655,6 +677,7 @@ class UIStateBuilder implements Builder<UIState, UIStateBuilder> {
productUIState: productUIState.build(), productUIState: productUIState.build(),
clientUIState: clientUIState.build(), clientUIState: clientUIState.build(),
invoiceUIState: invoiceUIState.build(), invoiceUIState: invoiceUIState.build(),
designUIState: designUIState.build(),
creditUIState: creditUIState.build(), creditUIState: creditUIState.build(),
userUIState: userUIState.build(), userUIState: userUIState.build(),
taxRateUIState: taxRateUIState.build(), taxRateUIState: taxRateUIState.build(),
@ -680,6 +703,8 @@ class UIStateBuilder implements Builder<UIState, UIStateBuilder> {
clientUIState.build(); clientUIState.build();
_$failedField = 'invoiceUIState'; _$failedField = 'invoiceUIState';
invoiceUIState.build(); invoiceUIState.build();
_$failedField = 'designUIState';
designUIState.build();
_$failedField = 'creditUIState'; _$failedField = 'creditUIState';
creditUIState.build(); creditUIState.build();
_$failedField = 'userUIState'; _$failedField = 'userUIState';

View File

@ -28,6 +28,8 @@ import 'package:invoiceninja_flutter/utils/localization.dart';
import 'package:invoiceninja_flutter/utils/platforms.dart'; import 'package:invoiceninja_flutter/utils/platforms.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
// STARTER: import - do not remove comment // STARTER: import - do not remove comment
import 'package:invoiceninja_flutter/redux/design/design_actions.dart';
class MenuDrawer extends StatelessWidget { class MenuDrawer extends StatelessWidget {
const MenuDrawer({ const MenuDrawer({
@ -256,6 +258,14 @@ class MenuDrawer extends StatelessWidget {
title: localization.expenses, title: localization.expenses,
), ),
// STARTER: menu - do not remove comment // STARTER: menu - do not remove comment
DrawerTile(
company: company,
entityType: EntityType.design,
icon: getEntityIcon(EntityType.design),
title: localization.designs,
},
),
DrawerTile( DrawerTile(
company: company, company: company,
icon: getEntityIcon(EntityType.reports), icon: getEntityIcon(EntityType.reports),

View File

@ -54,22 +54,22 @@ class CreditListVM extends EntityListVM {
List<String> tableColumns, List<String> tableColumns,
EntityType entityType, EntityType entityType,
}) : super( }) : super(
state: state, state: state,
user: user, user: user,
listState: listState, listState: listState,
invoiceList: invoiceList, invoiceList: invoiceList,
invoiceMap: invoiceMap, invoiceMap: invoiceMap,
clientMap: clientMap, clientMap: clientMap,
filter: filter, filter: filter,
isLoading: isLoading, isLoading: isLoading,
isLoaded: isLoaded, isLoaded: isLoaded,
onInvoiceTap: onInvoiceTap, onInvoiceTap: onInvoiceTap,
onRefreshed: onRefreshed, onRefreshed: onRefreshed,
onClearEntityFilterPressed: onClearEntityFilterPressed, onClearEntityFilterPressed: onClearEntityFilterPressed,
onViewEntityFilterPressed: onViewEntityFilterPressed, onViewEntityFilterPressed: onViewEntityFilterPressed,
tableColumns: tableColumns, tableColumns: tableColumns,
entityType: entityType, entityType: entityType,
); );
static CreditListVM fromStore(Store<AppState> store) { static CreditListVM fromStore(Store<AppState> store) {
Future<Null> _handleRefresh(BuildContext context) { Future<Null> _handleRefresh(BuildContext context) {
@ -105,7 +105,7 @@ class CreditListVM extends EntityListVM {
entityId: state.creditListState.filterEntityId, entityId: state.creditListState.filterEntityId,
entityType: state.creditListState.filterEntityType), entityType: state.creditListState.filterEntityType),
onEntityAction: (BuildContext context, List<BaseEntity> credits, onEntityAction: (BuildContext context, List<BaseEntity> credits,
EntityAction action) => EntityAction action) =>
handleCreditAction(context, credits, action), handleCreditAction(context, credits, action),
tableColumns: CreditPresenter.getTableFields(state.userCompany), tableColumns: CreditPresenter.getTableFields(state.userCompany),
entityType: EntityType.credit, entityType: EntityType.credit,

View File

@ -29,7 +29,7 @@ class CreditPresenter extends EntityPresenter {
return Text(credit.number); return Text(credit.number);
case CreditFields.client: case CreditFields.client:
return Text((state.clientState.map[credit.clientId] ?? return Text((state.clientState.map[credit.clientId] ??
ClientEntity(id: credit.clientId)) ClientEntity(id: credit.clientId))
.listDisplayName); .listDisplayName);
case CreditFields.date: case CreditFields.date:
return Text(formatDate(credit.date, context)); return Text(formatDate(credit.date, context));

View File

@ -26,7 +26,7 @@ class _CreditEditState extends State<CreditEdit>
TabController _controller; TabController _controller;
static final GlobalKey<FormState> _formKey = static final GlobalKey<FormState> _formKey =
GlobalKey<FormState>(debugLabel: '_creditEdit'); GlobalKey<FormState>(debugLabel: '_creditEdit');
static const kDetailsScreen = 0; static const kDetailsScreen = 0;
static const kItemScreen = 1; static const kItemScreen = 1;
@ -39,7 +39,7 @@ class _CreditEditState extends State<CreditEdit>
final viewModel = widget.viewModel; final viewModel = widget.viewModel;
final index = final index =
viewModel.invoiceItemIndex != null ? kItemScreen : kDetailsScreen; viewModel.invoiceItemIndex != null ? kItemScreen : kDetailsScreen;
_controller = TabController(vsync: this, length: 3, initialIndex: index); _controller = TabController(vsync: this, length: 3, initialIndex: index);
} }
@ -79,33 +79,33 @@ class _CreditEditState extends State<CreditEdit>
appBarBottom: state.prefState.isDesktop appBarBottom: state.prefState.isDesktop
? null ? null
: TabBar( : TabBar(
controller: _controller, controller: _controller,
//isScrollable: true, //isScrollable: true,
tabs: [ tabs: [
Tab( Tab(
text: localization.details, text: localization.details,
), ),
Tab( Tab(
text: localization.items, text: localization.items,
), ),
Tab( Tab(
text: localization.notes, text: localization.notes,
), ),
], ],
), ),
body: Form( body: Form(
key: _formKey, key: _formKey,
child: state.prefState.isDesktop child: state.prefState.isDesktop
? CreditEditDetailsScreen() ? CreditEditDetailsScreen()
: TabBarView( : TabBarView(
key: ValueKey('__credit_${viewModel.invoice.id}__'), key: ValueKey('__credit_${viewModel.invoice.id}__'),
controller: _controller, controller: _controller,
children: <Widget>[ children: <Widget>[
CreditEditDetailsScreen(), CreditEditDetailsScreen(),
CreditEditItemsScreen(), CreditEditItemsScreen(),
CreditEditNotesScreen(), CreditEditNotesScreen(),
], ],
), ),
), ),
bottomNavigationBar: BottomAppBar( bottomNavigationBar: BottomAppBar(
color: Theme.of(context).primaryColor, color: Theme.of(context).primaryColor,
@ -133,8 +133,8 @@ class _CreditEditState extends State<CreditEdit>
excluded: invoice.lineItems excluded: invoice.lineItems
.where((item) => item.isTask || item.isExpense) .where((item) => item.isTask || item.isExpense)
.map((item) => item.isTask .map((item) => item.isTask
? viewModel.state.taskState.map[item.taskId] ? viewModel.state.taskState.map[item.taskId]
: viewModel.state.expenseState.map[item.expenseId]) : viewModel.state.expenseState.map[item.expenseId])
.toList(), .toList(),
clientId: invoice.clientId, clientId: invoice.clientId,
onItemsSelected: (items, [clientId]) { onItemsSelected: (items, [clientId]) {

View File

@ -74,7 +74,8 @@ class CreditEditItemsVM extends EntityEditItemsVM {
if (index == credit.lineItems.length) { if (index == credit.lineItems.length) {
store.dispatch(AddCreditItem(creditItem: creditItem)); store.dispatch(AddCreditItem(creditItem: creditItem));
} else { } else {
store.dispatch(UpdateCreditItem(creditItem: creditItem, index: index)); store.dispatch(
UpdateCreditItem(creditItem: creditItem, index: index));
} }
}); });
} }

View File

@ -46,16 +46,16 @@ class CreditEditVM extends EntityEditVM {
bool isSaving, bool isSaving,
Function(BuildContext) onCancelPressed, Function(BuildContext) onCancelPressed,
}) : super( }) : super(
state: state, state: state,
company: company, company: company,
invoice: invoice, invoice: invoice,
invoiceItemIndex: invoiceItemIndex, invoiceItemIndex: invoiceItemIndex,
origInvoice: origInvoice, origInvoice: origInvoice,
onSavePressed: onSavePressed, onSavePressed: onSavePressed,
onItemsAdded: onItemsAdded, onItemsAdded: onItemsAdded,
isSaving: isSaving, isSaving: isSaving,
onCancelPressed: onCancelPressed, onCancelPressed: onCancelPressed,
); );
factory CreditEditVM.fromStore(Store<AppState> store) { factory CreditEditVM.fromStore(Store<AppState> store) {
final AppState state = store.state; final AppState state = store.state;
@ -75,7 +75,8 @@ class CreditEditVM extends EntityEditVM {
if (isMobile(context)) { if (isMobile(context)) {
store.dispatch(UpdateCurrentRoute(CreditViewScreen.route)); store.dispatch(UpdateCurrentRoute(CreditViewScreen.route));
if (credit.isNew) { if (credit.isNew) {
Navigator.of(context).pushReplacementNamed(CreditViewScreen.route); Navigator.of(context)
.pushReplacementNamed(CreditViewScreen.route);
} else { } else {
Navigator.of(context).pop(savedCredit); Navigator.of(context).pop(savedCredit);
} }

View File

@ -56,22 +56,22 @@ class CreditViewVM extends EntityViewVM {
Function(BuildContext, DocumentEntity) onDeleteDocument, Function(BuildContext, DocumentEntity) onDeleteDocument,
Function(BuildContext, DocumentEntity) onViewExpense, Function(BuildContext, DocumentEntity) onViewExpense,
}) : super( }) : super(
state: state, state: state,
company: company, company: company,
invoice: invoice, invoice: invoice,
client: client, client: client,
isSaving: isSaving, isSaving: isSaving,
isDirty: isDirty, isDirty: isDirty,
onActionSelected: onEntityAction, onActionSelected: onEntityAction,
onEditPressed: onEditPressed, onEditPressed: onEditPressed,
onClientPressed: onClientPressed, onClientPressed: onClientPressed,
onPaymentsPressed: onPaymentsPressed, onPaymentsPressed: onPaymentsPressed,
onPaymentPressed: onPaymentPressed, onPaymentPressed: onPaymentPressed,
onRefreshed: onRefreshed, onRefreshed: onRefreshed,
onUploadDocument: onUploadDocument, onUploadDocument: onUploadDocument,
onDeleteDocument: onDeleteDocument, onDeleteDocument: onDeleteDocument,
onViewExpense: onViewExpense, onViewExpense: onViewExpense,
); );
factory CreditViewVM.fromStore(Store<AppState> store) { factory CreditViewVM.fromStore(Store<AppState> store) {
final state = store.state; final state = store.state;
@ -125,8 +125,8 @@ class CreditViewVM extends EntityViewVM {
completer.future.then((client) { completer.future.then((client) {
Scaffold.of(context).showSnackBar(SnackBar( Scaffold.of(context).showSnackBar(SnackBar(
content: SnackBarRow( content: SnackBarRow(
message: AppLocalization.of(context).uploadedDocument, message: AppLocalization.of(context).uploadedDocument,
))); )));
}).catchError((Object error) { }).catchError((Object error) {
showDialog<ErrorDialog>( showDialog<ErrorDialog>(
context: context, context: context,

View File

@ -0,0 +1,167 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/redux/app/app_actions.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
import 'package:invoiceninja_flutter/redux/design/design_actions.dart';
import 'package:invoiceninja_flutter/redux/ui/pref_state.dart';
import 'package:invoiceninja_flutter/ui/app/entities/entity_actions_dialog.dart';
import 'package:invoiceninja_flutter/ui/app/help_text.dart';
import 'package:invoiceninja_flutter/ui/app/lists/list_divider.dart';
import 'package:invoiceninja_flutter/ui/app/loading_indicator.dart';
import 'package:invoiceninja_flutter/ui/app/presenters/entity_presenter.dart';
import 'package:invoiceninja_flutter/ui/design/design_presenter.dart';
import 'package:invoiceninja_flutter/ui/app/tables/entity_datatable.dart';
import 'package:invoiceninja_flutter/ui/design/design_list_item.dart';
import 'package:invoiceninja_flutter/ui/design/design_list_vm.dart';
import 'package:invoiceninja_flutter/utils/localization.dart';
class DesignList extends StatefulWidget {
const DesignList({
Key key,
@required this.viewModel,
}) : super(key: key);
final DesignListVM viewModel;
@override
_DesignListState createState() => _DesignListState();
}
class _DesignListState extends State<DesignList> {
EntityDataTableSource dataTableSource;
@override
void initState() {
super.initState();
final viewModel = widget.viewModel;
dataTableSource = EntityDataTableSource(
context: context,
entityType: EntityType.design,
editingId: viewModel.state.designUIState.editing.id,
tableColumns: viewModel.tableColumns,
entityList: viewModel.designList,
entityMap: viewModel.designMap,
entityPresenter: DesignPresenter(),
onTap: (BaseEntity design) => viewModel.onDesignTap(context, design));
}
@override
void didUpdateWidget(DesignList oldWidget) {
super.didUpdateWidget(oldWidget);
final viewModel = widget.viewModel;
dataTableSource.editingId = viewModel.state.designUIState.editing.id;
dataTableSource.entityList = viewModel.designList;
dataTableSource.entityMap = viewModel.designMap;
// ignore: invalid_use_of_visible_for_testing_member, invalid_use_of_protected_member
dataTableSource.notifyListeners();
}
@override
Widget build(BuildContext context) {
final store = StoreProvider.of<AppState>(context);
final viewModel = widget.viewModel;
final state = viewModel.state;
final listUIState = state.uiState.designUIState.listUIState;
final isInMultiselect = listUIState.isInMultiselect();
final isList = state.prefState.moduleLayout == ModuleLayout.list;
final designList = viewModel.designList;
if (!viewModel.isLoaded) {
return viewModel.isLoading ? LoadingIndicator() : SizedBox();
} else if (viewModel.designMap.isEmpty) {
return HelpText(AppLocalization.of(context).noRecordsFound);
}
if (state.shouldSelectEntity(
entityType: EntityType.design, hasRecords: designList.isNotEmpty)) {
viewEntityById(
context: context,
entityType: EntityType.design,
entityId: designList.isEmpty ? null : designList.first,
);
}
final listOrTable = () {
if (isList) {
return ListView.separated(
separatorBuilder: (context, index) => ListDivider(),
itemCount: viewModel.designList.length,
itemBuilder: (BuildContext context, index) {
final designId = viewModel.designList[index];
final design = viewModel.designMap[designId];
return DesignListItem(
user: viewModel.state.user,
filter: viewModel.filter,
design: design,
onEntityAction: (EntityAction action) {
if (action == EntityAction.more) {
showEntityActionsDialog(
entities: [design],
context: context,
);
} else {
handleDesignAction(context, [design], action);
}
},
onTap: () => viewModel.onDesignTap(context, design),
onLongPress: () async {
final longPressIsSelection =
state.prefState.longPressSelectionIsDefault ?? true;
if (longPressIsSelection && !isInMultiselect) {
handleDesignAction(
context, [design], EntityAction.toggleMultiselect);
} else {
showEntityActionsDialog(
entities: [design],
context: context,
);
}
},
isChecked: isInMultiselect && listUIState.isSelected(design.id),
);
});
} else {
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(12),
child: PaginatedDataTable(
onSelectAll: (value) {
final designs = viewModel.designList
.map<DesignEntity>(
(designId) => viewModel.designMap[designId])
.where((design) => value != listUIState.isSelected(design.id))
.toList();
handleDesignAction(
context, designs, EntityAction.toggleMultiselect);
},
columns: [
if (!listUIState.isInMultiselect()) DataColumn(label: SizedBox()),
...viewModel.tableColumns.map((field) => DataColumn(
label: Text(AppLocalization.of(context).lookup(field)),
numeric: EntityPresenter.isFieldNumeric(field),
onSort: (int columnIndex, bool ascending) =>
store.dispatch(SortDesigns(field)))),
],
source: dataTableSource,
header: DatatableHeader(
entityType: EntityType.design,
onClearPressed: viewModel.onClearEntityFilterPressed,
),
),
));
}
};
return RefreshIndicator(
onRefresh: () => viewModel.onRefreshed(context),
child: listOrTable(),
);
}
}

View File

@ -0,0 +1,104 @@
import 'package:flutter_redux/flutter_redux.dart';
import 'package:invoiceninja_flutter/data/models/design_model.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
import 'package:invoiceninja_flutter/ui/app/entity_state_label.dart';
import 'package:invoiceninja_flutter/utils/formatting.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/ui/app/dismissible_entity.dart';
class DesignListItem extends StatelessWidget {
const DesignListItem({
@required this.user,
@required this.onEntityAction,
@required this.onTap,
@required this.onLongPress,
@required this.design,
@required this.filter,
this.onCheckboxChanged,
this.isChecked = false,
});
final UserEntity user;
final Function(EntityAction) onEntityAction;
final GestureTapCallback onTap;
final GestureTapCallback onLongPress;
final DesignEntity design;
final String filter;
final Function(bool) onCheckboxChanged;
final bool isChecked;
static final designItemKey = (int id) => Key('__design_item_${id}__');
@override
Widget build(BuildContext context) {
final store = StoreProvider.of<AppState>(context);
final state = store.state;
final uiState = state.uiState;
final designUIState = uiState.designUIState;
final listUIState = designUIState.listUIState;
final isInMultiselect = listUIState.isInMultiselect();
final showCheckbox = onCheckboxChanged != null || isInMultiselect;
final filterMatch = filter != null && filter.isNotEmpty
? design.matchesFilterValue(filter)
: null;
final subtitle = filterMatch;
return DismissibleEntity(
userCompany: state.userCompany,
entity: design,
isSelected: design.id ==
(uiState.isEditing
? designUIState.editing.id
: designUIState.selectedId),
onEntityAction: onEntityAction,
child: ListTile(
onTap: isInMultiselect
? () => onEntityAction(EntityAction.toggleMultiselect)
: onTap,
onLongPress: onLongPress,
leading: showCheckbox
? IgnorePointer(
ignoring: listUIState.isInMultiselect(),
child: Checkbox(
value: isChecked,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
onChanged: (value) => onCheckboxChanged(value),
activeColor: Theme.of(context).accentColor,
),
)
: null,
title: Container(
width: MediaQuery.of(context).size.width,
child: Row(
children: <Widget>[
Expanded(
child: Text(
design.name,
style: Theme.of(context).textTheme.headline6,
),
),
Text(formatNumber(design.listDisplayAmount, context),
style: Theme.of(context).textTheme.headline6),
],
),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
subtitle != null && subtitle.isNotEmpty
? Text(
subtitle,
maxLines: 3,
overflow: TextOverflow.ellipsis,
)
: Container(),
EntityStateLabel(design),
],
),
),
);
}
}

View File

@ -0,0 +1,110 @@
import 'dart:async';
import 'package:invoiceninja_flutter/data/models/design_model.dart';
import 'package:redux/redux.dart';
import 'package:invoiceninja_flutter/redux/app/app_actions.dart';
import 'package:flutter/material.dart';
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/design/design_selectors.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/ui/design/design_list.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
import 'package:invoiceninja_flutter/redux/design/design_actions.dart';
class DesignListBuilder extends StatelessWidget {
const DesignListBuilder({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return StoreConnector<AppState, DesignListVM>(
converter: DesignListVM.fromStore,
builder: (context, viewModel) {
return DesignList(
viewModel: viewModel,
);
},
);
}
}
class DesignListVM {
DesignListVM({
@required this.state,
@required this.userCompany,
@required this.designList,
@required this.designMap,
@required this.filter,
@required this.isLoading,
@required this.isLoaded,
@required this.onDesignTap,
@required this.listState,
@required this.onRefreshed,
@required this.onEntityAction,
@required this.tableColumns,
@required this.onClearEntityFilterPressed,
@required this.onViewEntityFilterPressed,
});
static DesignListVM fromStore(Store<AppState> store) {
Future<Null> _handleRefresh(BuildContext context) {
if (store.state.isLoading) {
return Future<Null>(null);
}
final completer = snackBarCompleter<Null>(
context, AppLocalization.of(context).refreshComplete);
store.dispatch(LoadDesigns(completer: completer, force: true));
return completer.future;
}
final state = store.state;
return DesignListVM(
state: state,
userCompany: state.userCompany,
listState: state.designListState,
designList: memoizedFilteredDesignList(
state.designState.map, state.designState.list, state.designListState),
designMap: state.designState.map,
isLoading: state.isLoading,
isLoaded: state.designState.isLoaded,
filter: state.designUIState.listUIState.filter,
onClearEntityFilterPressed: () => store.dispatch(FilterDesignsByEntity()),
onViewEntityFilterPressed: (BuildContext context) => viewEntityById(
context: context,
entityId: state.designListState.filterEntityId,
entityType: state.designListState.filterEntityType),
onDesignTap: (context, design) {
if (store.state.designListState.isInMultiselect()) {
handleDesignAction(context, [design], EntityAction.toggleMultiselect);
} else {
viewEntity(context: context, entity: design);
}
},
onEntityAction: (BuildContext context, List<BaseEntity> designs,
EntityAction action) =>
handleDesignAction(context, designs, action),
onRefreshed: (context) => _handleRefresh(context),
);
}
final AppState state;
final UserCompanyEntity userCompany;
final List<String> designList;
final BuiltMap<String, DesignEntity> designMap;
final ListUIState listState;
final String filter;
final bool isLoading;
final bool isLoaded;
final Function(BuildContext, DesignEntity) onDesignTap;
final Function(BuildContext) onRefreshed;
final Function(BuildContext, List<BaseEntity>, EntityAction) onEntityAction;
final Function onClearEntityFilterPressed;
final Function(BuildContext) onViewEntityFilterPressed;
final List<String> tableColumns;
}

View File

@ -0,0 +1,24 @@
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/data/models/design_model.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
import 'package:invoiceninja_flutter/ui/app/presenters/entity_presenter.dart';
import 'package:invoiceninja_flutter/utils/formatting.dart';
class DesignPresenter extends EntityPresenter {
static List<String> getTableFields(UserCompanyEntity userCompany) {
return [];
}
@override
Widget getField({String field, BuildContext context}) {
final state = StoreProvider.of<AppState>(context).state;
final design = entity as InvoiceEntity;
switch (field) {
}
return super.getField(field: field, context: context);
}
}

View File

@ -0,0 +1,148 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/redux/app/app_actions.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
import 'package:invoiceninja_flutter/redux/design/design_actions.dart';
import 'package:invoiceninja_flutter/ui/app/app_bottom_bar.dart';
import 'package:invoiceninja_flutter/ui/app/forms/save_cancel_buttons.dart';
import 'package:invoiceninja_flutter/ui/app/list_scaffold.dart';
import 'package:invoiceninja_flutter/ui/app/entities/entity_actions_dialog.dart';
import 'package:invoiceninja_flutter/ui/app/list_filter.dart';
import 'package:invoiceninja_flutter/ui/app/list_filter_button.dart';
import 'package:invoiceninja_flutter/ui/design/design_list_vm.dart';
import 'package:invoiceninja_flutter/utils/localization.dart';
import 'design_screen_vm.dart';
class DesignScreen extends StatelessWidget {
const DesignScreen({
Key key,
@required this.viewModel,
}) : super(key: key);
static const String route = '/design';
final DesignScreenVM viewModel;
@override
Widget build(BuildContext context) {
final store = StoreProvider.of<AppState>(context);
final state = store.state;
final company = state.company;
final userCompany = state.userCompany;
final localization = AppLocalization.of(context);
final listUIState = state.uiState.designUIState.listUIState;
final isInMultiselect = listUIState.isInMultiselect();
return ListScaffold(
isChecked: isInMultiselect &&
listUIState.selectedIds.length == viewModel.designList.length,
showCheckbox: isInMultiselect,
onHamburgerLongPress: () => store.dispatch(StartDesignMultiselect()),
onCheckboxChanged: (value) {
final designs = viewModel.designList
.map<DesignEntity>((designId) => viewModel.designMap[designId])
.where((design) => value != listUIState.isSelected(design.id))
.toList();
handleDesignAction(context, designs, EntityAction.toggleMultiselect);
},
appBarTitle: ListFilter(
title: localization.designs,
key: ValueKey(state.designListState.filterClearedAt),
filter: state.designListState.filter,
onFilterChanged: (value) {
store.dispatch(FilterDesigns(value));
},
),
appBarActions: [
if (!viewModel.isInMultiselect)
ListFilterButton(
filter: state.designListState.filter,
onFilterPressed: (String value) {
store.dispatch(FilterDesigns(value));
},
),
if (viewModel.isInMultiselect)
SaveCancelButtons(
saveLabel: localization.done,
onSavePressed: listUIState.selectedIds.isEmpty
? null
: (context) async {
final designs = listUIState.selectedIds
.map<DesignEntity>(
(designId) => viewModel.designMap[designId])
.toList();
await showEntityActionsDialog(
entities: designs,
context: context,
multiselect: true,
completer: Completer<Null>()
..future.then<dynamic>(
(_) => store.dispatch(ClearDesignMultiselect())),
);
},
onCancelPressed: (context) =>
store.dispatch(ClearDesignMultiselect()),
),
],
body: DesignListBuilder(),
bottomNavigationBar: AppBottomBar(
entityType: EntityType.design,
onSelectedSortField: (value) {
store.dispatch(SortDesigns(value));
},
sortFields: [
DesignFields.name,
DesignFields.balance,
DesignFields.updatedAt,
],
onSelectedState: (EntityState state, value) {
store.dispatch(FilterDesignsByState(state));
},
onCheckboxPressed: () {
if (store.state.designListState.isInMultiselect()) {
store.dispatch(ClearDesignMultiselect());
} else {
store.dispatch(StartDesignMultiselect());
}
},
customValues1: company.getCustomFieldValues(CustomFieldType.design1,
excludeBlank: true),
customValues2: company.getCustomFieldValues(CustomFieldType.design2,
excludeBlank: true),
customValues3: company.getCustomFieldValues(CustomFieldType.design3,
excludeBlank: true),
customValues4: company.getCustomFieldValues(CustomFieldType.design4,
excludeBlank: true),
onSelectedCustom1: (value) =>
store.dispatch(FilterDesignsByCustom1(value)),
onSelectedCustom2: (value) =>
store.dispatch(FilterDesignsByCustom2(value)),
onSelectedCustom3: (value) =>
store.dispatch(FilterDesignsByCustom3(value)),
onSelectedCustom4: (value) =>
store.dispatch(FilterDesignsByCustom4(value)),
),
floatingActionButton: userCompany.canCreate(EntityType.design)
? FloatingActionButton(
heroTag: 'design_fab',
backgroundColor: Theme.of(context).primaryColorDark,
onPressed: () {
createEntityByType(
context: context, entityType: EntityType.design);
},
child: Icon(
Icons.add,
color: Colors.white,
),
tooltip: localization.newDesign,
)
: null,
);
}
}

View File

@ -0,0 +1,59 @@
import 'package:built_collection/built_collection.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
import 'package:invoiceninja_flutter/redux/design/design_actions.dart';
import 'package:invoiceninja_flutter/redux/design/design_selectors.dart';
import 'package:redux/redux.dart';
import 'design_screen.dart';
class DesignScreenBuilder extends StatelessWidget {
const DesignScreenBuilder({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return StoreConnector<AppState, DesignScreenVM>(
converter: DesignScreenVM.fromStore,
builder: (context, vm) {
return DesignScreen(
viewModel: vm,
);
},
);
}
}
class DesignScreenVM {
DesignScreenVM({
@required this.isInMultiselect,
@required this.designList,
@required this.userCompany,
@required this.onEntityAction,
@required this.designMap,
});
final bool isInMultiselect;
final UserCompanyEntity userCompany;
final List<String> designList;
final Function(BuildContext, List<BaseEntity>, EntityAction) onEntityAction;
final BuiltMap<String, DesignEntity> designMap;
static DesignScreenVM fromStore(Store<AppState> store) {
final state = store.state;
return DesignScreenVM(
designMap: state.designState.map,
designList: memoizedFilteredDesignList(
state.designState.map, state.designState.list, state.designListState),
userCompany: state.userCompany,
isInMultiselect: state.designListState.isInMultiselect(),
onEntityAction: (BuildContext context, List<BaseEntity> designs,
EntityAction action) =>
handleDesignAction(context, designs, action),
);
}
}

View File

@ -0,0 +1,102 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:invoiceninja_flutter/ui/app/form_card.dart';
import 'package:invoiceninja_flutter/ui/design/edit/design_edit_vm.dart';
import 'package:invoiceninja_flutter/utils/localization.dart';
import 'package:invoiceninja_flutter/utils/completers.dart';
class DesignEdit extends StatefulWidget {
const DesignEdit({
Key key,
@required this.viewModel,
}) : super(key: key);
final DesignEditVM viewModel;
@override
_DesignEditState createState() => _DesignEditState();
}
class _DesignEditState extends State<DesignEdit> {
static final GlobalKey<FormState> _formKey =
GlobalKey<FormState>(debugLabel: '_designEdit');
final _debouncer = Debouncer();
// STARTER: controllers - do not remove comment
List<TextEditingController> _controllers = [];
@override
void didChangeDependencies() {
_controllers = [
// STARTER: array - do not remove comment
];
_controllers.forEach((controller) => controller.removeListener(_onChanged));
final design = widget.viewModel.design;
// STARTER: read value - do not remove comment
_controllers.forEach((controller) => controller.addListener(_onChanged));
super.didChangeDependencies();
}
@override
void dispose() {
_controllers.forEach((controller) {
controller.removeListener(_onChanged);
controller.dispose();
});
super.dispose();
}
void _onChanged() {
_debouncer.run(() {
final design = widget.viewModel.design.rebuild((b) => b
// STARTER: set value - do not remove comment
);
if (design != widget.viewModel.design) {
widget.viewModel.onChanged(design);
}
});
}
@override
Widget build(BuildContext context) {
final viewModel = widget.viewModel;
final localization = AppLocalization.of(context);
final design = viewModel.design;
return EditScaffold(
onCancelPressed: (context) => viewModel.onCancelPressed(context),
onSavePressed: (context) {
final bool isValid = _formKey.currentState.validate();
setState(() {
_autoValidate = !isValid;
});
if (!isValid) {
return;
}
viewModel.onSavePressed(context);
},
body: Form(
key: _formKey,
child: Builder(builder: (BuildContext context) {
return ListView(
children: <Widget>[
FormCard(
children: <Widget>[
// STARTER: widgets - do not remove comment
],
),
],
);
})),
);
}
}

View File

@ -0,0 +1,105 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart';
import 'package:invoiceninja_flutter/ui/design/design_screen.dart';
import 'package:invoiceninja_flutter/utils/platforms.dart';
import 'package:redux/redux.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/ui/app/dialogs/error_dialog.dart';
import 'package:invoiceninja_flutter/ui/design/view/design_view_vm.dart';
import 'package:invoiceninja_flutter/redux/design/design_actions.dart';
import 'package:invoiceninja_flutter/data/models/design_model.dart';
import 'package:invoiceninja_flutter/ui/design/edit/design_edit.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
class DesignEditScreen extends StatelessWidget {
const DesignEditScreen({Key key}) : super(key: key);
static const String route = '/design/edit';
@override
Widget build(BuildContext context) {
return StoreConnector<AppState, DesignEditVM>(
converter: (Store<AppState> store) {
return DesignEditVM.fromStore(store);
},
builder: (context, viewModel) {
return DesignEdit(
viewModel: viewModel,
key: ValueKey(viewModel.design.id),
);
},
);
}
}
class DesignEditVM {
DesignEditVM({
@required this.state,
@required this.design,
@required this.company,
@required this.onChanged,
@required this.isSaving,
@required this.origDesign,
@required this.onSavePressed,
@required this.onCancelPressed,
@required this.isLoading,
});
factory DesignEditVM.fromStore(Store<AppState> store) {
final state = store.state;
final design = state.designUIState.editing;
return DesignEditVM(
state: state,
isLoading: state.isLoading,
isSaving: state.isSaving,
origDesign: state.designState.map[design.id],
design: design,
company: state.selectedCompany,
onChanged: (DesignEntity design) {
store.dispatch(UpdateDesign(design));
},
onCancelPressed: (BuildContext context) {
store.dispatch(
EditDesign(design: DesignEntity(), context: context, force: true));
store.dispatch(UpdateCurrentRoute(state.uiState.previousRoute));
},
onSavePressed: (BuildContext context) {
final Completer<DesignEntity> completer = new Completer<DesignEntity>();
store.dispatch(SaveDesignRequest(completer: completer, design: design));
return completer.future.then((savedDesign) {
if (isMobile(context)) {
store.dispatch(UpdateCurrentRoute(DesignViewScreen.route));
if (design.isNew) {
Navigator.of(context)
.pushReplacementNamed(DesignViewScreen.route);
} else {
Navigator.of(context).pop(savedDesign);
}
} else {
store.dispatch(ViewDesign(
context: context, designId: savedDesign.id, force: true));
}
}).catchError((Object error) {
showDialog<ErrorDialog>(
context: context,
builder: (BuildContext context) {
return ErrorDialog(error);
});
});
},
);
}
final DesignEntity design;
final CompanyEntity company;
final Function(DesignEntity) onChanged;
final Function(BuildContext) onSavePressed;
final Function(BuildContext) onCancelPressed;
final bool isLoading;
final bool isSaving;
final DesignEntity origDesign;
final AppState state;
}

View File

@ -0,0 +1,51 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:invoiceninja_flutter/ui/app/buttons/edit_icon_button.dart';
import 'package:invoiceninja_flutter/ui/app/actions_menu_button.dart';
import 'package:invoiceninja_flutter/ui/design/view/design_view_vm.dart';
import 'package:invoiceninja_flutter/ui/app/form_card.dart';
import 'package:invoiceninja_flutter/ui/app/entities/entity_state_title.dart';
class DesignView extends StatefulWidget {
const DesignView({
Key key,
@required this.viewModel,
}) : super(key: key);
final DesignViewVM viewModel;
@override
_DesignViewState createState() => new _DesignViewState();
}
class _DesignViewState extends State<DesignView> {
@override
Widget build(BuildContext context) {
final viewModel = widget.viewModel;
final userCompany = viewModel.state.userCompany;
final design = viewModel.design;
return Scaffold(
appBar: AppBar(
title: EntityStateTitle(entity: design),
actions: [
userCompany.canEditEntity(design)
? EditIconButton(
isVisible: !design.isDeleted,
onPressed: () => viewModel.onEditPressed(context),
)
: Container(),
ActionMenuButton(
entityActions: design.getActions(userCompany: userCompany),
isSaving: viewModel.isSaving,
entity: design,
onSelected: viewModel.onEntityAction,
)
],
),
body: FormCard(children: [
// STARTER: widgets - do not remove comment
]),
);
}
}

View File

@ -0,0 +1,81 @@
import 'dart:async';
import 'package:invoiceninja_flutter/ui/app/snackbar_row.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart';
import 'package:invoiceninja_flutter/utils/completers.dart';
import 'package:invoiceninja_flutter/ui/design/design_screen.dart';
import 'package:invoiceninja_flutter/utils/localization.dart';
import 'package:redux/redux.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:invoiceninja_flutter/redux/design/design_actions.dart';
import 'package:invoiceninja_flutter/data/models/design_model.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/ui/design/view/design_view.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
class DesignViewScreen extends StatelessWidget {
const DesignViewScreen({Key key}) : super(key: key);
static const String route = '/design/view';
@override
Widget build(BuildContext context) {
return StoreConnector<AppState, DesignViewVM>(
converter: (Store<AppState> store) {
return DesignViewVM.fromStore(store);
},
builder: (context, vm) {
return DesignView(
viewModel: vm,
);
},
);
}
}
class DesignViewVM {
DesignViewVM({
@required this.state,
@required this.design,
@required this.company,
@required this.onEntityAction,
@required this.onRefreshed,
@required this.isSaving,
@required this.isLoading,
@required this.isDirty,
});
factory DesignViewVM.fromStore(Store<AppState> store) {
final state = store.state;
final design = state.designState.map[state.designUIState.selectedId] ??
DesignEntity(id: state.designUIState.selectedId);
Future<Null> _handleRefresh(BuildContext context) {
final completer = snackBarCompleter<Null>(
context, AppLocalization.of(context).refreshComplete);
store.dispatch(LoadDesign(completer: completer, designId: design.id));
return completer.future;
}
return DesignViewVM(
state: state,
company: state.selectedCompany,
isSaving: state.isSaving,
isLoading: state.isLoading,
isDirty: design.isNew,
design: design,
onRefreshed: (context) => _handleRefresh(context),
onEntityAction: (BuildContext context, EntityAction action) =>
handleDesignAction(context, design, action),
);
}
final AppState state;
final DesignEntity design;
final CompanyEntity company;
final Function(BuildContext, EntityAction) onEntityAction;
final Function(BuildContext) onRefreshed;
final bool isSaving;
final bool isLoading;
final bool isDirty;
}

View File

@ -28,7 +28,7 @@ import 'package:memoize/memoize.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:redux/redux.dart'; import 'package:redux/redux.dart';
import 'package:invoiceninja_flutter/utils/web_stub.dart' import 'package:invoiceninja_flutter/utils/web_stub.dart'
if (dart.library.html) 'package:invoiceninja_flutter/utils/web.dart'; if (dart.library.html) 'package:invoiceninja_flutter/utils/web.dart';
class ReportsScreenBuilder extends StatelessWidget { class ReportsScreenBuilder extends StatelessWidget {
const ReportsScreenBuilder({Key key}) : super(key: key); const ReportsScreenBuilder({Key key}) : super(key: key);

View File

@ -15,6 +15,17 @@ mixin LocalizationsProvider on LocaleCodeAware {
static final Map<String, Map<String, String>> _localizedValues = { static final Map<String, Map<String, String>> _localizedValues = {
'en': { 'en': {
// STARTER: lang key - do not remove comment // STARTER: lang key - do not remove comment
'design': 'Design',
'designs': 'Designs',
'new_design': 'New Design',
'edit_design': 'Edit Design',
'created_design': 'Successfully created design',
'updated_design': 'Successfully updated design',
'archived_design': 'Successfully archived design',
'deleted_design': 'Successfully deleted design',
'removed_design': 'Successfully removed design',
'restored_design': 'Successfully restored design',
'proposals': 'Proposals', 'proposals': 'Proposals',
'tickets': 'Tickets', 'tickets': 'Tickets',
'recurring_invoices': 'Recurring Invoices', 'recurring_invoices': 'Recurring Invoices',
@ -32780,6 +32791,16 @@ mixin LocalizationsProvider on LocaleCodeAware {
String get appUpdated => _localizedValues[localeCode]['app_updated']; String get appUpdated => _localizedValues[localeCode]['app_updated'];
// STARTER: lang field - do not remove comment // STARTER: lang field - do not remove comment
String get design => _localizedValues[localeCode][' design'];
String get designs => _localizedValues[localeCode]['designs'];
String get newDesign => _localizedValues[localeCode]['new_design'];
String get createdDesign => _localizedValues[localeCode]['created_design'];
String get updatedDesign => _localizedValues[localeCode]['updated_design'];
String get archivedDesign => _localizedValues[localeCode]['archived_design'];
String get deletedDesign => _localizedValues[localeCode]['deleted_design'];
String get restoredDesign => _localizedValues[localeCode]['restored_design'];
String get editDesign => _localizedValues[localeCode]['edit_design'];
String get newCredit => _localizedValues[localeCode]['new_credit']; String get newCredit => _localizedValues[localeCode]['new_credit'];
String get createdCredit => _localizedValues[localeCode]['created_credit']; String get createdCredit => _localizedValues[localeCode]['created_credit'];

View File

@ -438,6 +438,10 @@ else
code="String get ${module_camel} => _localizedValues[localeCode][' ${module_snake}']; String get ${module_camel}s => _localizedValues[localeCode]['${module_snake}s']; String get new${Module} => _localizedValues[localeCode]['new_${module_snake}']; String get created${Module} => _localizedValues[localeCode]['created_${module_snake}']; String get updated${Module} => _localizedValues[localeCode]['updated_${module_snake}']; String get archived${Module} => _localizedValues[localeCode]['archived_${module_snake}']; String get deleted${Module} => _localizedValues[localeCode]['deleted_${module_snake}']; String get restored${Module} => _localizedValues[localeCode]['restored_${module_snake}']; String get edit${Module} => _localizedValues[localeCode]['edit_${module_snake}'];${lineBreak}" code="String get ${module_camel} => _localizedValues[localeCode][' ${module_snake}']; String get ${module_camel}s => _localizedValues[localeCode]['${module_snake}s']; String get new${Module} => _localizedValues[localeCode]['new_${module_snake}']; String get created${Module} => _localizedValues[localeCode]['created_${module_snake}']; String get updated${Module} => _localizedValues[localeCode]['updated_${module_snake}']; String get archived${Module} => _localizedValues[localeCode]['archived_${module_snake}']; String get deleted${Module} => _localizedValues[localeCode]['deleted_${module_snake}']; String get restored${Module} => _localizedValues[localeCode]['restored_${module_snake}']; String get edit${Module} => _localizedValues[localeCode]['edit_${module_snake}'];${lineBreak}"
sed -i -e "s/$comment/$comment${lineBreak}$code/g" ./lib/utils/i18n.dart sed -i -e "s/$comment/$comment${lineBreak}$code/g" ./lib/utils/i18n.dart
comment="STARTER: entity type - do not remove comment"
code="static const EntityType ${module_camel} = _\$${module_camel}${lineBreak}"
sed -i -e "s/$comment/$comment${lineBreak}$code/g" ./lib/data/models/entities.dart
echo "Generating built files.." echo "Generating built files.."
flutter packages pub run build_runner clean flutter packages pub run build_runner clean
flutter packages pub run build_runner build --delete-conflicting-outputs flutter packages pub run build_runner build --delete-conflicting-outputs

View File

@ -2,10 +2,10 @@ import 'package:built_value/built_value.dart';
import 'package:built_collection/built_collection.dart'; import 'package:built_collection/built_collection.dart';
import 'package:built_value/serializer.dart'; import 'package:built_value/serializer.dart';
import 'package:invoiceninja_flutter/data/models/models.dart'; import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/utils/formatting.dart';
part 'stub_model.g.dart'; part 'stub_model.g.dart';
abstract class StubListResponse abstract class StubListResponse
implements Built<StubListResponse, StubListResponseBuilder> { implements Built<StubListResponse, StubListResponseBuilder> {
factory StubListResponse([void updates(StubListResponseBuilder b)]) = factory StubListResponse([void updates(StubListResponseBuilder b)]) =
@ -48,6 +48,8 @@ abstract class StubEntity extends Object with BaseEntity implements Built<StubEn
); );
} }
StubEntity._();
String get displayName { String get displayName {
// STARTER: display name - do not remove comment // STARTER: display name - do not remove comment
} }
@ -80,6 +82,36 @@ abstract class StubEntity extends Object with BaseEntity implements Built<StubEn
return false; return false;
} }
StubEntity._(); @override
bool matchesFilter(String filter) {
if (filter == null || filter.isEmpty) {
return true;
}
filter = filter.toLowerCase();
return false;
}
@override
String matchesFilterValue(String filter) {
if (filter == null || filter.isEmpty) {
return null;
}
filter = filter.toLowerCase();
return null;
}
@override
String get listDisplayName => null;
@override
double get listDisplayAmount => null;
@override
FormatNumberType get listDisplayAmountType => null;
static Serializer<StubEntity> get serializer => _$stubEntitySerializer; static Serializer<StubEntity> get serializer => _$stubEntitySerializer;
} }