Skip to content

Commit

Permalink
add notification page
Browse files Browse the repository at this point in the history
  • Loading branch information
andannn committed Nov 11, 2023
1 parent f10a631 commit 71e8349
Show file tree
Hide file tree
Showing 15 changed files with 749 additions and 29 deletions.
103 changes: 98 additions & 5 deletions lib/app/local/util/string_resource_util.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import 'dart:convert';

import 'package:aniflow/app/local/ani_flow_localizations.dart';
import 'package:aniflow/core/common/model/activity_filter_type.dart';
import 'package:aniflow/core/common/model/anime_season.dart';
import 'package:aniflow/core/common/model/anime_source.dart';
import 'package:aniflow/core/common/model/character_role.dart';
import 'package:aniflow/core/common/model/media_status.dart';
import 'package:aniflow/core/common/util/time_util.dart';
import 'package:aniflow/core/data/model/activity_model.dart';
import 'package:aniflow/core/data/model/media_model.dart';
import 'package:aniflow/core/data/model/media_title_modle.dart';
import 'package:aniflow/core/data/model/notification_model.dart';
import 'package:aniflow/core/data/notification_repository.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

extension AnimeSourceEx on AnimeSource {
Expand Down Expand Up @@ -109,9 +116,95 @@ extension AnimeModelEx on MediaModel {
}

extension ActivityFilterTypeEx on ActivityFilterType {
String label(BuildContext context) => switch(this) {
ActivityFilterType.all => 'All',
ActivityFilterType.text => 'Text',
ActivityFilterType.list => 'List',
};
String label(BuildContext context) => switch (this) {
ActivityFilterType.all => 'All',
ActivityFilterType.text => 'Text',
ActivityFilterType.list => 'List',
};
}

extension NotificationCategoryEx on NotificationCategory {
String label(BuildContext context) => switch (this) {
NotificationCategory.all => 'All',
NotificationCategory.airing => 'Airing',
NotificationCategory.activity => 'Activity',
NotificationCategory.follows => 'Follows',
NotificationCategory.media => 'Media',
};
}

extension AiringNotificationEx on AiringNotification {
List<TextSpan> createTextSpanList(BuildContext buildContext,
{required VoidCallback onMediaTextClick}) {
final List contextList = jsonDecode(context);
final colorScheme = Theme.of(buildContext).colorScheme;
return [
TextSpan(text: '${contextList[0]} $episode ${contextList[1]}'),
TextSpan(
text: media.title?.native,
style: TextStyle(color: colorScheme.tertiary),
recognizer: TapGestureRecognizer()..onTap = onMediaTextClick,
),
TextSpan(text: contextList[2]),
];
}
}

extension FollowNotificationEx on FollowNotification {
List<TextSpan> createTextSpanList(BuildContext buildContext,
{required VoidCallback onUserTextClick}) {
final colorScheme = Theme.of(buildContext).colorScheme;
return [
TextSpan(
text: user.name,
style: TextStyle(color: colorScheme.tertiary),
recognizer: TapGestureRecognizer()..onTap = onUserTextClick,
),
TextSpan(text: context),
];
}
}

extension ActivityNotificationEx on ActivityNotification {
List<TextSpan> createTextSpanList(BuildContext buildContext,
{required VoidCallback onUserTextClick}) {
final colorScheme = Theme.of(buildContext).colorScheme;
return [
TextSpan(
text: user.name,
style: TextStyle(color: colorScheme.tertiary),
recognizer: TapGestureRecognizer()..onTap = onUserTextClick,
),
TextSpan(text: context),
];
}
}

extension MediaNotificationEx on MediaNotification {
List<TextSpan> createTextSpanList(BuildContext buildContext,
{required VoidCallback onUserTextClick}) {
final colorScheme = Theme.of(buildContext).colorScheme;
return [
TextSpan(
text: media.title?.native,
style: TextStyle(color: colorScheme.tertiary),
recognizer: TapGestureRecognizer()..onTap = onUserTextClick,
),
TextSpan(text: context),
];
}
}
extension ListActivityModelEx on ListActivityModel {
List<TextSpan> createTextSpanList(BuildContext buildContext,
{required VoidCallback onMediaClick}) {
final colorScheme = Theme.of(buildContext).colorScheme;
return [
TextSpan(text: '${status.toString()} $progress of '),
TextSpan(
text: media.title!.getLocalTitle(buildContext),
style: TextStyle(color: colorScheme.tertiary),
recognizer: TapGestureRecognizer()..onTap = onMediaClick,
),
];
}
}
7 changes: 7 additions & 0 deletions lib/app/navigation/ani_flow_route_path.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:aniflow/feature/detail_media/detail_media.dart';
import 'package:aniflow/feature/discover/discover.dart';
import 'package:aniflow/feature/media_page/media_page.dart';
import 'package:aniflow/feature/media_track/media_track.dart';
import 'package:aniflow/feature/notification/notification.dart';
import 'package:aniflow/feature/profile/profile.dart';
import 'package:aniflow/feature/social/social.dart';
import 'package:aniflow/feature/staff_page/staff_page.dart';
Expand Down Expand Up @@ -97,6 +98,10 @@ class AiringScheduleRoutePath extends AniFlowRoutePath {
const AiringScheduleRoutePath() : super(isFullScreen: true);
}

class NotificationRoutePath extends AniFlowRoutePath {
const NotificationRoutePath() : super(isFullScreen: true);
}

extension AniFlowRoutePathEx on AniFlowRoutePath {
Page generatePage() {
switch (this) {
Expand Down Expand Up @@ -134,6 +139,8 @@ extension AniFlowRoutePathEx on AniFlowRoutePath {
return const AiringSchedule(key: ValueKey('AiringSchedule'));
case SearchRoutePath():
return const SearchPage(key: ValueKey('SearchPage'));
case NotificationRoutePath():
return const NotificationPage(key: ValueKey('NotificationPage'));
default:
return const MaterialPage(child: SizedBox());
}
Expand Down
4 changes: 4 additions & 0 deletions lib/app/navigation/ani_flow_router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ class AFRouterDelegate extends RouterDelegate<AniFlowRoutePath>
_pushAsSingleton(const SearchRoutePath());
}

void navigateToNotification() {
_pushAsSingleton(const NotificationRoutePath());
}

void navigateToUserProfile(String userId) {
_pushAsSingleton(UserProfileRoutePath(userId));
}
Expand Down
1 change: 1 addition & 0 deletions lib/core/common/util/global_static_constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ mixin Config {

static const int profilePageDefaultPerPageCount = 6;
static const int activityPageDefaultPerPageCount = 50;
static const int notificationPageDefaultPerPageCount = 25;

/// Detail page consts.
static const double detailPagePreviewItemHeight = 133.0;
Expand Down
4 changes: 4 additions & 0 deletions lib/core/data/model/notification_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ sealed class NotificationModel extends Equatable {
createdAt: dto.createdAt ?? 0,
context: dto.context ?? '',
media: MediaModel.fromDto(dto.media!),
reason: dto.reason ?? '',
);
case RelatedMediaAdditionNotificationDto():
return RelatedMediaAdditionNotification(
Expand Down Expand Up @@ -226,7 +227,10 @@ class MediaDataChangeNotification extends MediaNotification {
required super.context,
required super.createdAt,
required super.media,
required this.reason,
});

final String reason;
}

class MediaMergeNotification extends MediaNotification {
Expand Down
53 changes: 35 additions & 18 deletions lib/core/design_system/widget/activity_item_widget.dart
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import 'package:aniflow/app/local/util/string_resource_util.dart';
import 'package:aniflow/core/data/model/activity_model.dart';
import 'package:aniflow/core/data/model/media_title_modle.dart';
import 'package:aniflow/core/design_system/widget/af_network_image.dart';
import 'package:aniflow/core/design_system/widget/avatar_icon.dart';
import 'package:aniflow/core/design_system/widget/short_num_label_icon_button.dart';
import 'package:flutter/material.dart';
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';

class ActivityItem extends StatelessWidget {
const ActivityItem(
{required this.model,
super.key,
this.onMediaClick,
this.onUserIconClick});
const ActivityItem({required this.model,
super.key,
this.onMediaClick,
this.onUserIconClick});

final ActivityModel model;
final Function(String mediaId)? onMediaClick;
Expand All @@ -29,8 +28,12 @@ class ActivityItem extends StatelessWidget {
}

Widget _buildListActivity(BuildContext context, ListActivityModel activity) {
final colorScheme = Theme.of(context).colorScheme;
final textTheme = Theme.of(context).textTheme;
final colorScheme = Theme
.of(context)
.colorScheme;
final textTheme = Theme
.of(context)
.textTheme;
return Container(
constraints: const BoxConstraints(minHeight: 120),
child: Card(
Expand Down Expand Up @@ -68,11 +71,17 @@ class ActivityItem extends StatelessWidget {
.copyWith(color: colorScheme.primary),
),
const SizedBox(height: 8),
Text(
// ignore: lines_longer_than_80_chars
'${activity.status.toString()} ${activity.progress} of ${activity.media.title!.getLocalTitle(context)}',
style: textTheme.bodyMedium!
.copyWith(color: colorScheme.onSurfaceVariant),
RichText(
text: TextSpan(
style: textTheme.bodyMedium!
.copyWith(color: colorScheme.onSurfaceVariant),
children: activity.createTextSpanList(
context,
onMediaClick: () {
onMediaClick?.call(activity.media.id);
},
),
),
),
const SizedBox(height: 8),
SizedBox(
Expand Down Expand Up @@ -112,8 +121,12 @@ class ActivityItem extends StatelessWidget {
}

Widget _buildTextActivity(BuildContext context, TextActivityModel activity) {
final colorScheme = Theme.of(context).colorScheme;
final textTheme = Theme.of(context).textTheme;
final colorScheme = Theme
.of(context)
.colorScheme;
final textTheme = Theme
.of(context)
.textTheme;
return Card(
elevation: 0,
color: colorScheme.surfaceVariant,
Expand Down Expand Up @@ -167,10 +180,14 @@ class ActivityItem extends StatelessWidget {
);
}

Widget _buildReplyLikeStateSection(
BuildContext context, bool isLike, int likeCount, int repliedCount) {
Widget _buildReplyLikeStateSection(BuildContext context, bool isLike,
int likeCount, int repliedCount) {
final defaultColor =
Theme.of(context).colorScheme.onSurfaceVariant.withOpacity(0.8);
Theme
.of(context)
.colorScheme
.onSurfaceVariant
.withOpacity(0.8);
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Expand Down
Loading

0 comments on commit 71e8349

Please sign in to comment.