diff --git a/lib/core/config/dependencies.dart b/lib/core/config/dependencies.dart index ec4f903..b688d6c 100644 --- a/lib/core/config/dependencies.dart +++ b/lib/core/config/dependencies.dart @@ -4,15 +4,18 @@ import 'package:tm_app/core/network/network_manager.dart'; import 'package:tm_app/core/theme/theme_provider.dart'; import 'package:tm_app/data/repositories/home_repository.dart'; import 'package:tm_app/data/repositories/liked_tenders_repository.dart'; +import 'package:tm_app/data/repositories/notification_repository.dart'; import 'package:tm_app/data/repositories/tender_detail_repository.dart'; import 'package:tm_app/data/repositories/tenders_repository.dart'; import 'package:tm_app/data/repositories/your_tenders_repository.dart'; import 'package:tm_app/data/services/home_service.dart'; import 'package:tm_app/data/services/liked_tenders_service.dart'; +import 'package:tm_app/data/services/notification_service.dart'; import 'package:tm_app/data/services/tender_detail_service.dart'; import 'package:tm_app/data/services/your_tenders_service.dart'; import 'package:tm_app/view_models/final_completion_of_documents_view_model.dart'; import 'package:tm_app/view_models/liked_tenders_view_model.dart'; +import 'package:tm_app/view_models/notification_view_model.dart'; import 'package:tm_app/view_models/tender_detail_view_model.dart'; import 'package:tm_app/view_models/tenders_view_model.dart'; import 'package:tm_app/view_models/your_tenders_view_model.dart'; @@ -56,6 +59,9 @@ List get apiClients { ), Provider( create: (context) => ProfileService(networkManager: context.read()), + ), + Provider( + create: (context) => NotificationsService(networkManager: context.read()), ), ]; } @@ -93,6 +99,9 @@ List get repositories { ), Provider( create: (context) => ProfileRepository(profileService: context.read()), + ), + Provider( + create: (context) => NotificationsRepository(notificationsService: context.read()), ), ]; } @@ -145,6 +154,11 @@ List get viewModels { ), ChangeNotifierProvider( create: (context) => FinalCompletionOfDocumentsViewModel(), + ), + ChangeNotifierProvider( + create: + (context) => + NotificationViewModel(repositoryViewModel: context.read()), ), ]; } diff --git a/lib/core/theme/colors.dart b/lib/core/theme/colors.dart index d182d1b..ce739df 100644 --- a/lib/core/theme/colors.dart +++ b/lib/core/theme/colors.dart @@ -100,6 +100,11 @@ class AppColors { ? const Color.fromRGBO(104, 151, 151, 0.2) : const Color(0xFFEAF8F9); + static Color get primary10Light => + _isDarkMode + ? const Color.fromRGBO(104, 151, 151, 0.2) + : const Color(0xFFF7FAFF); + static Color get primary20 => _isDarkMode ? const Color.fromRGBO(111, 134, 165, 0.2) diff --git a/lib/data/repositories/notification_repository.dart b/lib/data/repositories/notification_repository.dart new file mode 100644 index 0000000..78d94bc --- /dev/null +++ b/lib/data/repositories/notification_repository.dart @@ -0,0 +1,24 @@ +import 'package:tm_app/core/utils/result.dart'; +import 'package:tm_app/data/services/model/notification__response/notification_response_model.dart'; +import 'package:tm_app/data/services/notification_service.dart'; + +class NotificationsRepository { + NotificationsRepository({required NotificationsService notificationsService}) + : _notificationsService = notificationsService; + + final NotificationsService _notificationsService; + + Future> getNotifications({ + required int limit, + required int offset, + }) async { + return _notificationsService.getNotifications( + limit: limit, + offset: offset, + ); + } + + Future> markAllAsRead() async { + return _notificationsService.markAllAsRead(); + } +} diff --git a/lib/data/services/api/notification_api.dart b/lib/data/services/api/notification_api.dart new file mode 100644 index 0000000..d5a9273 --- /dev/null +++ b/lib/data/services/api/notification_api.dart @@ -0,0 +1,16 @@ +class NotificationApi { + static const String notifications = '/api/v1/notifications'; + static String getNotifications({required int limit, required int offset}) { + return '$notifications?limit=$limit&offset=$offset'; + } + static const String feedback = '/api/v1/feedback'; + + + + static const String markAllAsRead = '/api/v1/notifications'; + static const String markAsRead = '/api/v1/notifications/mark-as-read'; + static const String tenderApprovalsTender = '/api/v1/notifications'; + static String getTenderApprovalById({required String tenderId}) { + return '$tenderApprovalsTender/$tenderId'; + } +} diff --git a/lib/data/services/model/notification__response/notification_model.freezed.dart b/lib/data/services/model/notification__response/notification_model.freezed.dart new file mode 100644 index 0000000..5106c39 --- /dev/null +++ b/lib/data/services/model/notification__response/notification_model.freezed.dart @@ -0,0 +1,334 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'notification_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$NotificationModel { + + bool? get success; String? get message; NotificationData? get data; ErrorModel? get error; +/// Create a copy of NotificationModel +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$NotificationModelCopyWith get copyWith => _$NotificationModelCopyWithImpl(this as NotificationModel, _$identity); + + /// Serializes this NotificationModel to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is NotificationModel&&(identical(other.success, success) || other.success == success)&&(identical(other.message, message) || other.message == message)&&(identical(other.data, data) || other.data == data)&&(identical(other.error, error) || other.error == error)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,success,message,data,error); + +@override +String toString() { + return 'NotificationModel(success: $success, message: $message, data: $data, error: $error)'; +} + + +} + +/// @nodoc +abstract mixin class $NotificationModelCopyWith<$Res> { + factory $NotificationModelCopyWith(NotificationModel value, $Res Function(NotificationModel) _then) = _$NotificationModelCopyWithImpl; +@useResult +$Res call({ + bool? success, String? message, NotificationData? data, ErrorModel? error +}); + + +$NotificationDataCopyWith<$Res>? get data;$ErrorModelCopyWith<$Res>? get error; + +} +/// @nodoc +class _$NotificationModelCopyWithImpl<$Res> + implements $NotificationModelCopyWith<$Res> { + _$NotificationModelCopyWithImpl(this._self, this._then); + + final NotificationModel _self; + final $Res Function(NotificationModel) _then; + +/// Create a copy of NotificationModel +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? success = freezed,Object? message = freezed,Object? data = freezed,Object? error = freezed,}) { + return _then(_self.copyWith( +success: freezed == success ? _self.success : success // ignore: cast_nullable_to_non_nullable +as bool?,message: freezed == message ? _self.message : message // ignore: cast_nullable_to_non_nullable +as String?,data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable +as NotificationData?,error: freezed == error ? _self.error : error // ignore: cast_nullable_to_non_nullable +as ErrorModel?, + )); +} +/// Create a copy of NotificationModel +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$NotificationDataCopyWith<$Res>? get data { + if (_self.data == null) { + return null; + } + + return $NotificationDataCopyWith<$Res>(_self.data!, (value) { + return _then(_self.copyWith(data: value)); + }); +}/// Create a copy of NotificationModel +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$ErrorModelCopyWith<$Res>? get error { + if (_self.error == null) { + return null; + } + + return $ErrorModelCopyWith<$Res>(_self.error!, (value) { + return _then(_self.copyWith(error: value)); + }); +} +} + + +/// Adds pattern-matching-related methods to [NotificationModel]. +extension NotificationModelPatterns on NotificationModel { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _NotificationModel value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _NotificationModel() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _NotificationModel value) $default,){ +final _that = this; +switch (_that) { +case _NotificationModel(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _NotificationModel value)? $default,){ +final _that = this; +switch (_that) { +case _NotificationModel() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( bool? success, String? message, NotificationData? data, ErrorModel? error)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _NotificationModel() when $default != null: +return $default(_that.success,_that.message,_that.data,_that.error);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( bool? success, String? message, NotificationData? data, ErrorModel? error) $default,) {final _that = this; +switch (_that) { +case _NotificationModel(): +return $default(_that.success,_that.message,_that.data,_that.error);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( bool? success, String? message, NotificationData? data, ErrorModel? error)? $default,) {final _that = this; +switch (_that) { +case _NotificationModel() when $default != null: +return $default(_that.success,_that.message,_that.data,_that.error);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _NotificationModel implements NotificationModel { + const _NotificationModel({required this.success, required this.message, required this.data, required this.error}); + factory _NotificationModel.fromJson(Map json) => _$NotificationModelFromJson(json); + +@override final bool? success; +@override final String? message; +@override final NotificationData? data; +@override final ErrorModel? error; + +/// Create a copy of NotificationModel +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$NotificationModelCopyWith<_NotificationModel> get copyWith => __$NotificationModelCopyWithImpl<_NotificationModel>(this, _$identity); + +@override +Map toJson() { + return _$NotificationModelToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _NotificationModel&&(identical(other.success, success) || other.success == success)&&(identical(other.message, message) || other.message == message)&&(identical(other.data, data) || other.data == data)&&(identical(other.error, error) || other.error == error)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,success,message,data,error); + +@override +String toString() { + return 'NotificationModel(success: $success, message: $message, data: $data, error: $error)'; +} + + +} + +/// @nodoc +abstract mixin class _$NotificationModelCopyWith<$Res> implements $NotificationModelCopyWith<$Res> { + factory _$NotificationModelCopyWith(_NotificationModel value, $Res Function(_NotificationModel) _then) = __$NotificationModelCopyWithImpl; +@override @useResult +$Res call({ + bool? success, String? message, NotificationData? data, ErrorModel? error +}); + + +@override $NotificationDataCopyWith<$Res>? get data;@override $ErrorModelCopyWith<$Res>? get error; + +} +/// @nodoc +class __$NotificationModelCopyWithImpl<$Res> + implements _$NotificationModelCopyWith<$Res> { + __$NotificationModelCopyWithImpl(this._self, this._then); + + final _NotificationModel _self; + final $Res Function(_NotificationModel) _then; + +/// Create a copy of NotificationModel +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? success = freezed,Object? message = freezed,Object? data = freezed,Object? error = freezed,}) { + return _then(_NotificationModel( +success: freezed == success ? _self.success : success // ignore: cast_nullable_to_non_nullable +as bool?,message: freezed == message ? _self.message : message // ignore: cast_nullable_to_non_nullable +as String?,data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable +as NotificationData?,error: freezed == error ? _self.error : error // ignore: cast_nullable_to_non_nullable +as ErrorModel?, + )); +} + +/// Create a copy of NotificationModel +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$NotificationDataCopyWith<$Res>? get data { + if (_self.data == null) { + return null; + } + + return $NotificationDataCopyWith<$Res>(_self.data!, (value) { + return _then(_self.copyWith(data: value)); + }); +}/// Create a copy of NotificationModel +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$ErrorModelCopyWith<$Res>? get error { + if (_self.error == null) { + return null; + } + + return $ErrorModelCopyWith<$Res>(_self.error!, (value) { + return _then(_self.copyWith(error: value)); + }); +} +} + +// dart format on diff --git a/lib/data/services/model/notification__response/notification_model.g.dart b/lib/data/services/model/notification__response/notification_model.g.dart new file mode 100644 index 0000000..452ce5d --- /dev/null +++ b/lib/data/services/model/notification__response/notification_model.g.dart @@ -0,0 +1,29 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'notification_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_NotificationModel _$NotificationModelFromJson(Map json) => + _NotificationModel( + success: json['success'] as bool?, + message: json['message'] as String?, + data: + json['data'] == null + ? null + : NotificationData.fromJson(json['data'] as Map), + error: + json['error'] == null + ? null + : ErrorModel.fromJson(json['error'] as Map), + ); + +Map _$NotificationModelToJson(_NotificationModel instance) => + { + 'success': instance.success, + 'message': instance.message, + 'data': instance.data, + 'error': instance.error, + }; diff --git a/lib/data/services/model/notification__response/notification_response_model.dart b/lib/data/services/model/notification__response/notification_response_model.dart new file mode 100644 index 0000000..ea41993 --- /dev/null +++ b/lib/data/services/model/notification__response/notification_response_model.dart @@ -0,0 +1,21 @@ +// ignore_for_file: invalid_annotation_target + +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:tm_app/data/services/model/error/error_model.dart'; +import 'package:tm_app/data/services/model/notification_data/notification_data.dart'; + +part 'notification_response_model.freezed.dart'; +part 'notification_response_model.g.dart'; + +@freezed +abstract class NotificationResponseModel with _$NotificationResponseModel { + const factory NotificationResponseModel({ + required bool? success, + required String? message, + required NotificationData? data, + required ErrorModel? error, + }) = _NotificationResponseModel; + + factory NotificationResponseModel.fromJson(Map json) => + _$NotificationResponseModelFromJson(json); +} diff --git a/lib/data/services/model/notification__response/notification_response_model.freezed.dart b/lib/data/services/model/notification__response/notification_response_model.freezed.dart new file mode 100644 index 0000000..4e576b6 --- /dev/null +++ b/lib/data/services/model/notification__response/notification_response_model.freezed.dart @@ -0,0 +1,334 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'notification_response_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$NotificationResponseModel { + + bool? get success; String? get message; NotificationData? get data; ErrorModel? get error; +/// Create a copy of NotificationResponseModel +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$NotificationResponseModelCopyWith get copyWith => _$NotificationResponseModelCopyWithImpl(this as NotificationResponseModel, _$identity); + + /// Serializes this NotificationResponseModel to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is NotificationResponseModel&&(identical(other.success, success) || other.success == success)&&(identical(other.message, message) || other.message == message)&&(identical(other.data, data) || other.data == data)&&(identical(other.error, error) || other.error == error)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,success,message,data,error); + +@override +String toString() { + return 'NotificationResponseModel(success: $success, message: $message, data: $data, error: $error)'; +} + + +} + +/// @nodoc +abstract mixin class $NotificationResponseModelCopyWith<$Res> { + factory $NotificationResponseModelCopyWith(NotificationResponseModel value, $Res Function(NotificationResponseModel) _then) = _$NotificationResponseModelCopyWithImpl; +@useResult +$Res call({ + bool? success, String? message, NotificationData? data, ErrorModel? error +}); + + +$NotificationDataCopyWith<$Res>? get data;$ErrorModelCopyWith<$Res>? get error; + +} +/// @nodoc +class _$NotificationResponseModelCopyWithImpl<$Res> + implements $NotificationResponseModelCopyWith<$Res> { + _$NotificationResponseModelCopyWithImpl(this._self, this._then); + + final NotificationResponseModel _self; + final $Res Function(NotificationResponseModel) _then; + +/// Create a copy of NotificationResponseModel +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? success = freezed,Object? message = freezed,Object? data = freezed,Object? error = freezed,}) { + return _then(_self.copyWith( +success: freezed == success ? _self.success : success // ignore: cast_nullable_to_non_nullable +as bool?,message: freezed == message ? _self.message : message // ignore: cast_nullable_to_non_nullable +as String?,data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable +as NotificationData?,error: freezed == error ? _self.error : error // ignore: cast_nullable_to_non_nullable +as ErrorModel?, + )); +} +/// Create a copy of NotificationResponseModel +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$NotificationDataCopyWith<$Res>? get data { + if (_self.data == null) { + return null; + } + + return $NotificationDataCopyWith<$Res>(_self.data!, (value) { + return _then(_self.copyWith(data: value)); + }); +}/// Create a copy of NotificationResponseModel +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$ErrorModelCopyWith<$Res>? get error { + if (_self.error == null) { + return null; + } + + return $ErrorModelCopyWith<$Res>(_self.error!, (value) { + return _then(_self.copyWith(error: value)); + }); +} +} + + +/// Adds pattern-matching-related methods to [NotificationResponseModel]. +extension NotificationResponseModelPatterns on NotificationResponseModel { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _NotificationResponseModel value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _NotificationResponseModel() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _NotificationResponseModel value) $default,){ +final _that = this; +switch (_that) { +case _NotificationResponseModel(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _NotificationResponseModel value)? $default,){ +final _that = this; +switch (_that) { +case _NotificationResponseModel() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( bool? success, String? message, NotificationData? data, ErrorModel? error)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _NotificationResponseModel() when $default != null: +return $default(_that.success,_that.message,_that.data,_that.error);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( bool? success, String? message, NotificationData? data, ErrorModel? error) $default,) {final _that = this; +switch (_that) { +case _NotificationResponseModel(): +return $default(_that.success,_that.message,_that.data,_that.error);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( bool? success, String? message, NotificationData? data, ErrorModel? error)? $default,) {final _that = this; +switch (_that) { +case _NotificationResponseModel() when $default != null: +return $default(_that.success,_that.message,_that.data,_that.error);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _NotificationResponseModel implements NotificationResponseModel { + const _NotificationResponseModel({required this.success, required this.message, required this.data, required this.error}); + factory _NotificationResponseModel.fromJson(Map json) => _$NotificationResponseModelFromJson(json); + +@override final bool? success; +@override final String? message; +@override final NotificationData? data; +@override final ErrorModel? error; + +/// Create a copy of NotificationResponseModel +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$NotificationResponseModelCopyWith<_NotificationResponseModel> get copyWith => __$NotificationResponseModelCopyWithImpl<_NotificationResponseModel>(this, _$identity); + +@override +Map toJson() { + return _$NotificationResponseModelToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _NotificationResponseModel&&(identical(other.success, success) || other.success == success)&&(identical(other.message, message) || other.message == message)&&(identical(other.data, data) || other.data == data)&&(identical(other.error, error) || other.error == error)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,success,message,data,error); + +@override +String toString() { + return 'NotificationResponseModel(success: $success, message: $message, data: $data, error: $error)'; +} + + +} + +/// @nodoc +abstract mixin class _$NotificationResponseModelCopyWith<$Res> implements $NotificationResponseModelCopyWith<$Res> { + factory _$NotificationResponseModelCopyWith(_NotificationResponseModel value, $Res Function(_NotificationResponseModel) _then) = __$NotificationResponseModelCopyWithImpl; +@override @useResult +$Res call({ + bool? success, String? message, NotificationData? data, ErrorModel? error +}); + + +@override $NotificationDataCopyWith<$Res>? get data;@override $ErrorModelCopyWith<$Res>? get error; + +} +/// @nodoc +class __$NotificationResponseModelCopyWithImpl<$Res> + implements _$NotificationResponseModelCopyWith<$Res> { + __$NotificationResponseModelCopyWithImpl(this._self, this._then); + + final _NotificationResponseModel _self; + final $Res Function(_NotificationResponseModel) _then; + +/// Create a copy of NotificationResponseModel +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? success = freezed,Object? message = freezed,Object? data = freezed,Object? error = freezed,}) { + return _then(_NotificationResponseModel( +success: freezed == success ? _self.success : success // ignore: cast_nullable_to_non_nullable +as bool?,message: freezed == message ? _self.message : message // ignore: cast_nullable_to_non_nullable +as String?,data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable +as NotificationData?,error: freezed == error ? _self.error : error // ignore: cast_nullable_to_non_nullable +as ErrorModel?, + )); +} + +/// Create a copy of NotificationResponseModel +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$NotificationDataCopyWith<$Res>? get data { + if (_self.data == null) { + return null; + } + + return $NotificationDataCopyWith<$Res>(_self.data!, (value) { + return _then(_self.copyWith(data: value)); + }); +}/// Create a copy of NotificationResponseModel +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$ErrorModelCopyWith<$Res>? get error { + if (_self.error == null) { + return null; + } + + return $ErrorModelCopyWith<$Res>(_self.error!, (value) { + return _then(_self.copyWith(error: value)); + }); +} +} + +// dart format on diff --git a/lib/data/services/model/notification__response/notification_response_model.g.dart b/lib/data/services/model/notification__response/notification_response_model.g.dart new file mode 100644 index 0000000..a5fe2f3 --- /dev/null +++ b/lib/data/services/model/notification__response/notification_response_model.g.dart @@ -0,0 +1,31 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'notification_response_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_NotificationResponseModel _$NotificationResponseModelFromJson( + Map json, +) => _NotificationResponseModel( + success: json['success'] as bool?, + message: json['message'] as String?, + data: + json['data'] == null + ? null + : NotificationData.fromJson(json['data'] as Map), + error: + json['error'] == null + ? null + : ErrorModel.fromJson(json['error'] as Map), +); + +Map _$NotificationResponseModelToJson( + _NotificationResponseModel instance, +) => { + 'success': instance.success, + 'message': instance.message, + 'data': instance.data, + 'error': instance.error, +}; diff --git a/lib/data/services/model/notification_data/notification_data.dart b/lib/data/services/model/notification_data/notification_data.dart new file mode 100644 index 0000000..4a12eca --- /dev/null +++ b/lib/data/services/model/notification_data/notification_data.dart @@ -0,0 +1,46 @@ +// ignore_for_file: invalid_annotation_target + +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:tm_app/data/services/model/meta/meta.dart'; + +part 'notification_data.freezed.dart'; +part 'notification_data.g.dart'; + +@freezed +abstract class NotificationData with _$NotificationData { + const factory NotificationData({ + required List? notifications, + required Meta? meta, + }) = _NotificationData; + + factory NotificationData.fromJson(Map json) => + _$NotificationDataFromJson(json); +} + +@freezed +abstract class NotificationItem with _$NotificationItem { + const factory NotificationItem({ + required String? id, + @JsonKey(name: 'user_id') required String? userId, + required String? title, + required String? message, + required String? link, + required String? image, + required String? priority, + required String? type, + @JsonKey(name: 'event_type') required String? eventType, + required String? status, + required Map? methods, + required Map? metadata, + @JsonKey(name: 'schedule_at') required int? scheduleAt, + @JsonKey(name: 'created_at') required String? createdAt, + @JsonKey(name: 'updated_at') required String? updatedAt, + required bool? seen, + @JsonKey(name: 'seen_at') required int? seenAt, + @JsonKey(name: 'is_scheduled') required bool? isScheduled, + @JsonKey(name: 'scheduled_at') required int? scheduledAt, + }) = _NotificationItem; + + factory NotificationItem.fromJson(Map json) => + _$NotificationItemFromJson(json); +} \ No newline at end of file diff --git a/lib/data/services/model/notification_data/notification_data.freezed.dart b/lib/data/services/model/notification_data/notification_data.freezed.dart new file mode 100644 index 0000000..ff6ff1e --- /dev/null +++ b/lib/data/services/model/notification_data/notification_data.freezed.dart @@ -0,0 +1,645 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'notification_data.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$NotificationData { + + List? get notifications; Meta? get meta; +/// Create a copy of NotificationData +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$NotificationDataCopyWith get copyWith => _$NotificationDataCopyWithImpl(this as NotificationData, _$identity); + + /// Serializes this NotificationData to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is NotificationData&&const DeepCollectionEquality().equals(other.notifications, notifications)&&(identical(other.meta, meta) || other.meta == meta)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(notifications),meta); + +@override +String toString() { + return 'NotificationData(notifications: $notifications, meta: $meta)'; +} + + +} + +/// @nodoc +abstract mixin class $NotificationDataCopyWith<$Res> { + factory $NotificationDataCopyWith(NotificationData value, $Res Function(NotificationData) _then) = _$NotificationDataCopyWithImpl; +@useResult +$Res call({ + List? notifications, Meta? meta +}); + + +$MetaCopyWith<$Res>? get meta; + +} +/// @nodoc +class _$NotificationDataCopyWithImpl<$Res> + implements $NotificationDataCopyWith<$Res> { + _$NotificationDataCopyWithImpl(this._self, this._then); + + final NotificationData _self; + final $Res Function(NotificationData) _then; + +/// Create a copy of NotificationData +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? notifications = freezed,Object? meta = freezed,}) { + return _then(_self.copyWith( +notifications: freezed == notifications ? _self.notifications : notifications // ignore: cast_nullable_to_non_nullable +as List?,meta: freezed == meta ? _self.meta : meta // ignore: cast_nullable_to_non_nullable +as Meta?, + )); +} +/// Create a copy of NotificationData +/// 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)); + }); +} +} + + +/// Adds pattern-matching-related methods to [NotificationData]. +extension NotificationDataPatterns on NotificationData { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _NotificationData value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _NotificationData() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _NotificationData value) $default,){ +final _that = this; +switch (_that) { +case _NotificationData(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _NotificationData value)? $default,){ +final _that = this; +switch (_that) { +case _NotificationData() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( List? notifications, Meta? meta)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _NotificationData() when $default != null: +return $default(_that.notifications,_that.meta);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( List? notifications, Meta? meta) $default,) {final _that = this; +switch (_that) { +case _NotificationData(): +return $default(_that.notifications,_that.meta);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( List? notifications, Meta? meta)? $default,) {final _that = this; +switch (_that) { +case _NotificationData() when $default != null: +return $default(_that.notifications,_that.meta);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _NotificationData implements NotificationData { + const _NotificationData({required final List? notifications, required this.meta}): _notifications = notifications; + factory _NotificationData.fromJson(Map json) => _$NotificationDataFromJson(json); + + final List? _notifications; +@override List? get notifications { + final value = _notifications; + if (value == null) return null; + if (_notifications is EqualUnmodifiableListView) return _notifications; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); +} + +@override final Meta? meta; + +/// Create a copy of NotificationData +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$NotificationDataCopyWith<_NotificationData> get copyWith => __$NotificationDataCopyWithImpl<_NotificationData>(this, _$identity); + +@override +Map toJson() { + return _$NotificationDataToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _NotificationData&&const DeepCollectionEquality().equals(other._notifications, _notifications)&&(identical(other.meta, meta) || other.meta == meta)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_notifications),meta); + +@override +String toString() { + return 'NotificationData(notifications: $notifications, meta: $meta)'; +} + + +} + +/// @nodoc +abstract mixin class _$NotificationDataCopyWith<$Res> implements $NotificationDataCopyWith<$Res> { + factory _$NotificationDataCopyWith(_NotificationData value, $Res Function(_NotificationData) _then) = __$NotificationDataCopyWithImpl; +@override @useResult +$Res call({ + List? notifications, Meta? meta +}); + + +@override $MetaCopyWith<$Res>? get meta; + +} +/// @nodoc +class __$NotificationDataCopyWithImpl<$Res> + implements _$NotificationDataCopyWith<$Res> { + __$NotificationDataCopyWithImpl(this._self, this._then); + + final _NotificationData _self; + final $Res Function(_NotificationData) _then; + +/// Create a copy of NotificationData +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? notifications = freezed,Object? meta = freezed,}) { + return _then(_NotificationData( +notifications: freezed == notifications ? _self._notifications : notifications // ignore: cast_nullable_to_non_nullable +as List?,meta: freezed == meta ? _self.meta : meta // ignore: cast_nullable_to_non_nullable +as Meta?, + )); +} + +/// Create a copy of NotificationData +/// 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)); + }); +} +} + + +/// @nodoc +mixin _$NotificationItem { + + String? get id;@JsonKey(name: 'user_id') String? get userId; String? get title; String? get message; String? get link; String? get image; String? get priority; String? get type;@JsonKey(name: 'event_type') String? get eventType; String? get status; Map? get methods; Map? get metadata;@JsonKey(name: 'schedule_at') int? get scheduleAt;@JsonKey(name: 'created_at') String? get createdAt;@JsonKey(name: 'updated_at') String? get updatedAt; bool? get seen;@JsonKey(name: 'seen_at') int? get seenAt;@JsonKey(name: 'is_scheduled') bool? get isScheduled;@JsonKey(name: 'scheduled_at') int? get scheduledAt; +/// Create a copy of NotificationItem +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$NotificationItemCopyWith get copyWith => _$NotificationItemCopyWithImpl(this as NotificationItem, _$identity); + + /// Serializes this NotificationItem to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is NotificationItem&&(identical(other.id, id) || other.id == id)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.title, title) || other.title == title)&&(identical(other.message, message) || other.message == message)&&(identical(other.link, link) || other.link == link)&&(identical(other.image, image) || other.image == image)&&(identical(other.priority, priority) || other.priority == priority)&&(identical(other.type, type) || other.type == type)&&(identical(other.eventType, eventType) || other.eventType == eventType)&&(identical(other.status, status) || other.status == status)&&const DeepCollectionEquality().equals(other.methods, methods)&&const DeepCollectionEquality().equals(other.metadata, metadata)&&(identical(other.scheduleAt, scheduleAt) || other.scheduleAt == scheduleAt)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.seen, seen) || other.seen == seen)&&(identical(other.seenAt, seenAt) || other.seenAt == seenAt)&&(identical(other.isScheduled, isScheduled) || other.isScheduled == isScheduled)&&(identical(other.scheduledAt, scheduledAt) || other.scheduledAt == scheduledAt)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hashAll([runtimeType,id,userId,title,message,link,image,priority,type,eventType,status,const DeepCollectionEquality().hash(methods),const DeepCollectionEquality().hash(metadata),scheduleAt,createdAt,updatedAt,seen,seenAt,isScheduled,scheduledAt]); + +@override +String toString() { + return 'NotificationItem(id: $id, userId: $userId, title: $title, message: $message, link: $link, image: $image, priority: $priority, type: $type, eventType: $eventType, status: $status, methods: $methods, metadata: $metadata, scheduleAt: $scheduleAt, createdAt: $createdAt, updatedAt: $updatedAt, seen: $seen, seenAt: $seenAt, isScheduled: $isScheduled, scheduledAt: $scheduledAt)'; +} + + +} + +/// @nodoc +abstract mixin class $NotificationItemCopyWith<$Res> { + factory $NotificationItemCopyWith(NotificationItem value, $Res Function(NotificationItem) _then) = _$NotificationItemCopyWithImpl; +@useResult +$Res call({ + String? id,@JsonKey(name: 'user_id') String? userId, String? title, String? message, String? link, String? image, String? priority, String? type,@JsonKey(name: 'event_type') String? eventType, String? status, Map? methods, Map? metadata,@JsonKey(name: 'schedule_at') int? scheduleAt,@JsonKey(name: 'created_at') String? createdAt,@JsonKey(name: 'updated_at') String? updatedAt, bool? seen,@JsonKey(name: 'seen_at') int? seenAt,@JsonKey(name: 'is_scheduled') bool? isScheduled,@JsonKey(name: 'scheduled_at') int? scheduledAt +}); + + + + +} +/// @nodoc +class _$NotificationItemCopyWithImpl<$Res> + implements $NotificationItemCopyWith<$Res> { + _$NotificationItemCopyWithImpl(this._self, this._then); + + final NotificationItem _self; + final $Res Function(NotificationItem) _then; + +/// Create a copy of NotificationItem +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? id = freezed,Object? userId = freezed,Object? title = freezed,Object? message = freezed,Object? link = freezed,Object? image = freezed,Object? priority = freezed,Object? type = freezed,Object? eventType = freezed,Object? status = freezed,Object? methods = freezed,Object? metadata = freezed,Object? scheduleAt = freezed,Object? createdAt = freezed,Object? updatedAt = freezed,Object? seen = freezed,Object? seenAt = freezed,Object? isScheduled = freezed,Object? scheduledAt = freezed,}) { + return _then(_self.copyWith( +id: freezed == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String?,userId: freezed == userId ? _self.userId : userId // ignore: cast_nullable_to_non_nullable +as String?,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable +as String?,message: freezed == message ? _self.message : message // ignore: cast_nullable_to_non_nullable +as String?,link: freezed == link ? _self.link : link // ignore: cast_nullable_to_non_nullable +as String?,image: freezed == image ? _self.image : image // ignore: cast_nullable_to_non_nullable +as String?,priority: freezed == priority ? _self.priority : priority // ignore: cast_nullable_to_non_nullable +as String?,type: freezed == type ? _self.type : type // ignore: cast_nullable_to_non_nullable +as String?,eventType: freezed == eventType ? _self.eventType : eventType // ignore: cast_nullable_to_non_nullable +as String?,status: freezed == status ? _self.status : status // ignore: cast_nullable_to_non_nullable +as String?,methods: freezed == methods ? _self.methods : methods // ignore: cast_nullable_to_non_nullable +as Map?,metadata: freezed == metadata ? _self.metadata : metadata // ignore: cast_nullable_to_non_nullable +as Map?,scheduleAt: freezed == scheduleAt ? _self.scheduleAt : scheduleAt // ignore: cast_nullable_to_non_nullable +as int?,createdAt: freezed == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable +as String?,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable +as String?,seen: freezed == seen ? _self.seen : seen // ignore: cast_nullable_to_non_nullable +as bool?,seenAt: freezed == seenAt ? _self.seenAt : seenAt // ignore: cast_nullable_to_non_nullable +as int?,isScheduled: freezed == isScheduled ? _self.isScheduled : isScheduled // ignore: cast_nullable_to_non_nullable +as bool?,scheduledAt: freezed == scheduledAt ? _self.scheduledAt : scheduledAt // ignore: cast_nullable_to_non_nullable +as int?, + )); +} + +} + + +/// Adds pattern-matching-related methods to [NotificationItem]. +extension NotificationItemPatterns on NotificationItem { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _NotificationItem value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _NotificationItem() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _NotificationItem value) $default,){ +final _that = this; +switch (_that) { +case _NotificationItem(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _NotificationItem value)? $default,){ +final _that = this; +switch (_that) { +case _NotificationItem() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String? id, @JsonKey(name: 'user_id') String? userId, String? title, String? message, String? link, String? image, String? priority, String? type, @JsonKey(name: 'event_type') String? eventType, String? status, Map? methods, Map? metadata, @JsonKey(name: 'schedule_at') int? scheduleAt, @JsonKey(name: 'created_at') String? createdAt, @JsonKey(name: 'updated_at') String? updatedAt, bool? seen, @JsonKey(name: 'seen_at') int? seenAt, @JsonKey(name: 'is_scheduled') bool? isScheduled, @JsonKey(name: 'scheduled_at') int? scheduledAt)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _NotificationItem() when $default != null: +return $default(_that.id,_that.userId,_that.title,_that.message,_that.link,_that.image,_that.priority,_that.type,_that.eventType,_that.status,_that.methods,_that.metadata,_that.scheduleAt,_that.createdAt,_that.updatedAt,_that.seen,_that.seenAt,_that.isScheduled,_that.scheduledAt);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String? id, @JsonKey(name: 'user_id') String? userId, String? title, String? message, String? link, String? image, String? priority, String? type, @JsonKey(name: 'event_type') String? eventType, String? status, Map? methods, Map? metadata, @JsonKey(name: 'schedule_at') int? scheduleAt, @JsonKey(name: 'created_at') String? createdAt, @JsonKey(name: 'updated_at') String? updatedAt, bool? seen, @JsonKey(name: 'seen_at') int? seenAt, @JsonKey(name: 'is_scheduled') bool? isScheduled, @JsonKey(name: 'scheduled_at') int? scheduledAt) $default,) {final _that = this; +switch (_that) { +case _NotificationItem(): +return $default(_that.id,_that.userId,_that.title,_that.message,_that.link,_that.image,_that.priority,_that.type,_that.eventType,_that.status,_that.methods,_that.metadata,_that.scheduleAt,_that.createdAt,_that.updatedAt,_that.seen,_that.seenAt,_that.isScheduled,_that.scheduledAt);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String? id, @JsonKey(name: 'user_id') String? userId, String? title, String? message, String? link, String? image, String? priority, String? type, @JsonKey(name: 'event_type') String? eventType, String? status, Map? methods, Map? metadata, @JsonKey(name: 'schedule_at') int? scheduleAt, @JsonKey(name: 'created_at') String? createdAt, @JsonKey(name: 'updated_at') String? updatedAt, bool? seen, @JsonKey(name: 'seen_at') int? seenAt, @JsonKey(name: 'is_scheduled') bool? isScheduled, @JsonKey(name: 'scheduled_at') int? scheduledAt)? $default,) {final _that = this; +switch (_that) { +case _NotificationItem() when $default != null: +return $default(_that.id,_that.userId,_that.title,_that.message,_that.link,_that.image,_that.priority,_that.type,_that.eventType,_that.status,_that.methods,_that.metadata,_that.scheduleAt,_that.createdAt,_that.updatedAt,_that.seen,_that.seenAt,_that.isScheduled,_that.scheduledAt);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _NotificationItem implements NotificationItem { + const _NotificationItem({required this.id, @JsonKey(name: 'user_id') required this.userId, required this.title, required this.message, required this.link, required this.image, required this.priority, required this.type, @JsonKey(name: 'event_type') required this.eventType, required this.status, required final Map? methods, required final Map? metadata, @JsonKey(name: 'schedule_at') required this.scheduleAt, @JsonKey(name: 'created_at') required this.createdAt, @JsonKey(name: 'updated_at') required this.updatedAt, required this.seen, @JsonKey(name: 'seen_at') required this.seenAt, @JsonKey(name: 'is_scheduled') required this.isScheduled, @JsonKey(name: 'scheduled_at') required this.scheduledAt}): _methods = methods,_metadata = metadata; + factory _NotificationItem.fromJson(Map json) => _$NotificationItemFromJson(json); + +@override final String? id; +@override@JsonKey(name: 'user_id') final String? userId; +@override final String? title; +@override final String? message; +@override final String? link; +@override final String? image; +@override final String? priority; +@override final String? type; +@override@JsonKey(name: 'event_type') final String? eventType; +@override final String? status; + final Map? _methods; +@override Map? get methods { + final value = _methods; + if (value == null) return null; + if (_methods is EqualUnmodifiableMapView) return _methods; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(value); +} + + final Map? _metadata; +@override Map? get metadata { + final value = _metadata; + if (value == null) return null; + if (_metadata is EqualUnmodifiableMapView) return _metadata; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(value); +} + +@override@JsonKey(name: 'schedule_at') final int? scheduleAt; +@override@JsonKey(name: 'created_at') final String? createdAt; +@override@JsonKey(name: 'updated_at') final String? updatedAt; +@override final bool? seen; +@override@JsonKey(name: 'seen_at') final int? seenAt; +@override@JsonKey(name: 'is_scheduled') final bool? isScheduled; +@override@JsonKey(name: 'scheduled_at') final int? scheduledAt; + +/// Create a copy of NotificationItem +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$NotificationItemCopyWith<_NotificationItem> get copyWith => __$NotificationItemCopyWithImpl<_NotificationItem>(this, _$identity); + +@override +Map toJson() { + return _$NotificationItemToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _NotificationItem&&(identical(other.id, id) || other.id == id)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.title, title) || other.title == title)&&(identical(other.message, message) || other.message == message)&&(identical(other.link, link) || other.link == link)&&(identical(other.image, image) || other.image == image)&&(identical(other.priority, priority) || other.priority == priority)&&(identical(other.type, type) || other.type == type)&&(identical(other.eventType, eventType) || other.eventType == eventType)&&(identical(other.status, status) || other.status == status)&&const DeepCollectionEquality().equals(other._methods, _methods)&&const DeepCollectionEquality().equals(other._metadata, _metadata)&&(identical(other.scheduleAt, scheduleAt) || other.scheduleAt == scheduleAt)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.seen, seen) || other.seen == seen)&&(identical(other.seenAt, seenAt) || other.seenAt == seenAt)&&(identical(other.isScheduled, isScheduled) || other.isScheduled == isScheduled)&&(identical(other.scheduledAt, scheduledAt) || other.scheduledAt == scheduledAt)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hashAll([runtimeType,id,userId,title,message,link,image,priority,type,eventType,status,const DeepCollectionEquality().hash(_methods),const DeepCollectionEquality().hash(_metadata),scheduleAt,createdAt,updatedAt,seen,seenAt,isScheduled,scheduledAt]); + +@override +String toString() { + return 'NotificationItem(id: $id, userId: $userId, title: $title, message: $message, link: $link, image: $image, priority: $priority, type: $type, eventType: $eventType, status: $status, methods: $methods, metadata: $metadata, scheduleAt: $scheduleAt, createdAt: $createdAt, updatedAt: $updatedAt, seen: $seen, seenAt: $seenAt, isScheduled: $isScheduled, scheduledAt: $scheduledAt)'; +} + + +} + +/// @nodoc +abstract mixin class _$NotificationItemCopyWith<$Res> implements $NotificationItemCopyWith<$Res> { + factory _$NotificationItemCopyWith(_NotificationItem value, $Res Function(_NotificationItem) _then) = __$NotificationItemCopyWithImpl; +@override @useResult +$Res call({ + String? id,@JsonKey(name: 'user_id') String? userId, String? title, String? message, String? link, String? image, String? priority, String? type,@JsonKey(name: 'event_type') String? eventType, String? status, Map? methods, Map? metadata,@JsonKey(name: 'schedule_at') int? scheduleAt,@JsonKey(name: 'created_at') String? createdAt,@JsonKey(name: 'updated_at') String? updatedAt, bool? seen,@JsonKey(name: 'seen_at') int? seenAt,@JsonKey(name: 'is_scheduled') bool? isScheduled,@JsonKey(name: 'scheduled_at') int? scheduledAt +}); + + + + +} +/// @nodoc +class __$NotificationItemCopyWithImpl<$Res> + implements _$NotificationItemCopyWith<$Res> { + __$NotificationItemCopyWithImpl(this._self, this._then); + + final _NotificationItem _self; + final $Res Function(_NotificationItem) _then; + +/// Create a copy of NotificationItem +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? id = freezed,Object? userId = freezed,Object? title = freezed,Object? message = freezed,Object? link = freezed,Object? image = freezed,Object? priority = freezed,Object? type = freezed,Object? eventType = freezed,Object? status = freezed,Object? methods = freezed,Object? metadata = freezed,Object? scheduleAt = freezed,Object? createdAt = freezed,Object? updatedAt = freezed,Object? seen = freezed,Object? seenAt = freezed,Object? isScheduled = freezed,Object? scheduledAt = freezed,}) { + return _then(_NotificationItem( +id: freezed == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String?,userId: freezed == userId ? _self.userId : userId // ignore: cast_nullable_to_non_nullable +as String?,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable +as String?,message: freezed == message ? _self.message : message // ignore: cast_nullable_to_non_nullable +as String?,link: freezed == link ? _self.link : link // ignore: cast_nullable_to_non_nullable +as String?,image: freezed == image ? _self.image : image // ignore: cast_nullable_to_non_nullable +as String?,priority: freezed == priority ? _self.priority : priority // ignore: cast_nullable_to_non_nullable +as String?,type: freezed == type ? _self.type : type // ignore: cast_nullable_to_non_nullable +as String?,eventType: freezed == eventType ? _self.eventType : eventType // ignore: cast_nullable_to_non_nullable +as String?,status: freezed == status ? _self.status : status // ignore: cast_nullable_to_non_nullable +as String?,methods: freezed == methods ? _self._methods : methods // ignore: cast_nullable_to_non_nullable +as Map?,metadata: freezed == metadata ? _self._metadata : metadata // ignore: cast_nullable_to_non_nullable +as Map?,scheduleAt: freezed == scheduleAt ? _self.scheduleAt : scheduleAt // ignore: cast_nullable_to_non_nullable +as int?,createdAt: freezed == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable +as String?,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable +as String?,seen: freezed == seen ? _self.seen : seen // ignore: cast_nullable_to_non_nullable +as bool?,seenAt: freezed == seenAt ? _self.seenAt : seenAt // ignore: cast_nullable_to_non_nullable +as int?,isScheduled: freezed == isScheduled ? _self.isScheduled : isScheduled // ignore: cast_nullable_to_non_nullable +as bool?,scheduledAt: freezed == scheduledAt ? _self.scheduledAt : scheduledAt // ignore: cast_nullable_to_non_nullable +as int?, + )); +} + + +} + +// dart format on diff --git a/lib/data/services/model/notification_data/notification_data.g.dart b/lib/data/services/model/notification_data/notification_data.g.dart new file mode 100644 index 0000000..1b8e249 --- /dev/null +++ b/lib/data/services/model/notification_data/notification_data.g.dart @@ -0,0 +1,71 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'notification_data.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_NotificationData _$NotificationDataFromJson(Map json) => + _NotificationData( + notifications: + (json['notifications'] as List?) + ?.map((e) => NotificationItem.fromJson(e as Map)) + .toList(), + meta: + json['meta'] == null + ? null + : Meta.fromJson(json['meta'] as Map), + ); + +Map _$NotificationDataToJson(_NotificationData instance) => + { + 'notifications': instance.notifications, + 'meta': instance.meta, + }; + +_NotificationItem _$NotificationItemFromJson(Map json) => + _NotificationItem( + id: json['id'] as String?, + userId: json['user_id'] as String?, + title: json['title'] as String?, + message: json['message'] as String?, + link: json['link'] as String?, + image: json['image'] as String?, + priority: json['priority'] as String?, + type: json['type'] as String?, + eventType: json['event_type'] as String?, + status: json['status'] as String?, + methods: json['methods'] as Map?, + metadata: json['metadata'] as Map?, + scheduleAt: (json['schedule_at'] as num?)?.toInt(), + createdAt: json['created_at'] as String?, + updatedAt: json['updated_at'] as String?, + seen: json['seen'] as bool?, + seenAt: (json['seen_at'] as num?)?.toInt(), + isScheduled: json['is_scheduled'] as bool?, + scheduledAt: (json['scheduled_at'] as num?)?.toInt(), + ); + +Map _$NotificationItemToJson(_NotificationItem instance) => + { + 'id': instance.id, + 'user_id': instance.userId, + 'title': instance.title, + 'message': instance.message, + 'link': instance.link, + 'image': instance.image, + 'priority': instance.priority, + 'type': instance.type, + 'event_type': instance.eventType, + 'status': instance.status, + 'methods': instance.methods, + 'metadata': instance.metadata, + 'schedule_at': instance.scheduleAt, + 'created_at': instance.createdAt, + 'updated_at': instance.updatedAt, + 'seen': instance.seen, + 'seen_at': instance.seenAt, + 'is_scheduled': instance.isScheduled, + 'scheduled_at': instance.scheduledAt, + }; diff --git a/lib/data/services/model/tenders/tenders_data/tenders_data.dart b/lib/data/services/model/tenders/tenders_data/tenders_data.dart index f3f0082..35ba095 100644 --- a/lib/data/services/model/tenders/tenders_data/tenders_data.dart +++ b/lib/data/services/model/tenders/tenders_data/tenders_data.dart @@ -2,7 +2,6 @@ import 'package:freezed_annotation/freezed_annotation.dart'; -import '../../meta/meta.dart'; import '../../tender_data/tender_data.dart'; part 'tenders_data.freezed.dart'; diff --git a/lib/data/services/notification_service.dart b/lib/data/services/notification_service.dart new file mode 100644 index 0000000..8c15908 --- /dev/null +++ b/lib/data/services/notification_service.dart @@ -0,0 +1,48 @@ +// notification_service.dart +import 'dart:convert'; +import 'package:tm_app/data/services/api/notification_api.dart'; +import 'package:tm_app/data/services/model/notification__response/notification_response_model.dart'; + +import 'package:tm_app/core/utils/result.dart'; +import '../../core/network/network_manager.dart'; + +class NotificationsService { + NotificationsService({required NetworkManager networkManager}) + : _networkManager = networkManager; + final NetworkManager _networkManager; + + Future> getNotifications({ + required int limit, + required int offset, + }) async { + final result = await _networkManager.makeRequest( + NotificationApi.getNotifications(limit: limit, offset: offset), + //NotificationApi.notifications, + method: 'GET', + (json) => NotificationResponseModel.fromJson(json), + ); + return result; + } + + Future> markAllAsRead() async { + final result = await _networkManager.makeRequest( + NotificationApi.markAllAsRead, + method: 'POST', + (json) => NotificationResponseModel.fromJson(json), + ); + return result; + } + + Future> markAsRead({ + required String notificationId, + }) async { + final data = jsonEncode({'notification_id': notificationId}); + final result = await _networkManager.makeRequest( + NotificationApi.markAsRead, + method: 'POST', + (json) => NotificationResponseModel.fromJson(json), + data: data, + ); + return result; + } +} diff --git a/lib/view_models/notification_view_model.dart b/lib/view_models/notification_view_model.dart new file mode 100644 index 0000000..a5d84d0 --- /dev/null +++ b/lib/view_models/notification_view_model.dart @@ -0,0 +1,132 @@ +import 'package:flutter/material.dart'; +import 'package:tm_app/core/utils/result.dart'; +import 'package:tm_app/data/repositories/notification_repository.dart'; +import 'package:tm_app/data/services/model/notification__response/notification_response_model.dart'; + +class NotificationViewModel with ChangeNotifier { + final NotificationsRepository _repository; + + NotificationViewModel({required NotificationsRepository repositoryViewModel}) + : _repository = repositoryViewModel { + WidgetsBinding.instance.addPostFrameCallback((_) { + getNotifications(); + }); + } + + bool _isLoading = false; + String? _errorMessage; + NotificationResponseModel? _notificationResponseModel; + + int currentPage = 1; + static const int _pageSize = 10; + int _totalPages = 0; + bool _hasMoreData = true; + + bool get isLoading => _isLoading; + String? get errorMessage => _errorMessage; + NotificationResponseModel? get notificationResponse => + _notificationResponseModel; + bool get hasMoreData => _hasMoreData; + int get totalPages => _totalPages; + + /// Fetch notifications +Future getNotifications({int page = 1, bool isMobile = false}) async { + _isLoading = true; + notifyListeners(); + + final int offset = _pageSize * page; + + final result = await _repository.getNotifications( + limit: _pageSize, + offset: offset, + ); + + switch (result) { + case Ok(): + if (isMobile && page > 1) { + final oldNotifications = + _notificationResponseModel?.data?.notifications ?? []; + + final newNotifications = result.value.data?.notifications ?? []; + + _notificationResponseModel = result.value.copyWith( + data: result.value.data?.copyWith( + notifications: [...oldNotifications, ...newNotifications], + ), + ); + } else { + _notificationResponseModel = result.value; + } + + currentPage = page; + _hasMoreData = + (result.value.data?.notifications?.length ?? 0) >= _pageSize; + _totalPages = result.value.data?.meta?.pages ?? 1; + break; + + case Error(): + _errorMessage = result.error.toString(); + break; + } + + _isLoading = false; + notifyListeners(); +} + + /// Jump to page + Future jumpToPage(int page) async { + if (_totalPages == 0 || page < 1 || page > _totalPages) { + return; + } + await getNotifications(page: page); + } + + /// Mark all as read + Future markAllAsRead() async { + _errorMessage = null; + notifyListeners(); + + final result = await _repository.markAllAsRead(); + + switch (result) { + case Ok(): + if (_notificationResponseModel?.data?.notifications != null) { + final updatedNotifications = + _notificationResponseModel!.data!.notifications! + .map((n) => n.copyWith(seen: true)) + .toList(); + + _notificationResponseModel = _notificationResponseModel!.copyWith( + data: _notificationResponseModel!.data!.copyWith( + notifications: updatedNotifications, + ), + ); + } + break; + + case Error(): + _errorMessage = result.error.toString(); + break; + } + + notifyListeners(); + } + + String timeAgo(String dateTimeString) { + final createdAt = DateTime.parse(dateTimeString); + final now = DateTime.now(); + final difference = now.difference(createdAt); + + if (difference.inMinutes < 1) { + return 'Now'; + } else if (difference.inMinutes < 60) { + return '${difference.inMinutes} Min '; + } else if (difference.inHours < 24) { + return '${difference.inHours} Hour '; + } else if (difference.inDays == 1) { + return 'Yesterday'; + } else { + return '${difference.inDays} days ago'; + } + } +} diff --git a/lib/views/notification/pages/d_notification_page.dart b/lib/views/notification/pages/d_notification_page.dart index e05ff43..06df345 100644 --- a/lib/views/notification/pages/d_notification_page.dart +++ b/lib/views/notification/pages/d_notification_page.dart @@ -1,16 +1,15 @@ import 'package:flutter/material.dart'; -import 'package:flutter_svg/svg.dart'; -import 'package:tm_app/core/constants/assets.dart'; +import 'package:provider/provider.dart'; import 'package:tm_app/core/theme/colors.dart'; import 'package:tm_app/core/utils/size_config.dart'; +import 'package:tm_app/view_models/notification_view_model.dart'; import 'package:tm_app/views/notification/strings/notification_strings.dart'; import 'package:tm_app/views/shared/page_selection_dialog.dart'; import 'package:tm_app/views/shared/desktop_navigation_widget.dart'; - -import '../../shared/main_tab_bar.dart'; import '../widgets/notification_all_tab.dart'; import '../widgets/notification_important_tab.dart'; import '../widgets/notification_unread_tab.dart'; +import '../../shared/main_tab_bar.dart'; class DesktopNotificationPage extends StatefulWidget { const DesktopNotificationPage({super.key}); @@ -38,14 +37,14 @@ class _DesktopNotificationPageState extends State @override Widget build(BuildContext context) { + final viewModel = context.watch(); + return Scaffold( backgroundColor: AppColors.backgroundColor, body: Column( children: [ const DesktopNavigationWidget(currentIndex: 3), - SizedBox(height: 60.0.h()), - Expanded( child: Center( child: SizedBox( @@ -60,15 +59,13 @@ class _DesktopNotificationPageState extends State NotificationStrings.important, ], ), - SizedBox(height: 12.0.h()), - Padding( padding: EdgeInsetsDirectional.only(end: 24.0.w()), child: Align( alignment: Alignment.centerRight, child: InkWell( - onTap: () {}, + onTap: viewModel.markAllAsRead, borderRadius: BorderRadius.circular(99), child: Container( width: 132.0.w(), @@ -119,14 +116,14 @@ class _DesktopNotificationPageState extends State onTap: () async { final selectedPage = await showDialog( context: context, - builder: - (context) => - const PageSelectionDialog(totalPages: 10), + builder: (context) => PageSelectionDialog( + totalPages: viewModel.totalPages), ); - if (selectedPage != null) {} + if (selectedPage != null) { + viewModel.jumpToPage(selectedPage); + } }, - child: Container( decoration: BoxDecoration( border: Border.all( @@ -139,7 +136,7 @@ class _DesktopNotificationPageState extends State children: [ SizedBox(width: 5.0.w()), Text( - NotificationStrings.currentPage, + viewModel.currentPage.toString(), style: TextStyle( fontSize: 14.0.sp(), fontWeight: FontWeight.w500, @@ -147,12 +144,9 @@ class _DesktopNotificationPageState extends State ), ), SizedBox(width: 5.0.w()), - SvgPicture.asset( - AssetsManager.arrowDownSmall, - colorFilter: ColorFilter.mode( - AppColors.grey80, - BlendMode.srcIn, - ), + Icon( + Icons.arrow_drop_down, + color: AppColors.grey80, ), SizedBox(width: 5.0.w()), ], @@ -170,7 +164,7 @@ class _DesktopNotificationPageState extends State ), SizedBox(width: 5.0.w()), Text( - NotificationStrings.totalPages, + viewModel.totalPages.toString(), style: TextStyle( fontSize: 13.0.sp(), fontWeight: FontWeight.w400, @@ -179,7 +173,6 @@ class _DesktopNotificationPageState extends State ), ], ), - SizedBox(height: 30.0.h()), ], ), diff --git a/lib/views/notification/pages/m_notification_page.dart b/lib/views/notification/pages/m_notification_page.dart index 5a517f6..cb8f40b 100644 --- a/lib/views/notification/pages/m_notification_page.dart +++ b/lib/views/notification/pages/m_notification_page.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import 'package:tm_app/core/theme/colors.dart'; import 'package:tm_app/core/utils/size_config.dart'; +import 'package:tm_app/view_models/notification_view_model.dart'; import 'package:tm_app/views/notification/strings/notification_strings.dart'; import 'package:tm_app/views/shared/tender_app_bar.dart'; @@ -19,21 +21,38 @@ class MobileNotificationPage extends StatefulWidget { class _MobileNotificationPageState extends State with SingleTickerProviderStateMixin { late TabController controller; + final ScrollController _scrollController = ScrollController(); @override void initState() { super.initState(); controller = TabController(length: 3, vsync: this); + + _scrollController.addListener(() { + final viewModel = context.read(); + if (_scrollController.position.pixels >= + _scrollController.position.maxScrollExtent - 100 && + !viewModel.isLoading && + viewModel.hasMoreData) { + viewModel.getNotifications( + page: viewModel.currentPage + 1, + isMobile: true, + ); + } + }); } @override void dispose() { controller.dispose(); + _scrollController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { + final viewModel = context.watch(); // ✅ وصل به ویومدل + return Scaffold( backgroundColor: AppColors.backgroundColor, appBar: tenderMobileAppBar(title: NotificationStrings.notificationTitle), @@ -53,7 +72,7 @@ class _MobileNotificationPageState extends State child: Align( alignment: Alignment.centerRight, child: InkWell( - onTap: () {}, + onTap: viewModel.markAllAsRead, // ✅ مثل دسکتاپ/تبلت borderRadius: BorderRadius.circular(99), child: Container( width: 132.0.w(), @@ -80,13 +99,18 @@ class _MobileNotificationPageState extends State Expanded( child: TabBarView( controller: controller, - children: const [ - NotificationAllTab(), - NotificationUnreadTab(), - NotificationImportantTab(), + children: [ + NotificationAllTab(scrollController: _scrollController), + NotificationUnreadTab(scrollController: _scrollController), + NotificationImportantTab(scrollController: _scrollController), ], ), ), + if (viewModel.isLoading) ...[ + SizedBox(height: 10.0.h()), + const Center(child: CircularProgressIndicator()), + SizedBox(height: 10.0.h()), + ], ], ), ); diff --git a/lib/views/notification/pages/notification_screen.dart b/lib/views/notification/pages/notification_screen.dart index d1faf3b..bba8da0 100644 --- a/lib/views/notification/pages/notification_screen.dart +++ b/lib/views/notification/pages/notification_screen.dart @@ -19,49 +19,49 @@ class NotificationScreen extends StatelessWidget { } } -class NotificationModel { - final String title; - final String description; - final String time; - final String type; +// class NotificationModel { +// final String title; +// final String description; +// final String time; +// final String type; - NotificationModel({ - required this.title, - required this.description, - required this.time, - required this.type, - }); -} +// NotificationModel({ +// required this.title, +// required this.description, +// required this.time, +// required this.type, +// }); +// } -final List notifications = [ - NotificationModel( - title: 'New Tender Invitation', - description: 'You’ve been invited to tender #A-778.', - time: '5 Min', - type: 'new', - ), - NotificationModel( - title: 'New Tender Invitation', - description: 'You’ve been invited to tender #N-3762.', - time: '28 Min', - type: 'new', - ), - NotificationModel( - title: 'Tender Submitted', - description: 'You submitted bid for tender #B-554.', - time: '28 Min', - type: 'submit', - ), - NotificationModel( - title: 'New Tender Invitation', - description: 'You’ve been invited to tender #N-3762.', - time: '28 Min', - type: 'new', - ), - NotificationModel( - title: 'Missing Documents', - description: 'Please upload your company registration certificate.', - time: '2 days ago', - type: 'missing', - ), -]; +// final List notifications = [ +// NotificationModel( +// title: 'New Tender Invitation', +// description: 'You’ve been invited to tender #A-778.', +// time: '5 Min', +// type: 'new', +// ), +// NotificationModel( +// title: 'New Tender Invitation', +// description: 'You’ve been invited to tender #N-3762.', +// time: '28 Min', +// type: 'new', +// ), +// NotificationModel( +// title: 'Tender Submitted', +// description: 'You submitted bid for tender #B-554.', +// time: '28 Min', +// type: 'submit', +// ), +// NotificationModel( +// title: 'New Tender Invitation', +// description: 'You’ve been invited to tender #N-3762.', +// time: '28 Min', +// type: 'new', +// ), +// NotificationModel( +// title: 'Missing Documents', +// description: 'Please upload your company registration certificate.', +// time: '2 days ago', +// type: 'missing', +// ), +// ]; diff --git a/lib/views/notification/pages/t_notification_page.dart b/lib/views/notification/pages/t_notification_page.dart index 09185b7..e2ba926 100644 --- a/lib/views/notification/pages/t_notification_page.dart +++ b/lib/views/notification/pages/t_notification_page.dart @@ -1,8 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:provider/provider.dart'; import 'package:tm_app/core/constants/assets.dart'; import 'package:tm_app/core/theme/colors.dart'; import 'package:tm_app/core/utils/size_config.dart'; +import 'package:tm_app/view_models/notification_view_model.dart'; import 'package:tm_app/views/notification/strings/notification_strings.dart'; import 'package:tm_app/views/shared/page_selection_dialog.dart'; import 'package:tm_app/views/shared/tablet_navigation_widget.dart'; @@ -38,7 +40,9 @@ class _TabletNotificationPageState extends State @override Widget build(BuildContext context) { + final viewModel = context.watch(); final GlobalKey key = GlobalKey(); + return Scaffold( key: key, backgroundColor: AppColors.backgroundColor, @@ -66,7 +70,7 @@ class _TabletNotificationPageState extends State child: Align( alignment: Alignment.centerRight, child: InkWell( - onTap: () {}, + onTap: viewModel.markAllAsRead, borderRadius: BorderRadius.circular(99), child: Container( width: 132.0.w(), @@ -101,7 +105,6 @@ class _TabletNotificationPageState extends State ), ), SizedBox(height: 10.0.h()), - Row( children: [ const Spacer(), @@ -118,12 +121,14 @@ class _TabletNotificationPageState extends State onTap: () async { final selectedPage = await showDialog( context: context, - builder: - (context) => - const PageSelectionDialog(totalPages: 10), + builder: (context) => PageSelectionDialog( + totalPages: viewModel.totalPages, + ), ); - if (selectedPage != null) {} + if (selectedPage != null) { + viewModel.jumpToPage(selectedPage); + } }, child: Container( decoration: BoxDecoration( @@ -134,7 +139,7 @@ class _TabletNotificationPageState extends State children: [ SizedBox(width: 5.0.w()), Text( - NotificationStrings.currentPage, + viewModel.currentPage.toString(), style: TextStyle( fontSize: 14.0.sp(), fontWeight: FontWeight.w500, @@ -165,7 +170,7 @@ class _TabletNotificationPageState extends State ), SizedBox(width: 5.0.w()), Text( - NotificationStrings.totalPages, + viewModel.totalPages.toString(), style: TextStyle( fontSize: 13.0.sp(), fontWeight: FontWeight.w400, @@ -174,7 +179,6 @@ class _TabletNotificationPageState extends State ), ], ), - SizedBox(height: 30.0.h()), ], ), diff --git a/lib/views/notification/widgets/notification_all_tab.dart b/lib/views/notification/widgets/notification_all_tab.dart index 6a1b1d0..6d52fca 100644 --- a/lib/views/notification/widgets/notification_all_tab.dart +++ b/lib/views/notification/widgets/notification_all_tab.dart @@ -1,20 +1,27 @@ import 'package:flutter/material.dart'; -import 'package:tm_app/core/utils/size_config.dart'; +import 'package:provider/provider.dart'; +import 'package:tm_app/view_models/notification_view_model.dart'; -import '../pages/notification_screen.dart'; import 'notification_card.dart'; class NotificationAllTab extends StatelessWidget { - const NotificationAllTab({super.key}); + const NotificationAllTab({this.scrollController, super.key}); + + final ScrollController? scrollController; @override Widget build(BuildContext context) { + final viewModel = context.watch(); + final notifications = + viewModel.notificationResponse?.data?.notifications ?? []; + return ListView.builder( + controller: scrollController, itemCount: notifications.length, itemBuilder: (context, index) { final notification = notifications[index]; return Padding( - padding: EdgeInsets.only(bottom: 16.0.h()), + padding: const EdgeInsets.only(bottom: 16.0), child: NotificationCard(notification: notification), ); }, diff --git a/lib/views/notification/widgets/notification_card.dart b/lib/views/notification/widgets/notification_card.dart index 192ebaf..7b65d94 100644 --- a/lib/views/notification/widgets/notification_card.dart +++ b/lib/views/notification/widgets/notification_card.dart @@ -1,93 +1,90 @@ import 'package:flutter/material.dart'; -import 'package:flutter_svg/svg.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:provider/provider.dart'; import 'package:tm_app/core/constants/assets.dart'; import 'package:tm_app/core/theme/colors.dart'; import 'package:tm_app/core/utils/size_config.dart'; - -import '../pages/notification_screen.dart'; +import 'package:tm_app/data/services/model/notification_data/notification_data.dart'; +import 'package:tm_app/view_models/notification_view_model.dart'; class NotificationCard extends StatelessWidget { + final NotificationItem notification; const NotificationCard({required this.notification, super.key}); - final NotificationModel notification; @override Widget build(BuildContext context) { + final viewModel = context.read(); return Container( width: double.infinity, - // height: 108.0.h(), margin: EdgeInsets.symmetric(horizontal: 24.0.w()), - padding: EdgeInsets.symmetric(horizontal: 16.0.w(), vertical: 16.0.h()), + padding: const EdgeInsets.all(16.0), decoration: BoxDecoration( color: - notification.title == 'Tender Submitted' + notification.type == 'reject' ? AppColors.orange10 - : notification.title == 'New Tender Invitation' - ? AppColors.mainBlue.withValues(alpha: 0.03) + : notification.type == 'info' + ? AppColors.primary10Light : AppColors.green0, borderRadius: BorderRadius.circular(4), border: Border.all( color: - notification.title == 'Tender Submitted' + notification.type! == 'reject' ? AppColors.warningColor - : notification.title == 'New Tender Invitation' - ? AppColors.mainBlue.withValues(alpha: 0.15) - : AppColors.green20, + : notification.type == 'info' ? AppColors.primary20 : AppColors.green20, ), ), - child: Column( + child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( + Padding( + padding: EdgeInsets.only(top: 3.0.h()), + child: SvgPicture.asset( + notification.type! == 'info' + ? AssetsManager.notification + : notification.type! == 'reject' + ? AssetsManager.danger + : AssetsManager.tickCircle, + ), + ), + SizedBox(width: 5.0.w()), + Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - SvgPicture.asset( - notification.title == 'Tender Submitted' - ? AssetsManager.danger - : notification.title == 'New Tender Invitation' - ? AssetsManager.notification - : AssetsManager.tickCircle, - ), - SizedBox(width: 8.0.w()), Text( - notification.title, + notification.title!, style: TextStyle( fontSize: 16.0.sp(), - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, color: AppColors.grey80, ), ), - ], - ), - SizedBox(height: 8.0.h()), - Padding( - padding: EdgeInsetsDirectional.only(start: 28.0.w()), - child: Text( - notification.description, - maxLines: 1, - overflow: TextOverflow.ellipsis, - textAlign: TextAlign.start, - style: TextStyle( - fontSize: 14.0.sp(), - fontWeight: FontWeight.w400, - color: AppColors.grey60, - ), - ), - ), - SizedBox(height: 8.0.h()), - Padding( - padding: EdgeInsetsDirectional.only(start: 28.0.w()), - child: Row( - children: [ - SvgPicture.asset(AssetsManager.calendar), - SizedBox(width: 4.0.w()), - Text( - notification.time, - style: TextStyle( - fontSize: 14.0.sp(), - color: AppColors.grey60, - ), + SizedBox(height: 8.0.h()), + Text( + notification.message!, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 14.0.sp(), + fontWeight: FontWeight.w400, + color: AppColors.grey70, ), - ], - ), + ), + SizedBox(height: 8.0.h()), + Row( + children: [ + SvgPicture.asset(AssetsManager.calendar), + SizedBox(width: 4.0.w()), + Text( + viewModel.timeAgo(notification.createdAt!), + style: TextStyle( + fontSize: 12.0.sp(), + fontWeight: FontWeight.w400, + color: AppColors.grey60, + ), + ), + ], + ), + ], ), ], ), diff --git a/lib/views/notification/widgets/notification_important_tab.dart b/lib/views/notification/widgets/notification_important_tab.dart index 5323cc1..840b883 100644 --- a/lib/views/notification/widgets/notification_important_tab.dart +++ b/lib/views/notification/widgets/notification_important_tab.dart @@ -1,21 +1,34 @@ import 'package:flutter/material.dart'; -import 'package:tm_app/core/utils/size_config.dart'; +import 'package:provider/provider.dart'; +import 'package:tm_app/view_models/notification_view_model.dart'; -import '../pages/notification_screen.dart'; import 'notification_card.dart'; class NotificationImportantTab extends StatelessWidget { - const NotificationImportantTab({super.key}); + const NotificationImportantTab({this.scrollController, super.key}); + + final ScrollController? scrollController; @override Widget build(BuildContext context) { + final viewModel = context.watch(); + final allNotifications = + viewModel.notificationResponse?.data?.notifications ?? []; + + final importantNotifications = allNotifications + .where((n) => n.priority?.toLowerCase() == 'important') + .toList(); + return ListView.builder( - itemCount: notifications.length, + controller: scrollController, + itemCount: importantNotifications.length, itemBuilder: (context, index) { - final notification = notifications[index]; + final notification = importantNotifications[index]; return Padding( - padding: EdgeInsets.only(bottom: 16.0.h()), - child: NotificationCard(notification: notification), + padding: const EdgeInsets.only(bottom: 16.0), + child: NotificationCard( + notification: notification, + ), ); }, ); diff --git a/lib/views/notification/widgets/notification_unread_tab.dart b/lib/views/notification/widgets/notification_unread_tab.dart index 452fb3a..b5275d3 100644 --- a/lib/views/notification/widgets/notification_unread_tab.dart +++ b/lib/views/notification/widgets/notification_unread_tab.dart @@ -1,14 +1,19 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import 'package:tm_app/core/utils/size_config.dart'; +import 'package:tm_app/view_models/notification_view_model.dart'; -import '../pages/notification_screen.dart'; import 'notification_card.dart'; class NotificationUnreadTab extends StatelessWidget { - const NotificationUnreadTab({super.key}); + final ScrollController? scrollController; + const NotificationUnreadTab({this.scrollController, super.key}); @override Widget build(BuildContext context) { + final viewModel = context.watch(); + final notifications = + viewModel.notificationResponse?.data?.notifications ?? []; return ListView.builder( itemCount: notifications.length, itemBuilder: (context, index) {