Fixed pagination tenders for desktop and

added new config for notification
This commit is contained in:
llsajjad
2025-09-21 14:03:32 +03:30
parent 8a289f2d05
commit 6e6f248702
15 changed files with 422 additions and 219 deletions
+1 -1
View File
@@ -25,7 +25,7 @@ android {
applicationId = "com.opplens.app" applicationId = "com.opplens.app"
// You can update the following values to match your application needs. // You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config. // For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion minSdk = 23
targetSdk = flutter.targetSdkVersion targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode versionCode = flutter.versionCode
versionName = flutter.versionName versionName = flutter.versionName
+5 -5
View File
@@ -1,13 +1,13 @@
{ {
"project_info": { "project_info": {
"project_number": "1071558853812", "project_number": "6923326255",
"project_id": "opplens", "project_id": "opplens-270d1",
"storage_bucket": "opplens.firebasestorage.app" "storage_bucket": "opplens-270d1.firebasestorage.app"
}, },
"client": [ "client": [
{ {
"client_info": { "client_info": {
"mobilesdk_app_id": "1:1071558853812:android:7831119a56a99c5948c3e1", "mobilesdk_app_id": "1:6923326255:android:6704c5b71e4451abed87da",
"android_client_info": { "android_client_info": {
"package_name": "com.opplens.app" "package_name": "com.opplens.app"
} }
@@ -15,7 +15,7 @@
"oauth_client": [], "oauth_client": [],
"api_key": [ "api_key": [
{ {
"current_key": "AIzaSyD5LaGXppjGEdTf-a0Ha25FX1p0v_h-c8A" "current_key": "AIzaSyCqZ6B5iih0RdFj0lf5_erwbZvMyb7obEg"
} }
], ],
"services": { "services": {
@@ -12,7 +12,6 @@ part 'tenders_data.g.dart';
abstract class TendersData with _$TendersData { abstract class TendersData with _$TendersData {
const factory TendersData({ const factory TendersData({
required List<TenderData>? tenders, required List<TenderData>? tenders,
required Meta? metadata,
}) = _TendersData; }) = _TendersData;
factory TendersData.fromJson(Map<String, Object?> json) => factory TendersData.fromJson(Map<String, Object?> json) =>
@@ -15,7 +15,7 @@ T _$identity<T>(T value) => value;
/// @nodoc /// @nodoc
mixin _$TendersData { mixin _$TendersData {
List<TenderData>? get tenders; Meta? get metadata; List<TenderData>? get tenders;
/// Create a copy of TendersData /// Create a copy of TendersData
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@@ -28,16 +28,16 @@ $TendersDataCopyWith<TendersData> get copyWith => _$TendersDataCopyWithImpl<Tend
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is TendersData&&const DeepCollectionEquality().equals(other.tenders, tenders)&&(identical(other.metadata, metadata) || other.metadata == metadata)); return identical(this, other) || (other.runtimeType == runtimeType&&other is TendersData&&const DeepCollectionEquality().equals(other.tenders, tenders));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(tenders),metadata); int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(tenders));
@override @override
String toString() { String toString() {
return 'TendersData(tenders: $tenders, metadata: $metadata)'; return 'TendersData(tenders: $tenders)';
} }
@@ -48,11 +48,11 @@ abstract mixin class $TendersDataCopyWith<$Res> {
factory $TendersDataCopyWith(TendersData value, $Res Function(TendersData) _then) = _$TendersDataCopyWithImpl; factory $TendersDataCopyWith(TendersData value, $Res Function(TendersData) _then) = _$TendersDataCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
List<TenderData>? tenders, Meta? metadata List<TenderData>? tenders
}); });
$MetaCopyWith<$Res>? get metadata;
} }
/// @nodoc /// @nodoc
@@ -65,26 +65,13 @@ class _$TendersDataCopyWithImpl<$Res>
/// Create a copy of TendersData /// Create a copy of TendersData
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? tenders = freezed,Object? metadata = freezed,}) { @pragma('vm:prefer-inline') @override $Res call({Object? tenders = freezed,}) {
return _then(_self.copyWith( return _then(_self.copyWith(
tenders: freezed == tenders ? _self.tenders : tenders // ignore: cast_nullable_to_non_nullable tenders: freezed == tenders ? _self.tenders : tenders // ignore: cast_nullable_to_non_nullable
as List<TenderData>?,metadata: freezed == metadata ? _self.metadata : metadata // ignore: cast_nullable_to_non_nullable as List<TenderData>?,
as Meta?,
)); ));
} }
/// Create a copy of TendersData
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$MetaCopyWith<$Res>? get metadata {
if (_self.metadata == null) {
return null;
}
return $MetaCopyWith<$Res>(_self.metadata!, (value) {
return _then(_self.copyWith(metadata: value));
});
}
} }
@@ -166,10 +153,10 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<TenderData>? tenders, Meta? metadata)? $default,{required TResult orElse(),}) {final _that = this; @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<TenderData>? tenders)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _TendersData() when $default != null: case _TendersData() when $default != null:
return $default(_that.tenders,_that.metadata);case _: return $default(_that.tenders);case _:
return orElse(); return orElse();
} }
@@ -187,10 +174,10 @@ return $default(_that.tenders,_that.metadata);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<TenderData>? tenders, Meta? metadata) $default,) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<TenderData>? tenders) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _TendersData(): case _TendersData():
return $default(_that.tenders,_that.metadata);case _: return $default(_that.tenders);case _:
throw StateError('Unexpected subclass'); throw StateError('Unexpected subclass');
} }
@@ -207,10 +194,10 @@ return $default(_that.tenders,_that.metadata);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<TenderData>? tenders, Meta? metadata)? $default,) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<TenderData>? tenders)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _TendersData() when $default != null: case _TendersData() when $default != null:
return $default(_that.tenders,_that.metadata);case _: return $default(_that.tenders);case _:
return null; return null;
} }
@@ -222,7 +209,7 @@ return $default(_that.tenders,_that.metadata);case _:
@JsonSerializable() @JsonSerializable()
class _TendersData implements TendersData { class _TendersData implements TendersData {
const _TendersData({required final List<TenderData>? tenders, required this.metadata}): _tenders = tenders; const _TendersData({required final List<TenderData>? tenders}): _tenders = tenders;
factory _TendersData.fromJson(Map<String, dynamic> json) => _$TendersDataFromJson(json); factory _TendersData.fromJson(Map<String, dynamic> json) => _$TendersDataFromJson(json);
final List<TenderData>? _tenders; final List<TenderData>? _tenders;
@@ -234,7 +221,6 @@ class _TendersData implements TendersData {
return EqualUnmodifiableListView(value); return EqualUnmodifiableListView(value);
} }
@override final Meta? metadata;
/// Create a copy of TendersData /// Create a copy of TendersData
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@@ -249,16 +235,16 @@ Map<String, dynamic> toJson() {
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _TendersData&&const DeepCollectionEquality().equals(other._tenders, _tenders)&&(identical(other.metadata, metadata) || other.metadata == metadata)); return identical(this, other) || (other.runtimeType == runtimeType&&other is _TendersData&&const DeepCollectionEquality().equals(other._tenders, _tenders));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_tenders),metadata); int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_tenders));
@override @override
String toString() { String toString() {
return 'TendersData(tenders: $tenders, metadata: $metadata)'; return 'TendersData(tenders: $tenders)';
} }
@@ -269,11 +255,11 @@ abstract mixin class _$TendersDataCopyWith<$Res> implements $TendersDataCopyWith
factory _$TendersDataCopyWith(_TendersData value, $Res Function(_TendersData) _then) = __$TendersDataCopyWithImpl; factory _$TendersDataCopyWith(_TendersData value, $Res Function(_TendersData) _then) = __$TendersDataCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
List<TenderData>? tenders, Meta? metadata List<TenderData>? tenders
}); });
@override $MetaCopyWith<$Res>? get metadata;
} }
/// @nodoc /// @nodoc
@@ -286,27 +272,14 @@ class __$TendersDataCopyWithImpl<$Res>
/// Create a copy of TendersData /// Create a copy of TendersData
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? tenders = freezed,Object? metadata = freezed,}) { @override @pragma('vm:prefer-inline') $Res call({Object? tenders = freezed,}) {
return _then(_TendersData( return _then(_TendersData(
tenders: freezed == tenders ? _self._tenders : tenders // ignore: cast_nullable_to_non_nullable tenders: freezed == tenders ? _self._tenders : tenders // ignore: cast_nullable_to_non_nullable
as List<TenderData>?,metadata: freezed == metadata ? _self.metadata : metadata // ignore: cast_nullable_to_non_nullable as List<TenderData>?,
as Meta?,
)); ));
} }
/// Create a copy of TendersData
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$MetaCopyWith<$Res>? get metadata {
if (_self.metadata == null) {
return null;
}
return $MetaCopyWith<$Res>(_self.metadata!, (value) {
return _then(_self.copyWith(metadata: value));
});
}
} }
// dart format on // dart format on
@@ -11,14 +11,7 @@ _TendersData _$TendersDataFromJson(Map<String, dynamic> json) => _TendersData(
(json['tenders'] as List<dynamic>?) (json['tenders'] as List<dynamic>?)
?.map((e) => TenderData.fromJson(e as Map<String, dynamic>)) ?.map((e) => TenderData.fromJson(e as Map<String, dynamic>))
.toList(), .toList(),
metadata:
json['metadata'] == null
? null
: Meta.fromJson(json['metadata'] as Map<String, dynamic>),
); );
Map<String, dynamic> _$TendersDataToJson(_TendersData instance) => Map<String, dynamic> _$TendersDataToJson(_TendersData instance) =>
<String, dynamic>{ <String, dynamic>{'tenders': instance.tenders};
'tenders': instance.tenders,
'metadata': instance.metadata,
};
@@ -1,6 +1,7 @@
// ignore_for_file: invalid_annotation_target // ignore_for_file: invalid_annotation_target
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:tm_app/data/services/model/meta/meta.dart';
import '../../error/error_model.dart'; import '../../error/error_model.dart';
import '../tenders_data/tenders_data.dart'; import '../tenders_data/tenders_data.dart';
@@ -15,6 +16,7 @@ abstract class TendersResponse with _$TendersResponse {
required ErrorModel? error, required ErrorModel? error,
required String? message, required String? message,
required bool? success, required bool? success,
required Meta? meta,
}) = _TendersResponse; }) = _TendersResponse;
factory TendersResponse.fromJson(Map<String, Object?> json) => factory TendersResponse.fromJson(Map<String, Object?> json) =>
@@ -15,7 +15,7 @@ T _$identity<T>(T value) => value;
/// @nodoc /// @nodoc
mixin _$TendersResponse { mixin _$TendersResponse {
TendersData? get data; ErrorModel? get error; String? get message; bool? get success; TendersData? get data; ErrorModel? get error; String? get message; bool? get success; Meta? get meta;
/// Create a copy of TendersResponse /// Create a copy of TendersResponse
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@@ -28,16 +28,16 @@ $TendersResponseCopyWith<TendersResponse> get copyWith => _$TendersResponseCopyW
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is TendersResponse&&(identical(other.data, data) || other.data == data)&&(identical(other.error, error) || other.error == error)&&(identical(other.message, message) || other.message == message)&&(identical(other.success, success) || other.success == success)); return identical(this, other) || (other.runtimeType == runtimeType&&other is TendersResponse&&(identical(other.data, data) || other.data == data)&&(identical(other.error, error) || other.error == error)&&(identical(other.message, message) || other.message == message)&&(identical(other.success, success) || other.success == success)&&(identical(other.meta, meta) || other.meta == meta));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType,data,error,message,success); int get hashCode => Object.hash(runtimeType,data,error,message,success,meta);
@override @override
String toString() { String toString() {
return 'TendersResponse(data: $data, error: $error, message: $message, success: $success)'; return 'TendersResponse(data: $data, error: $error, message: $message, success: $success, meta: $meta)';
} }
@@ -48,11 +48,11 @@ abstract mixin class $TendersResponseCopyWith<$Res> {
factory $TendersResponseCopyWith(TendersResponse value, $Res Function(TendersResponse) _then) = _$TendersResponseCopyWithImpl; factory $TendersResponseCopyWith(TendersResponse value, $Res Function(TendersResponse) _then) = _$TendersResponseCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
TendersData? data, ErrorModel? error, String? message, bool? success TendersData? data, ErrorModel? error, String? message, bool? success, Meta? meta
}); });
$TendersDataCopyWith<$Res>? get data;$ErrorModelCopyWith<$Res>? get error; $TendersDataCopyWith<$Res>? get data;$ErrorModelCopyWith<$Res>? get error;$MetaCopyWith<$Res>? get meta;
} }
/// @nodoc /// @nodoc
@@ -65,13 +65,14 @@ class _$TendersResponseCopyWithImpl<$Res>
/// Create a copy of TendersResponse /// Create a copy of TendersResponse
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? data = freezed,Object? error = freezed,Object? message = freezed,Object? success = freezed,}) { @pragma('vm:prefer-inline') @override $Res call({Object? data = freezed,Object? error = freezed,Object? message = freezed,Object? success = freezed,Object? meta = freezed,}) {
return _then(_self.copyWith( return _then(_self.copyWith(
data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable
as TendersData?,error: freezed == error ? _self.error : error // ignore: cast_nullable_to_non_nullable as TendersData?,error: freezed == error ? _self.error : error // ignore: cast_nullable_to_non_nullable
as ErrorModel?,message: freezed == message ? _self.message : message // ignore: cast_nullable_to_non_nullable as ErrorModel?,message: freezed == message ? _self.message : message // ignore: cast_nullable_to_non_nullable
as String?,success: freezed == success ? _self.success : success // ignore: cast_nullable_to_non_nullable as String?,success: freezed == success ? _self.success : success // ignore: cast_nullable_to_non_nullable
as bool?, as bool?,meta: freezed == meta ? _self.meta : meta // ignore: cast_nullable_to_non_nullable
as Meta?,
)); ));
} }
/// Create a copy of TendersResponse /// Create a copy of TendersResponse
@@ -98,6 +99,18 @@ $ErrorModelCopyWith<$Res>? get error {
return $ErrorModelCopyWith<$Res>(_self.error!, (value) { return $ErrorModelCopyWith<$Res>(_self.error!, (value) {
return _then(_self.copyWith(error: value)); return _then(_self.copyWith(error: value));
}); });
}/// Create a copy of TendersResponse
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$MetaCopyWith<$Res>? get meta {
if (_self.meta == null) {
return null;
}
return $MetaCopyWith<$Res>(_self.meta!, (value) {
return _then(_self.copyWith(meta: value));
});
} }
} }
@@ -180,10 +193,10 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( TendersData? data, ErrorModel? error, String? message, bool? success)? $default,{required TResult orElse(),}) {final _that = this; @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( TendersData? data, ErrorModel? error, String? message, bool? success, Meta? meta)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _TendersResponse() when $default != null: case _TendersResponse() when $default != null:
return $default(_that.data,_that.error,_that.message,_that.success);case _: return $default(_that.data,_that.error,_that.message,_that.success,_that.meta);case _:
return orElse(); return orElse();
} }
@@ -201,10 +214,10 @@ return $default(_that.data,_that.error,_that.message,_that.success);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( TendersData? data, ErrorModel? error, String? message, bool? success) $default,) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( TendersData? data, ErrorModel? error, String? message, bool? success, Meta? meta) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _TendersResponse(): case _TendersResponse():
return $default(_that.data,_that.error,_that.message,_that.success);case _: return $default(_that.data,_that.error,_that.message,_that.success,_that.meta);case _:
throw StateError('Unexpected subclass'); throw StateError('Unexpected subclass');
} }
@@ -221,10 +234,10 @@ return $default(_that.data,_that.error,_that.message,_that.success);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( TendersData? data, ErrorModel? error, String? message, bool? success)? $default,) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( TendersData? data, ErrorModel? error, String? message, bool? success, Meta? meta)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _TendersResponse() when $default != null: case _TendersResponse() when $default != null:
return $default(_that.data,_that.error,_that.message,_that.success);case _: return $default(_that.data,_that.error,_that.message,_that.success,_that.meta);case _:
return null; return null;
} }
@@ -236,13 +249,14 @@ return $default(_that.data,_that.error,_that.message,_that.success);case _:
@JsonSerializable() @JsonSerializable()
class _TendersResponse implements TendersResponse { class _TendersResponse implements TendersResponse {
const _TendersResponse({required this.data, required this.error, required this.message, required this.success}); const _TendersResponse({required this.data, required this.error, required this.message, required this.success, required this.meta});
factory _TendersResponse.fromJson(Map<String, dynamic> json) => _$TendersResponseFromJson(json); factory _TendersResponse.fromJson(Map<String, dynamic> json) => _$TendersResponseFromJson(json);
@override final TendersData? data; @override final TendersData? data;
@override final ErrorModel? error; @override final ErrorModel? error;
@override final String? message; @override final String? message;
@override final bool? success; @override final bool? success;
@override final Meta? meta;
/// Create a copy of TendersResponse /// Create a copy of TendersResponse
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@@ -257,16 +271,16 @@ Map<String, dynamic> toJson() {
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _TendersResponse&&(identical(other.data, data) || other.data == data)&&(identical(other.error, error) || other.error == error)&&(identical(other.message, message) || other.message == message)&&(identical(other.success, success) || other.success == success)); return identical(this, other) || (other.runtimeType == runtimeType&&other is _TendersResponse&&(identical(other.data, data) || other.data == data)&&(identical(other.error, error) || other.error == error)&&(identical(other.message, message) || other.message == message)&&(identical(other.success, success) || other.success == success)&&(identical(other.meta, meta) || other.meta == meta));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType,data,error,message,success); int get hashCode => Object.hash(runtimeType,data,error,message,success,meta);
@override @override
String toString() { String toString() {
return 'TendersResponse(data: $data, error: $error, message: $message, success: $success)'; return 'TendersResponse(data: $data, error: $error, message: $message, success: $success, meta: $meta)';
} }
@@ -277,11 +291,11 @@ abstract mixin class _$TendersResponseCopyWith<$Res> implements $TendersResponse
factory _$TendersResponseCopyWith(_TendersResponse value, $Res Function(_TendersResponse) _then) = __$TendersResponseCopyWithImpl; factory _$TendersResponseCopyWith(_TendersResponse value, $Res Function(_TendersResponse) _then) = __$TendersResponseCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
TendersData? data, ErrorModel? error, String? message, bool? success TendersData? data, ErrorModel? error, String? message, bool? success, Meta? meta
}); });
@override $TendersDataCopyWith<$Res>? get data;@override $ErrorModelCopyWith<$Res>? get error; @override $TendersDataCopyWith<$Res>? get data;@override $ErrorModelCopyWith<$Res>? get error;@override $MetaCopyWith<$Res>? get meta;
} }
/// @nodoc /// @nodoc
@@ -294,13 +308,14 @@ class __$TendersResponseCopyWithImpl<$Res>
/// Create a copy of TendersResponse /// Create a copy of TendersResponse
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? data = freezed,Object? error = freezed,Object? message = freezed,Object? success = freezed,}) { @override @pragma('vm:prefer-inline') $Res call({Object? data = freezed,Object? error = freezed,Object? message = freezed,Object? success = freezed,Object? meta = freezed,}) {
return _then(_TendersResponse( return _then(_TendersResponse(
data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable
as TendersData?,error: freezed == error ? _self.error : error // ignore: cast_nullable_to_non_nullable as TendersData?,error: freezed == error ? _self.error : error // ignore: cast_nullable_to_non_nullable
as ErrorModel?,message: freezed == message ? _self.message : message // ignore: cast_nullable_to_non_nullable as ErrorModel?,message: freezed == message ? _self.message : message // ignore: cast_nullable_to_non_nullable
as String?,success: freezed == success ? _self.success : success // ignore: cast_nullable_to_non_nullable as String?,success: freezed == success ? _self.success : success // ignore: cast_nullable_to_non_nullable
as bool?, as bool?,meta: freezed == meta ? _self.meta : meta // ignore: cast_nullable_to_non_nullable
as Meta?,
)); ));
} }
@@ -328,6 +343,18 @@ $ErrorModelCopyWith<$Res>? get error {
return $ErrorModelCopyWith<$Res>(_self.error!, (value) { return $ErrorModelCopyWith<$Res>(_self.error!, (value) {
return _then(_self.copyWith(error: value)); return _then(_self.copyWith(error: value));
}); });
}/// Create a copy of TendersResponse
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$MetaCopyWith<$Res>? get meta {
if (_self.meta == null) {
return null;
}
return $MetaCopyWith<$Res>(_self.meta!, (value) {
return _then(_self.copyWith(meta: value));
});
} }
} }
@@ -18,6 +18,10 @@ _TendersResponse _$TendersResponseFromJson(Map<String, dynamic> json) =>
: ErrorModel.fromJson(json['error'] as Map<String, dynamic>), : ErrorModel.fromJson(json['error'] as Map<String, dynamic>),
message: json['message'] as String?, message: json['message'] as String?,
success: json['success'] as bool?, success: json['success'] as bool?,
meta:
json['meta'] == null
? null
: Meta.fromJson(json['meta'] as Map<String, dynamic>),
); );
Map<String, dynamic> _$TendersResponseToJson(_TendersResponse instance) => Map<String, dynamic> _$TendersResponseToJson(_TendersResponse instance) =>
@@ -26,4 +30,5 @@ Map<String, dynamic> _$TendersResponseToJson(_TendersResponse instance) =>
'error': instance.error, 'error': instance.error,
'message': instance.message, 'message': instance.message,
'success': instance.success, 'success': instance.success,
'meta': instance.meta,
}; };
+12
View File
@@ -80,6 +80,9 @@ class $AssetsIconsGen {
/// File path: assets/icons/icon_contracts.svg /// File path: assets/icons/icon_contracts.svg
String get iconContracts => 'assets/icons/icon_contracts.svg'; String get iconContracts => 'assets/icons/icon_contracts.svg';
/// File path: assets/icons/left-arrow.svg
String get leftArrow => 'assets/icons/left-arrow.svg';
/// File path: assets/icons/like.svg /// File path: assets/icons/like.svg
String get like => 'assets/icons/like.svg'; String get like => 'assets/icons/like.svg';
@@ -113,9 +116,15 @@ class $AssetsIconsGen {
/// File path: assets/icons/profile-circle_active.svg /// File path: assets/icons/profile-circle_active.svg
String get profileCircleActive => 'assets/icons/profile-circle_active.svg'; String get profileCircleActive => 'assets/icons/profile-circle_active.svg';
/// File path: assets/icons/search_normal.svg
String get searchNormal => 'assets/icons/search_normal.svg';
/// File path: assets/icons/shield.svg /// File path: assets/icons/shield.svg
String get shield => 'assets/icons/shield.svg'; String get shield => 'assets/icons/shield.svg';
/// File path: assets/icons/sort.svg
String get sort => 'assets/icons/sort.svg';
/// File path: assets/icons/sun.svg /// File path: assets/icons/sun.svg
String get sun => 'assets/icons/sun.svg'; String get sun => 'assets/icons/sun.svg';
@@ -180,6 +189,7 @@ class $AssetsIconsGen {
home, home,
homeActive, homeActive,
iconContracts, iconContracts,
leftArrow,
like, like,
location, location,
logoBig, logoBig,
@@ -191,7 +201,9 @@ class $AssetsIconsGen {
notifyIconActive, notifyIconActive,
profileCircle, profileCircle,
profileCircleActive, profileCircleActive,
searchNormal,
shield, shield,
sort,
sun, sun,
taskSquareActive, taskSquareActive,
taskSquare, taskSquare,
+8 -8
View File
@@ -37,7 +37,7 @@ void main() async {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
List<SingleChildWidget> providers = [ List<SingleChildWidget> providers = [
Provider(create: (context) => prefs), Provider(create: (context) => prefs),
...providersRemote, ...providersRemote,
]; ];
// Configure web URL strategy if running on web // Configure web URL strategy if running on web
@@ -46,13 +46,13 @@ void main() async {
if (kIsWeb) { if (kIsWeb) {
await Firebase.initializeApp( await Firebase.initializeApp(
options: const FirebaseOptions( options: const FirebaseOptions(
apiKey: 'AIzaSyAUQhM789-0qZIaIWtwTQXyz9gEtN-TnnU', apiKey: 'AIzaSyCTjdsk2jE34IfnvSKP1JMTIf_Abd7tbt0',
authDomain: 'opplens.firebaseapp.com', authDomain: 'opplens-270d1.firebaseapp.com',
appId: '1:1071558853812:web:d9ca896b088ee7da48c3e1', projectId: 'opplens-270d1',
messagingSenderId: '1071558853812', storageBucket: 'opplens-270d1.firebasestorage.app',
projectId: 'opplens', messagingSenderId: '6923326255',
storageBucket: 'opplens.firebasestorage.app', appId: '1:6923326255:web:c71763ece06aaaf3ed87da',
measurementId: 'G-GJGP9WB28B', measurementId: 'G-M5HQR4YZVS',
), ),
); );
final _firebaseMessaging = FirebaseMessaging.instance; final _firebaseMessaging = FirebaseMessaging.instance;
+41
View File
@@ -54,6 +54,8 @@ class TendersViewModel with ChangeNotifier {
int currentPage = 1; int currentPage = 1;
int _currentOffset = 0; int _currentOffset = 0;
int _totalPages = 0;
int get totalPages => _totalPages;
static const int _pageSize = 10; static const int _pageSize = 10;
bool _hasMoreData = true; bool _hasMoreData = true;
@@ -117,6 +119,14 @@ class TendersViewModel with ChangeNotifier {
getTenderFeedback(_tendersResponse!.data!.tenders!.first.id!); getTenderFeedback(_tendersResponse!.data!.tenders!.first.id!);
getTenderApprovals(_tendersResponse!.data!.tenders!.first.id!); getTenderApprovals(_tendersResponse!.data!.tenders!.first.id!);
} }
if (_tendersResponse?.meta?.pages != null) {
_totalPages = _tendersResponse!.meta!.pages!;
print('Total pages: $_totalPages');
} else {
_totalPages = 1;
print('Total pages not available, fallback to 1');
}
notifyListeners();
break; break;
case Error<TendersResponse>(): case Error<TendersResponse>():
_errorMessage = result.error.toString(); _errorMessage = result.error.toString();
@@ -500,4 +510,35 @@ class TendersViewModel with ChangeNotifier {
_tenderApprovalErrorMessage = null; _tenderApprovalErrorMessage = null;
notifyListeners(); notifyListeners();
} }
void jumpToPage(int page) async {
final meta = tendersResponse?.meta;
if (meta?.pages == null) {
return;
}
if (page < 1 || page > meta!.pages!) {
return;
}
currentPage = page;
_currentOffset = (page - 1) * _pageSize;
final result = await _tendersRepository.getTenders(
limit: _pageSize,
offset: _currentOffset,
);
switch (result) {
case Ok<TendersResponse>():
_tendersResponse = result.value;
break;
case Error<TendersResponse>():
_errorMessage = result.error.toString();
break;
}
notifyListeners();
}
} }
+207 -105
View File
@@ -7,6 +7,8 @@ import 'package:tm_app/core/utils/size_config.dart';
import 'package:tm_app/view_models/tenders_view_model.dart'; import 'package:tm_app/view_models/tenders_view_model.dart';
import 'package:tm_app/views/shared/base_button.dart'; import 'package:tm_app/views/shared/base_button.dart';
import 'package:tm_app/views/shared/desktop_navigation_widget.dart'; import 'package:tm_app/views/shared/desktop_navigation_widget.dart';
import 'package:tm_app/views/shared/page_selection_dialog.dart';
import 'package:tm_app/views/tenders/strings/tenders_strings.dart';
import 'package:tm_app/views/tenders/widgets/main_tenders_slider.dart'; import 'package:tm_app/views/tenders/widgets/main_tenders_slider.dart';
import '../../../core/constants/common_strings.dart'; import '../../../core/constants/common_strings.dart';
@@ -21,6 +23,7 @@ class DesktopTendersPage extends StatefulWidget {
class _DesktopTendersPageState extends State<DesktopTendersPage> { class _DesktopTendersPageState extends State<DesktopTendersPage> {
late final TendersViewModel viewModel; late final TendersViewModel viewModel;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@@ -49,121 +52,49 @@ class _DesktopTendersPageState extends State<DesktopTendersPage> {
backgroundColor: AppColors.backgroundColor, backgroundColor: AppColors.backgroundColor,
body: Column( body: Column(
children: [ children: [
const DesktopNavigationWidget( const DesktopNavigationWidget(currentIndex: 1),
currentIndex: 1, // Tenders index
),
SizedBox(height: 48.0.h()), SizedBox(height: 48.0.h()),
//search box const _SearchBox(),
SizedBox( const _ActionButtons(),
height: 48.0.h(),
width: 680,
child: TextField(
style: TextStyle(fontSize: 16.0.sp(), color: AppColors.grey0),
decoration: InputDecoration(
hintText: 'Search',
hintStyle: TextStyle(
fontSize: 16.0.sp(),
color: AppColors.grey60,
),
prefixIcon: Padding(
padding: EdgeInsets.all(12.0.w()),
child: SvgPicture.asset(
AssetsManager.normalSearch,
width: 24.0.w(),
height: 24.0.h(),
),
),
filled: true,
fillColor: AppColors.grey0,
contentPadding: EdgeInsets.symmetric(
horizontal: 12.0.w(),
vertical: 12.0.h(),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12.0.w()),
borderSide: const BorderSide(
color: AppColors.mainBlue,
width: 1,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12.0.w()),
borderSide: BorderSide(color: AppColors.grey40, width: 1),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12.0.w()),
borderSide: const BorderSide(
color: AppColors.grey,
width: 1.5,
),
),
),
),
),
Container(
margin: EdgeInsets.only(top: 25.0.h()),
height: 40.0.h(),
width: 680,
child: Row(
children: [
BaseButton(
width: 178.0.w(),
isEnabled: true,
onPressed: () {},
text: 'Filter',
icon: AssetsManager.filter,
iconColor: AppColors.textBlue,
backgroundColor: AppColors.primary2,
textColor: AppColors.textBlue,
),
SizedBox(width: 10.0.w()),
BaseButton(
width: 178.0.w(),
isEnabled: true,
onPressed: () {},
text: 'Sort',
icon: AssetsManager.sort,
iconColor: AppColors.textBlue,
backgroundColor: AppColors.primary2,
textColor: AppColors.textBlue,
),
],
),
),
Expanded( Expanded(
child: Consumer<TendersViewModel>( child: Consumer<TendersViewModel>(
builder: (context, viewModel, child) { builder: (context, vm, child) {
if (viewModel.isLoading) { if (vm.isLoading) {
return const Expanded( return const Center(
child: Center( child: CircularProgressIndicator(color: AppColors.jellyBean),
child: CircularProgressIndicator(
color: AppColors.jellyBean,
),
),
); );
} }
if (viewModel.errorMessage != null) { if (vm.errorMessage != null) {
return Center(child: Text(viewModel.errorMessage!)); return Center(child: Text(vm.errorMessage!));
} }
if ((viewModel.tendersResponse?.data?.tenders == null || final tenders = vm.tendersResponse?.data?.tenders ?? [];
viewModel.tendersResponse!.data!.tenders!.isEmpty) && if (tenders.isEmpty) {
viewModel.isLoading == false) {
return const Center(child: Text(CommonStrings.noData)); return const Center(child: Text(CommonStrings.noData));
} }
return SingleChildScrollView(
child: SizedBox( return Column(
width: 740, children: [
height: 800, Expanded(
child: Padding( child: SingleChildScrollView(
padding: EdgeInsets.symmetric(vertical: 4.0.h()), child: SizedBox(
child: MainTendersSlider( width: 740,
tenders: viewModel.tendersResponse?.data?.tenders ?? [], height: 800,
isDesktop: true, child: Padding(
padding: EdgeInsets.symmetric(vertical: 4.0.h()),
child: MainTendersSlider(
tenders: tenders,
isDesktop: true,
),
),
),
), ),
), ),
), _DesktopPagination(
totalPages: vm.totalPages,
currentPage: vm.currentPage,
onPageSelected: vm.jumpToPage,
),
],
); );
}, },
), ),
@@ -173,3 +104,174 @@ class _DesktopTendersPageState extends State<DesktopTendersPage> {
); );
} }
} }
class _SearchBox extends StatelessWidget {
const _SearchBox();
@override
Widget build(BuildContext context) {
return SizedBox(
height: 48.0.h(),
width: 680,
child: TextField(
style: TextStyle(fontSize: 16.0.sp(), color: AppColors.grey0),
decoration: InputDecoration(
hintText: 'Search',
hintStyle: TextStyle(fontSize: 16.0.sp(), color: AppColors.grey60),
prefixIcon: Padding(
padding: EdgeInsets.all(12.0.w()),
child: SvgPicture.asset(
AssetsManager.normalSearch,
width: 24.0.w(),
height: 24.0.h(),
),
),
filled: true,
fillColor: AppColors.grey0,
contentPadding: EdgeInsets.symmetric(
horizontal: 12.0.w(),
vertical: 12.0.h(),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12.0.w()),
borderSide: const BorderSide(color: AppColors.mainBlue, width: 1),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12.0.w()),
borderSide: BorderSide(color: AppColors.grey40, width: 1),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12.0.w()),
borderSide: const BorderSide(color: AppColors.grey, width: 1.5),
),
),
),
);
}
}
class _ActionButtons extends StatelessWidget {
const _ActionButtons();
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(top: 25.0.h()),
height: 40.0.h(),
width: 680,
child: Row(
children: [
BaseButton(
width: 178.0.w(),
isEnabled: true,
onPressed: () {},
text: 'Filter',
icon: AssetsManager.filter,
iconColor: AppColors.textBlue,
backgroundColor: AppColors.primary2,
textColor: AppColors.textBlue,
),
SizedBox(width: 10.0.w()),
BaseButton(
width: 178.0.w(),
isEnabled: true,
onPressed: () {},
text: 'Sort',
icon: AssetsManager.sort,
iconColor: AppColors.textBlue,
backgroundColor: AppColors.primary2,
textColor: AppColors.textBlue,
),
],
),
);
}
}
class _DesktopPagination extends StatelessWidget {
final int totalPages;
final int currentPage;
final ValueChanged<int> onPageSelected;
const _DesktopPagination({
required this.totalPages,
required this.currentPage,
required this.onPageSelected,
});
@override
Widget build(BuildContext context) {
return SizedBox(
width: 680,
child: Padding(
padding: EdgeInsets.symmetric(vertical: 12.0.h()),
child: Row(
children: [
const Spacer(),
Text(
TendersStrings.page,
style: TextStyle(
fontSize: 13.0.sp(),
fontWeight: FontWeight.w300,
color: AppColors.grey60,
),
),
SizedBox(width: 10.0.w()),
InkWell(
onTap: () async {
final selectedPage = await showDialog<int>(
context: context,
builder: (context) =>
PageSelectionDialog(totalPages: totalPages),
);
if (selectedPage != null) {
onPageSelected(selectedPage);
}
},
child: Container(
decoration: BoxDecoration(
border: Border.all(color: AppColors.grey30, width: 1),
borderRadius: BorderRadius.circular(4),
),
child: Row(
children: [
SizedBox(width: 5.0.w()),
Text(
'$currentPage',
style: TextStyle(
fontSize: 14.0.sp(),
fontWeight: FontWeight.w500,
color: AppColors.grey70,
),
),
SizedBox(width: 5.0.w()),
SvgPicture.asset(AssetsManager.arrowDownSmall),
SizedBox(width: 5.0.w()),
],
),
),
),
SizedBox(width: 5.0.w()),
Text(
TendersStrings.of,
style: TextStyle(
fontSize: 14.0.sp(),
fontWeight: FontWeight.w400,
color: AppColors.grey60,
),
),
SizedBox(width: 5.0.w()),
Text(
'$totalPages',
style: TextStyle(
fontSize: 13.0.sp(),
fontWeight: FontWeight.w400,
color: AppColors.grey60,
),
),
],
),
),
);
}
}
@@ -10,4 +10,6 @@ class TendersStrings {
static const String tenderDeadlineLabel = 'Deadline'; static const String tenderDeadlineLabel = 'Deadline';
static const String contracts = 'Contracts'; static const String contracts = 'Contracts';
static const String notifications = 'Notifications'; static const String notifications = 'Notifications';
static const String of= 'Of';
static const String page = 'Page';
} }
@@ -27,6 +27,31 @@ class _MainTendersSliderState extends State<MainTendersSlider> {
final PageController pageController = PageController(initialPage: 0); final PageController pageController = PageController(initialPage: 0);
int currentPage = 1; int currentPage = 1;
ScrollController? _scrollController;
@override
void initState() {
super.initState();
if (widget.isDesktop) {
_scrollController = ScrollController();
_scrollController!.addListener(_onScroll);
}
}
void _onScroll() {
if (!widget.isDesktop) {
return;
}
final viewModel = context.read<TendersViewModel>();
if (_scrollController!.position.pixels >=
_scrollController!.position.maxScrollExtent - 200 &&
viewModel.hasMoreData &&
!viewModel.isLoadingMore) {
viewModel.loadMoreTenders();
}
}
void _goToPreviousPage() { void _goToPreviousPage() {
if (currentPage == 1) { if (currentPage == 1) {
return; return;
@@ -88,6 +113,7 @@ class _MainTendersSliderState extends State<MainTendersSlider> {
@override @override
void dispose() { void dispose() {
pageController.dispose(); pageController.dispose();
_scrollController?.dispose();
super.dispose(); super.dispose();
} }
@@ -98,19 +124,6 @@ class _MainTendersSliderState extends State<MainTendersSlider> {
return widget.isDesktop ? _tendersListView() : _tendersSlider(isLtr); return widget.isDesktop ? _tendersListView() : _tendersSlider(isLtr);
} }
Widget _tendersListView() {
return ListView.separated(
padding: EdgeInsets.all(16.0.h()),
itemCount: widget.tenders.length,
separatorBuilder:
(_, __) => SizedBox(height: widget.isDesktop ? 0 : 16.0.h()),
itemBuilder: (context, index) {
final tender = widget.tenders[index];
return TenderCard(tender: tender, isDesktop: widget.isDesktop);
},
);
}
Widget _tendersSlider(bool isLtr) { Widget _tendersSlider(bool isLtr) {
return Stack( return Stack(
children: [_tendersPageView(), _nextPreviousArrows(isLtr: isLtr)], children: [_tendersPageView(), _nextPreviousArrows(isLtr: isLtr)],
@@ -251,4 +264,38 @@ class _MainTendersSliderState extends State<MainTendersSlider> {
) )
: SizedBox(width: 32.0.h(), height: 32.0.h()); : SizedBox(width: 32.0.h(), height: 32.0.h());
} }
Widget _tendersListView() {
final viewModel = context.watch<TendersViewModel>();
return Column(
children: [
Expanded(
child: ListView.separated(
padding: EdgeInsets.all(16.0.h()),
itemCount:
widget.tenders.length +
(widget.isDesktop && viewModel.isLoadingMore ? 1 : 0),
separatorBuilder:
(_, __) => SizedBox(height: widget.isDesktop ? 0 : 16.0.h()),
itemBuilder: (context, index) {
if (index < widget.tenders.length) {
final tender = widget.tenders[index];
return TenderCard(tender: tender, isDesktop: widget.isDesktop);
} else {
return const Padding(
padding: EdgeInsets.all(16.0),
child: Center(
child: CircularProgressIndicator(
strokeWidth: 2.0,
color: AppColors.jellyBean,
),
),
);
}
},
),
),
],
);
}
} }
+7 -7
View File
@@ -2,13 +2,13 @@ importScripts("https://www.gstatic.com/firebasejs/9.22.2/firebase-app-compat.js"
importScripts("https://www.gstatic.com/firebasejs/9.22.2/firebase-messaging-compat.js"); importScripts("https://www.gstatic.com/firebasejs/9.22.2/firebase-messaging-compat.js");
firebase.initializeApp({ firebase.initializeApp({
apiKey: 'AIzaSyAUQhM789-0qZIaIWtwTQXyz9gEtN-TnnU', apiKey: "AIzaSyCTjdsk2jE34IfnvSKP1JMTIf_Abd7tbt0",
authDomain: 'opplens.firebaseapp.com', authDomain: "opplens-270d1.firebaseapp.com",
appId: '1:1071558853812:web:d9ca896b088ee7da48c3e1', projectId: "opplens-270d1",
messagingSenderId: '1071558853812', storageBucket: "opplens-270d1.firebasestorage.app",
projectId: 'opplens', messagingSenderId: "6923326255",
storageBucket: 'opplens.firebasestorage.app', appId: "1:6923326255:web:c71763ece06aaaf3ed87da",
measurementId: 'G-GJGP9WB28B', measurementId: "G-M5HQR4YZVS"
}); });
const messaging = firebase.messaging(); const messaging = firebase.messaging();