From 26a29003d9413e529ee1bccc2cda36747d269132 Mon Sep 17 00:00:00 2001 From: amirrezaghabeli Date: Sat, 27 Sep 2025 15:31:40 +0330 Subject: [PATCH 1/3] notification se more added --- .../services/model/metadata/metadata.dart | 14 + .../model/metadata/metadata.freezed.dart | 277 ++++++++++++++++++ .../services/model/metadata/metadata.g.dart | 14 + .../notification_response_model.dart | 4 +- .../notification_response_model.freezed.dart | 68 +++-- .../notification_response_model.g.dart | 5 +- .../strings/notification_strings.dart | 1 + .../widgets/notification_card.dart | 13 +- .../widgets/notification_more_dialog.dart | 121 ++++++++ 9 files changed, 487 insertions(+), 30 deletions(-) create mode 100644 lib/data/services/model/metadata/metadata.dart create mode 100644 lib/data/services/model/metadata/metadata.freezed.dart create mode 100644 lib/data/services/model/metadata/metadata.g.dart create mode 100644 lib/views/notification/widgets/notification_more_dialog.dart diff --git a/lib/data/services/model/metadata/metadata.dart b/lib/data/services/model/metadata/metadata.dart new file mode 100644 index 0000000..886c354 --- /dev/null +++ b/lib/data/services/model/metadata/metadata.dart @@ -0,0 +1,14 @@ +// ignore_for_file: invalid_annotation_target + +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'metadata.freezed.dart'; +part 'metadata.g.dart'; + +@freezed +abstract class Metadata with _$Metadata { + const factory Metadata({required String? tender}) = _Metadata; + + factory Metadata.fromJson(Map json) => + _$MetadataFromJson(json); +} diff --git a/lib/data/services/model/metadata/metadata.freezed.dart b/lib/data/services/model/metadata/metadata.freezed.dart new file mode 100644 index 0000000..cf3d47b --- /dev/null +++ b/lib/data/services/model/metadata/metadata.freezed.dart @@ -0,0 +1,277 @@ +// 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 'metadata.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$Metadata { + + String? get tender; +/// Create a copy of Metadata +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$MetadataCopyWith get copyWith => _$MetadataCopyWithImpl(this as Metadata, _$identity); + + /// Serializes this Metadata to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is Metadata&&(identical(other.tender, tender) || other.tender == tender)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,tender); + +@override +String toString() { + return 'Metadata(tender: $tender)'; +} + + +} + +/// @nodoc +abstract mixin class $MetadataCopyWith<$Res> { + factory $MetadataCopyWith(Metadata value, $Res Function(Metadata) _then) = _$MetadataCopyWithImpl; +@useResult +$Res call({ + String? tender +}); + + + + +} +/// @nodoc +class _$MetadataCopyWithImpl<$Res> + implements $MetadataCopyWith<$Res> { + _$MetadataCopyWithImpl(this._self, this._then); + + final Metadata _self; + final $Res Function(Metadata) _then; + +/// Create a copy of Metadata +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? tender = freezed,}) { + return _then(_self.copyWith( +tender: freezed == tender ? _self.tender : tender // ignore: cast_nullable_to_non_nullable +as String?, + )); +} + +} + + +/// Adds pattern-matching-related methods to [Metadata]. +extension MetadataPatterns on Metadata { +/// 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( _Metadata value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _Metadata() 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( _Metadata value) $default,){ +final _that = this; +switch (_that) { +case _Metadata(): +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( _Metadata value)? $default,){ +final _that = this; +switch (_that) { +case _Metadata() 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? tender)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _Metadata() when $default != null: +return $default(_that.tender);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? tender) $default,) {final _that = this; +switch (_that) { +case _Metadata(): +return $default(_that.tender);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? tender)? $default,) {final _that = this; +switch (_that) { +case _Metadata() when $default != null: +return $default(_that.tender);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _Metadata implements Metadata { + const _Metadata({required this.tender}); + factory _Metadata.fromJson(Map json) => _$MetadataFromJson(json); + +@override final String? tender; + +/// Create a copy of Metadata +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$MetadataCopyWith<_Metadata> get copyWith => __$MetadataCopyWithImpl<_Metadata>(this, _$identity); + +@override +Map toJson() { + return _$MetadataToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _Metadata&&(identical(other.tender, tender) || other.tender == tender)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,tender); + +@override +String toString() { + return 'Metadata(tender: $tender)'; +} + + +} + +/// @nodoc +abstract mixin class _$MetadataCopyWith<$Res> implements $MetadataCopyWith<$Res> { + factory _$MetadataCopyWith(_Metadata value, $Res Function(_Metadata) _then) = __$MetadataCopyWithImpl; +@override @useResult +$Res call({ + String? tender +}); + + + + +} +/// @nodoc +class __$MetadataCopyWithImpl<$Res> + implements _$MetadataCopyWith<$Res> { + __$MetadataCopyWithImpl(this._self, this._then); + + final _Metadata _self; + final $Res Function(_Metadata) _then; + +/// Create a copy of Metadata +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? tender = freezed,}) { + return _then(_Metadata( +tender: freezed == tender ? _self.tender : tender // ignore: cast_nullable_to_non_nullable +as String?, + )); +} + + +} + +// dart format on diff --git a/lib/data/services/model/metadata/metadata.g.dart b/lib/data/services/model/metadata/metadata.g.dart new file mode 100644 index 0000000..1eb8390 --- /dev/null +++ b/lib/data/services/model/metadata/metadata.g.dart @@ -0,0 +1,14 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'metadata.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_Metadata _$MetadataFromJson(Map json) => + _Metadata(tender: json['tender'] as String?); + +Map _$MetadataToJson(_Metadata instance) => { + 'tender': instance.tender, +}; diff --git a/lib/data/services/model/notification__response/notification_response_model.dart b/lib/data/services/model/notification__response/notification_response_model.dart index 94a5b0a..c79620a 100644 --- a/lib/data/services/model/notification__response/notification_response_model.dart +++ b/lib/data/services/model/notification__response/notification_response_model.dart @@ -5,6 +5,8 @@ import 'package:tm_app/data/services/model/error/error_model.dart'; import 'package:tm_app/data/services/model/meta/meta.dart'; import 'package:tm_app/data/services/model/notification_data/notification_recipient.dart'; +import '../metadata/metadata.dart'; + part 'notification_response_model.freezed.dart'; part 'notification_response_model.g.dart'; @@ -37,7 +39,7 @@ abstract class NotificationItem with _$NotificationItem { @JsonKey(name: 'event_type') required String? eventType, required String? status, required Map? methods, - required Map? metadata, + required Metadata? metadata, @JsonKey(name: 'schedule_at') required int? scheduleAt, @JsonKey(name: 'created_at') required int? createdAt, @JsonKey(name: 'updated_at') required int? updatedAt, 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 index 22ef060..1142b1e 100644 --- a/lib/data/services/model/notification__response/notification_response_model.freezed.dart +++ b/lib/data/services/model/notification__response/notification_response_model.freezed.dart @@ -346,8 +346,7 @@ $MetaCopyWith<$Res>? get meta { /// @nodoc mixin _$NotificationItem { - String? get id;@JsonKey(name: 'user_id') String? get userId; Recipient? get recipient;// ✅ اضافه شد - 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') int? get createdAt;@JsonKey(name: 'updated_at') int? 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; + String? get id;@JsonKey(name: 'user_id') String? get userId; Recipient? get recipient; 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; Metadata? get metadata;@JsonKey(name: 'schedule_at') int? get scheduleAt;@JsonKey(name: 'created_at') int? get createdAt;@JsonKey(name: 'updated_at') int? 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) @@ -360,12 +359,12 @@ $NotificationItemCopyWith get copyWith => _$NotificationItemCo @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.recipient, recipient) || other.recipient == recipient)&&(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)); + 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.recipient, recipient) || other.recipient == recipient)&&(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)&&(identical(other.metadata, metadata) || 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,recipient,title,message,link,image,priority,type,eventType,status,const DeepCollectionEquality().hash(methods),const DeepCollectionEquality().hash(metadata),scheduleAt,createdAt,updatedAt,seen,seenAt,isScheduled,scheduledAt]); +int get hashCode => Object.hashAll([runtimeType,id,userId,recipient,title,message,link,image,priority,type,eventType,status,const DeepCollectionEquality().hash(methods),metadata,scheduleAt,createdAt,updatedAt,seen,seenAt,isScheduled,scheduledAt]); @override String toString() { @@ -380,11 +379,11 @@ 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, Recipient? recipient, 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') int? createdAt,@JsonKey(name: 'updated_at') int? updatedAt, bool? seen,@JsonKey(name: 'seen_at') int? seenAt,@JsonKey(name: 'is_scheduled') bool? isScheduled,@JsonKey(name: 'scheduled_at') int? scheduledAt + String? id,@JsonKey(name: 'user_id') String? userId, Recipient? recipient, String? title, String? message, String? link, String? image, String? priority, String? type,@JsonKey(name: 'event_type') String? eventType, String? status, Map? methods, Metadata? metadata,@JsonKey(name: 'schedule_at') int? scheduleAt,@JsonKey(name: 'created_at') int? createdAt,@JsonKey(name: 'updated_at') int? updatedAt, bool? seen,@JsonKey(name: 'seen_at') int? seenAt,@JsonKey(name: 'is_scheduled') bool? isScheduled,@JsonKey(name: 'scheduled_at') int? scheduledAt }); -$RecipientCopyWith<$Res>? get recipient; +$RecipientCopyWith<$Res>? get recipient;$MetadataCopyWith<$Res>? get metadata; } /// @nodoc @@ -412,7 +411,7 @@ as String?,eventType: freezed == eventType ? _self.eventType : eventType // igno 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 Metadata?,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 int?,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable as int?,seen: freezed == seen ? _self.seen : seen // ignore: cast_nullable_to_non_nullable @@ -434,6 +433,18 @@ $RecipientCopyWith<$Res>? get recipient { return $RecipientCopyWith<$Res>(_self.recipient!, (value) { return _then(_self.copyWith(recipient: value)); }); +}/// Create a copy of NotificationItem +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$MetadataCopyWith<$Res>? get metadata { + if (_self.metadata == null) { + return null; + } + + return $MetadataCopyWith<$Res>(_self.metadata!, (value) { + return _then(_self.copyWith(metadata: value)); + }); } } @@ -516,7 +527,7 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult maybeWhen(TResult Function( String? id, @JsonKey(name: 'user_id') String? userId, Recipient? recipient, 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') int? createdAt, @JsonKey(name: 'updated_at') int? 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; +@optionalTypeArgs TResult maybeWhen(TResult Function( String? id, @JsonKey(name: 'user_id') String? userId, Recipient? recipient, String? title, String? message, String? link, String? image, String? priority, String? type, @JsonKey(name: 'event_type') String? eventType, String? status, Map? methods, Metadata? metadata, @JsonKey(name: 'schedule_at') int? scheduleAt, @JsonKey(name: 'created_at') int? createdAt, @JsonKey(name: 'updated_at') int? 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.recipient,_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 _: @@ -537,7 +548,7 @@ return $default(_that.id,_that.userId,_that.recipient,_that.title,_that.message, /// } /// ``` -@optionalTypeArgs TResult when(TResult Function( String? id, @JsonKey(name: 'user_id') String? userId, Recipient? recipient, 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') int? createdAt, @JsonKey(name: 'updated_at') int? 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; +@optionalTypeArgs TResult when(TResult Function( String? id, @JsonKey(name: 'user_id') String? userId, Recipient? recipient, String? title, String? message, String? link, String? image, String? priority, String? type, @JsonKey(name: 'event_type') String? eventType, String? status, Map? methods, Metadata? metadata, @JsonKey(name: 'schedule_at') int? scheduleAt, @JsonKey(name: 'created_at') int? createdAt, @JsonKey(name: 'updated_at') int? 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.recipient,_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 _: @@ -557,7 +568,7 @@ return $default(_that.id,_that.userId,_that.recipient,_that.title,_that.message, /// } /// ``` -@optionalTypeArgs TResult? whenOrNull(TResult? Function( String? id, @JsonKey(name: 'user_id') String? userId, Recipient? recipient, 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') int? createdAt, @JsonKey(name: 'updated_at') int? 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; +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String? id, @JsonKey(name: 'user_id') String? userId, Recipient? recipient, String? title, String? message, String? link, String? image, String? priority, String? type, @JsonKey(name: 'event_type') String? eventType, String? status, Map? methods, Metadata? metadata, @JsonKey(name: 'schedule_at') int? scheduleAt, @JsonKey(name: 'created_at') int? createdAt, @JsonKey(name: 'updated_at') int? 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.recipient,_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 _: @@ -572,13 +583,12 @@ return $default(_that.id,_that.userId,_that.recipient,_that.title,_that.message, @JsonSerializable() class _NotificationItem implements NotificationItem { - const _NotificationItem({required this.id, @JsonKey(name: 'user_id') required this.userId, required this.recipient, 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; + const _NotificationItem({required this.id, @JsonKey(name: 'user_id') required this.userId, required this.recipient, 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 this.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; factory _NotificationItem.fromJson(Map json) => _$NotificationItemFromJson(json); @override final String? id; @override@JsonKey(name: 'user_id') final String? userId; @override final Recipient? recipient; -// ✅ اضافه شد @override final String? title; @override final String? message; @override final String? link; @@ -596,15 +606,7 @@ class _NotificationItem implements NotificationItem { 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 final Metadata? metadata; @override@JsonKey(name: 'schedule_at') final int? scheduleAt; @override@JsonKey(name: 'created_at') final int? createdAt; @override@JsonKey(name: 'updated_at') final int? updatedAt; @@ -626,12 +628,12 @@ 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.recipient, recipient) || other.recipient == recipient)&&(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)); + 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.recipient, recipient) || other.recipient == recipient)&&(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)&&(identical(other.metadata, metadata) || 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,recipient,title,message,link,image,priority,type,eventType,status,const DeepCollectionEquality().hash(_methods),const DeepCollectionEquality().hash(_metadata),scheduleAt,createdAt,updatedAt,seen,seenAt,isScheduled,scheduledAt]); +int get hashCode => Object.hashAll([runtimeType,id,userId,recipient,title,message,link,image,priority,type,eventType,status,const DeepCollectionEquality().hash(_methods),metadata,scheduleAt,createdAt,updatedAt,seen,seenAt,isScheduled,scheduledAt]); @override String toString() { @@ -646,11 +648,11 @@ abstract mixin class _$NotificationItemCopyWith<$Res> implements $NotificationIt factory _$NotificationItemCopyWith(_NotificationItem value, $Res Function(_NotificationItem) _then) = __$NotificationItemCopyWithImpl; @override @useResult $Res call({ - String? id,@JsonKey(name: 'user_id') String? userId, Recipient? recipient, 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') int? createdAt,@JsonKey(name: 'updated_at') int? updatedAt, bool? seen,@JsonKey(name: 'seen_at') int? seenAt,@JsonKey(name: 'is_scheduled') bool? isScheduled,@JsonKey(name: 'scheduled_at') int? scheduledAt + String? id,@JsonKey(name: 'user_id') String? userId, Recipient? recipient, String? title, String? message, String? link, String? image, String? priority, String? type,@JsonKey(name: 'event_type') String? eventType, String? status, Map? methods, Metadata? metadata,@JsonKey(name: 'schedule_at') int? scheduleAt,@JsonKey(name: 'created_at') int? createdAt,@JsonKey(name: 'updated_at') int? updatedAt, bool? seen,@JsonKey(name: 'seen_at') int? seenAt,@JsonKey(name: 'is_scheduled') bool? isScheduled,@JsonKey(name: 'scheduled_at') int? scheduledAt }); -@override $RecipientCopyWith<$Res>? get recipient; +@override $RecipientCopyWith<$Res>? get recipient;@override $MetadataCopyWith<$Res>? get metadata; } /// @nodoc @@ -677,8 +679,8 @@ as String?,type: freezed == type ? _self.type : type // ignore: cast_nullable_to 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 Map?,metadata: freezed == metadata ? _self.metadata : metadata // ignore: cast_nullable_to_non_nullable +as Metadata?,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 int?,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable as int?,seen: freezed == seen ? _self.seen : seen // ignore: cast_nullable_to_non_nullable @@ -701,6 +703,18 @@ $RecipientCopyWith<$Res>? get recipient { return $RecipientCopyWith<$Res>(_self.recipient!, (value) { return _then(_self.copyWith(recipient: value)); }); +}/// Create a copy of NotificationItem +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$MetadataCopyWith<$Res>? get metadata { + if (_self.metadata == null) { + return null; + } + + return $MetadataCopyWith<$Res>(_self.metadata!, (value) { + return _then(_self.copyWith(metadata: value)); + }); } } 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 index 83c2ef4..0c90491 100644 --- a/lib/data/services/model/notification__response/notification_response_model.g.dart +++ b/lib/data/services/model/notification__response/notification_response_model.g.dart @@ -52,7 +52,10 @@ _NotificationItem _$NotificationItemFromJson(Map json) => eventType: json['event_type'] as String?, status: json['status'] as String?, methods: json['methods'] as Map?, - metadata: json['metadata'] as Map?, + metadata: + json['metadata'] == null + ? null + : Metadata.fromJson(json['metadata'] as Map), scheduleAt: (json['schedule_at'] as num?)?.toInt(), createdAt: (json['created_at'] as num?)?.toInt(), updatedAt: (json['updated_at'] as num?)?.toInt(), diff --git a/lib/views/notification/strings/notification_strings.dart b/lib/views/notification/strings/notification_strings.dart index 32af793..af73a41 100644 --- a/lib/views/notification/strings/notification_strings.dart +++ b/lib/views/notification/strings/notification_strings.dart @@ -24,4 +24,5 @@ class NotificationStrings { static const String noUnreadNotifications = 'No unread notifications'; static const String noImportantNotifications = 'No important notifications'; static const String moreTextButton = 'More'; + static const String seeDetails = 'See Details'; } diff --git a/lib/views/notification/widgets/notification_card.dart b/lib/views/notification/widgets/notification_card.dart index 85825b5..a64976d 100644 --- a/lib/views/notification/widgets/notification_card.dart +++ b/lib/views/notification/widgets/notification_card.dart @@ -6,6 +6,7 @@ import 'package:tm_app/core/theme/colors.dart'; import 'package:tm_app/core/utils/size_config.dart'; import 'package:tm_app/data/services/model/notification__response/notification_response_model.dart'; import 'package:tm_app/view_models/notification_view_model.dart'; +import 'package:tm_app/views/notification/widgets/notification_more_dialog.dart'; import '../strings/notification_strings.dart'; @@ -121,7 +122,17 @@ class NotificationCard extends StatelessWidget { ), const Spacer(), TextButton( - onPressed: () {}, + onPressed: () { + showDialog( + context: context, + builder: (context) { + return NotificationMoreDialog( + notification: notification, + viewModel: viewModel, + ); + }, + ); + }, child: Row( children: [ Text( diff --git a/lib/views/notification/widgets/notification_more_dialog.dart b/lib/views/notification/widgets/notification_more_dialog.dart new file mode 100644 index 0000000..a2375a7 --- /dev/null +++ b/lib/views/notification/widgets/notification_more_dialog.dart @@ -0,0 +1,121 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:go_router/go_router.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/data/services/model/notification__response/notification_response_model.dart'; +import 'package:tm_app/view_models/notification_view_model.dart'; + +import '../../../core/routes/app_routes.dart'; +import '../strings/notification_strings.dart'; + +class NotificationMoreDialog extends StatelessWidget { + const NotificationMoreDialog({ + required this.notification, + required this.viewModel, + super.key, + }); + + final NotificationItem notification; + final NotificationViewModel viewModel; + + @override + Widget build(BuildContext context) { + return Dialog( + backgroundColor: AppColors.backgroundColor, + insetPadding: EdgeInsets.symmetric(horizontal: 24.0.w()), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), + child: Container( + width: double.infinity, + // margin: EdgeInsets.symmetric(horizontal: 24.0.w()), + padding: EdgeInsets.symmetric(horizontal: 16.0.w(), vertical: 16.0.h()), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + SvgPicture.asset(AssetsManager.notification), + SizedBox(width: 8.0.w()), + Text( + notification.title!, + style: TextStyle( + fontSize: 16.0.sp(), + fontWeight: FontWeight.w600, + color: AppColors.grey80, + ), + ), + ], + ), + SizedBox(height: 8.0.h()), + Padding( + padding: EdgeInsets.only(left: 28.0.w()), + child: Text( + notification.message!, + style: TextStyle( + fontSize: 14.0.sp(), + fontWeight: FontWeight.w400, + color: AppColors.grey70, + ), + ), + ), + SizedBox(height: 12.0.h()), + Padding( + padding: EdgeInsets.only(left: 28.0.w()), + child: Row( + children: [ + SvgPicture.asset(AssetsManager.calendar), + SizedBox(width: 4.0.w()), + Text( + viewModel.timeAgo(notification.createdAt!.toString()), + style: TextStyle( + fontSize: 12.0.sp(), + fontWeight: FontWeight.w400, + color: AppColors.grey60, + ), + ), + const Spacer(), + Visibility( + visible: + (notification.metadata?.tender != null && + notification.metadata?.tender != ''), + child: TextButton( + onPressed: () { + context.pop(); + TenderDetailRouteData( + tenderId: + notification.metadata?.tender!.toString() ?? '', + ).push(context); + }, + child: Row( + children: [ + Text( + NotificationStrings.seeDetails, + style: TextStyle( + fontSize: 14.0.sp(), + fontWeight: FontWeight.w500, + color: AppColors.mainBlue, + ), + ), + SizedBox(width: 4.0.w()), + SvgPicture.asset( + AssetsManager.arrowRight, + colorFilter: const ColorFilter.mode( + AppColors.mainBlue, + BlendMode.srcIn, + ), + ), + ], + ), + ), + ), + ], + ), + ), + ], + ), + ), + ); + } +} From fd19e8c66ce46de044805b69ed8bc0025f4e797a Mon Sep 17 00:00:00 2001 From: amirrezaghabeli Date: Sun, 28 Sep 2025 07:23:25 +0330 Subject: [PATCH 2/3] fixing conflict --- .../repositories/notification_repository.dart | 11 ++++ lib/data/services/api/notification_api.dart | 16 +++--- lib/data/services/notification_service.dart | 17 ++++-- lib/view_models/notification_view_model.dart | 19 +++++-- .../pages/m_notification_page.dart | 56 ++++++------------- .../strings/notification_strings.dart | 1 + .../widgets/notification_all_tab.dart | 15 ++++- .../widgets/notification_important_tab.dart | 22 +++----- .../widgets/notification_unread_tab.dart | 12 ++-- 9 files changed, 92 insertions(+), 77 deletions(-) diff --git a/lib/data/repositories/notification_repository.dart b/lib/data/repositories/notification_repository.dart index fc0075c..4a0db4f 100644 --- a/lib/data/repositories/notification_repository.dart +++ b/lib/data/repositories/notification_repository.dart @@ -1,3 +1,4 @@ +// notification_repository.dart 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'; @@ -28,4 +29,14 @@ class NotificationsRepository { offset: offset, ); } + + Future> getImportantNotifications({ + required int limit, + required int offset, + }) { + return _notificationsService.getImportantNotifications( + limit: limit, + offset: offset, + ); + } } diff --git a/lib/data/services/api/notification_api.dart b/lib/data/services/api/notification_api.dart index a2d0af3..07c4d57 100644 --- a/lib/data/services/api/notification_api.dart +++ b/lib/data/services/api/notification_api.dart @@ -1,17 +1,19 @@ +// notification_api.dart 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/mark'; static const String markAsRead = '/api/v1/notifications/mark-as-read'; - static const String tenderApprovalsTender = '/api/v1/notifications'; + static const String unreadNotifications = '/api/v1/notifications?seen=false'; - static String getUnreadNotifications({required int limit, required int offset}) { - return '$unreadNotifications?limit=$limit&offset=$offset'; + static String getUnreadNotifications({required int limit, required int offset}) { + return '/api/v1/notifications?seen=false&limit=$limit&offset=$offset'; } -} + + static String getImportantNotifications({required int limit, required int offset}) { + return '/api/v1/notifications?priority=important&limit=$limit&offset=$offset'; + } +} \ No newline at end of file diff --git a/lib/data/services/notification_service.dart b/lib/data/services/notification_service.dart index 45e0b25..14eab95 100644 --- a/lib/data/services/notification_service.dart +++ b/lib/data/services/notification_service.dart @@ -1,10 +1,8 @@ // notification_service.dart import 'dart:convert'; - import 'package:tm_app/core/utils/result.dart'; import 'package:tm_app/data/services/api/notification_api.dart'; import 'package:tm_app/data/services/model/notification__response/notification_response_model.dart'; - import '../../core/network/network_manager.dart'; class NotificationsService { @@ -18,7 +16,6 @@ class NotificationsService { }) async { final result = await _networkManager.makeRequest( NotificationApi.getNotifications(limit: limit, offset: offset), - //NotificationApi.notifications, method: 'GET', (json) => NotificationResponseModel.fromJson(json), ); @@ -52,7 +49,19 @@ class NotificationsService { required int offset, }) async { final result = await _networkManager.makeRequest( - NotificationApi.unreadNotifications, + NotificationApi.getUnreadNotifications(limit: limit, offset: offset), + method: 'GET', + (json) => NotificationResponseModel.fromJson(json), + ); + return result; + } + + Future> getImportantNotifications({ + required int limit, + required int offset, + }) async { + final result = await _networkManager.makeRequest( + NotificationApi.getImportantNotifications(limit: limit, offset: offset), method: 'GET', (json) => NotificationResponseModel.fromJson(json), ); diff --git a/lib/view_models/notification_view_model.dart b/lib/view_models/notification_view_model.dart index 43bdf17..5be00ca 100644 --- a/lib/view_models/notification_view_model.dart +++ b/lib/view_models/notification_view_model.dart @@ -1,3 +1,4 @@ +// notification_view_model.dart import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:tm_app/core/utils/app_toast.dart'; @@ -79,7 +80,8 @@ class NotificationViewModel with ChangeNotifier { } notifyListeners(); - final int offset = (page - 1) * _pageSize; + final int offset = page * _pageSize; + final result = await _repository.getNotifications( limit: _pageSize, offset: offset, @@ -88,10 +90,15 @@ class NotificationViewModel with ChangeNotifier { switch (result) { case Ok(): final data = result.value; + if (isPagination && allNotificationResponse != null) { final old = allNotificationResponse?.data ?? []; final fresh = data.data ?? []; - allNotificationResponse = data.copyWith(data: [...old, ...fresh]); + + allNotificationResponse = allNotificationResponse!.copyWith( + data: [...old, ...fresh], + meta: data.meta, + ); } else { allNotificationResponse = data; } @@ -100,7 +107,7 @@ class NotificationViewModel with ChangeNotifier { currentPageAll = page; totalPagesAll = data.meta?.pages ?? 1; - hasMoreAll = (data.data?.length ?? 0) >= _pageSize; + hasMoreAll = currentPageAll < totalPagesAll; break; case Error(): @@ -124,7 +131,7 @@ class NotificationViewModel with ChangeNotifier { } notifyListeners(); - final int offset = (page - 1) * _pageSize; + final int offset = page * _pageSize; final result = await _repository.getUnreadNotifications( limit: _pageSize, offset: offset, @@ -167,9 +174,9 @@ class NotificationViewModel with ChangeNotifier { } notifyListeners(); - final int offset = (page - 1) * _pageSize; + final int offset = page * _pageSize; - final result = await _repository.getUnreadNotifications( + final result = await _repository.getImportantNotifications( limit: _pageSize, offset: offset, ); diff --git a/lib/views/notification/pages/m_notification_page.dart b/lib/views/notification/pages/m_notification_page.dart index 9e91d32..dd94b67 100644 --- a/lib/views/notification/pages/m_notification_page.dart +++ b/lib/views/notification/pages/m_notification_page.dart @@ -24,6 +24,7 @@ class _MobileNotificationPageState extends State final ScrollController _scrollAll = ScrollController(); final ScrollController _scrollUnread = ScrollController(); + final ScrollController _scrollImportant = ScrollController(); @override void initState() { @@ -55,6 +56,18 @@ class _MobileNotificationPageState extends State ); } }); + + _scrollImportant.addListener(() { + if (_scrollImportant.position.pixels >= + _scrollImportant.position.maxScrollExtent - 100 && + !viewModel.isLoadingImportant && + viewModel.hasMoreImportant) { + viewModel.getImportantNotifications( + page: viewModel.currentPageImportant + 1, + isPagination: true, + ); + } + }); } @override @@ -62,6 +75,7 @@ class _MobileNotificationPageState extends State controller.dispose(); _scrollAll.dispose(); _scrollUnread.dispose(); + _scrollImportant.dispose(); super.dispose(); } @@ -83,45 +97,13 @@ class _MobileNotificationPageState extends State ], ), SizedBox(height: 12.0.h()), - Padding( - padding: EdgeInsetsDirectional.only(end: 24.0.w()), - child: Align( - alignment: Alignment.centerRight, - child: InkWell( - onTap: () { - viewModel.markAllAsRead(context); - }, - borderRadius: BorderRadius.circular(99), - child: Container( - width: 132.0.w(), - height: 32.0.h(), - decoration: BoxDecoration( - color: AppColors.primary20, - borderRadius: BorderRadius.circular(99), - ), - child: Center( - child: Text( - NotificationStrings.markAllAsReadButton, - style: TextStyle( - fontSize: 14.0.sp(), - fontWeight: FontWeight.w500, - color: AppColors.mainBlue, - ), - ), - ), - ), - ), - ), - ), - SizedBox(height: 12.0.h()), - Expanded( child: TabBarView( controller: controller, children: [ NotificationAllTab(scrollController: _scrollAll), NotificationUnreadTab(scrollController: _scrollUnread), - const NotificationImportantTab(), + NotificationImportantTab(scrollController: _scrollImportant), ], ), ), @@ -142,11 +124,9 @@ class _MobileNotificationPageState extends State ), ), - if ((viewModel.isLoadingAll && - (viewModel.allNotificationResponse?.data?.isEmpty ?? true)) || - (viewModel.isLoadingUnread && - (viewModel.unreadNotificationResponse?.data?.isEmpty ?? true))) - const Expanded( + if (viewModel.isMoreLoadingImportant) + const Padding( + padding: EdgeInsets.symmetric(vertical: 16), child: Center( child: CircularProgressIndicator(color: AppColors.secondary50), ), diff --git a/lib/views/notification/strings/notification_strings.dart b/lib/views/notification/strings/notification_strings.dart index af73a41..4d9f564 100644 --- a/lib/views/notification/strings/notification_strings.dart +++ b/lib/views/notification/strings/notification_strings.dart @@ -25,4 +25,5 @@ class NotificationStrings { static const String noImportantNotifications = 'No important notifications'; static const String moreTextButton = 'More'; static const String seeDetails = 'See Details'; + static const String noNotifications = 'No notifications'; } diff --git a/lib/views/notification/widgets/notification_all_tab.dart b/lib/views/notification/widgets/notification_all_tab.dart index 6df4c93..d148ddd 100644 --- a/lib/views/notification/widgets/notification_all_tab.dart +++ b/lib/views/notification/widgets/notification_all_tab.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:tm_app/view_models/notification_view_model.dart'; +import 'package:tm_app/views/notification/strings/notification_strings.dart'; import 'notification_card.dart'; @@ -12,8 +13,17 @@ class NotificationAllTab extends StatelessWidget { @override Widget build(BuildContext context) { final viewModel = context.watch(); - final notifications = - viewModel.allNotificationResponse?.data ?? []; + + if (viewModel.isLoadingAll && + (viewModel.allNotificationResponse?.data?.isEmpty ?? true)) { + return const Center(child: CircularProgressIndicator()); + } + + final notifications = viewModel.allNotificationResponse?.data ?? []; + + if (notifications.isEmpty) { + return const Center(child: Text(NotificationStrings.noNotifications)); + } return ListView.builder( controller: scrollController, @@ -28,3 +38,4 @@ class NotificationAllTab extends StatelessWidget { ); } } + diff --git a/lib/views/notification/widgets/notification_important_tab.dart b/lib/views/notification/widgets/notification_important_tab.dart index cc00dbe..93a7f01 100644 --- a/lib/views/notification/widgets/notification_important_tab.dart +++ b/lib/views/notification/widgets/notification_important_tab.dart @@ -13,22 +13,18 @@ class NotificationImportantTab extends StatelessWidget { Widget build(BuildContext context) { final viewModel = context.watch(); - if (viewModel.isLoadingImportant) { + if (viewModel.isLoadingImportant && + (viewModel.importantNotificationResponse?.data?.isEmpty ?? true)) { return const Center(child: CircularProgressIndicator()); } - final importantFromApi = viewModel.importantNotificationResponse?.data ?? []; - - final allNotifications = viewModel.allNotificationResponse?.data ?? []; - - final source = importantFromApi.isNotEmpty ? importantFromApi : allNotifications; - - final importantNotifications = source - .where((n) => (n.priority ?? '').toLowerCase() == 'important') - .toList(); + final importantNotifications = + viewModel.importantNotificationResponse?.data ?? []; if (importantNotifications.isEmpty) { - return const Center(child: Text(NotificationStrings.noImportantNotifications)); + return const Center( + child: Text(NotificationStrings.noImportantNotifications), + ); } return ListView.builder( @@ -38,9 +34,7 @@ class NotificationImportantTab extends StatelessWidget { final notification = importantNotifications[index]; return Padding( padding: const EdgeInsets.only(bottom: 16.0), - child: NotificationCard( - notification: notification, - ), + 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 af714ce..a2990a7 100644 --- a/lib/views/notification/widgets/notification_unread_tab.dart +++ b/lib/views/notification/widgets/notification_unread_tab.dart @@ -3,7 +3,6 @@ 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 'package:tm_app/views/notification/strings/notification_strings.dart'; - import 'notification_card.dart'; class NotificationUnreadTab extends StatelessWidget { @@ -13,16 +12,17 @@ class NotificationUnreadTab extends StatelessWidget { @override Widget build(BuildContext context) { final viewModel = context.watch(); - - if (viewModel.isLoadingUnread){ + if (viewModel.isLoadingUnread && + (viewModel.unreadNotificationResponse?.data?.isEmpty ?? true)) { return const Center(child: CircularProgressIndicator()); } - final notifications = - viewModel.unreadNotificationResponse?.data ?? []; + final notifications = viewModel.unreadNotificationResponse?.data ?? []; if (notifications.isEmpty) { - return const Center(child: Text(NotificationStrings.noUnreadNotifications,)); + return const Center( + child: Text(NotificationStrings.noUnreadNotifications), + ); } return ListView.builder( From 292d6653a085edc286e310d722dc6c933bcce889 Mon Sep 17 00:00:00 2001 From: amirrezaghabeli Date: Sun, 28 Sep 2025 07:26:19 +0330 Subject: [PATCH 3/3] fixed strings --- lib/views/notification/strings/notification_strings.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/views/notification/strings/notification_strings.dart b/lib/views/notification/strings/notification_strings.dart index 4d9f564..2497f52 100644 --- a/lib/views/notification/strings/notification_strings.dart +++ b/lib/views/notification/strings/notification_strings.dart @@ -24,6 +24,5 @@ class NotificationStrings { static const String noUnreadNotifications = 'No unread notifications'; static const String noImportantNotifications = 'No important notifications'; static const String moreTextButton = 'More'; - static const String seeDetails = 'See Details'; static const String noNotifications = 'No notifications'; }