Merge branch 'notifications-comments-version-updates' into 'main'

Remove unused withListenerNotifications Notifications service methods.

See merge request mysocialportal/relatica!70
merge-requests/71/merge
HankG 2025-07-07 17:39:39 +00:00
commit 44568c2141
18 zmienionych plików z 1324 dodań i 304 usunięć

Wyświetl plik

@ -1,5 +1,31 @@
# Relatica Change Log
## Version 1.1.0
* Changes
* Streamlined the Notifications screen loading/reloading to make it less quirky.
* Fixes
* Fix drop shadow on comments bubbles being rendered instead of the base background color.
* New Features
## Version 1.0.1
* Changes
* Fixes
* New Features
* Adds ability to report a post/comment by redirecting to a WebView browser session on the Friendica website for
that user. Currently, Friendica doesn't support the reporting API endpoint.
* User can toggle on whether to confirm reporting a post/comment confirmation dialog box. Default is on.
* Requires users to agree to abide by the server rules for the account's server when signing in as their respective
user.
## Version 1.0.0
* Changes
* Fixes
* Fix control overflows on Settings Screen
* New Features
## Version 0.16.0 (beta)
* Changes

Wyświetl plik

@ -166,7 +166,7 @@ SPEC CHECKSUMS:
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_file_dialog: 4c014a45b105709a27391e266c277d7e588e9299
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
flutter_web_auth_2: 051cf9f5dc366f31b5dcc4e2952c2b954767be8a
flutter_web_auth_2: 06d500582775790a0d4c323222fcb6d7990f9603
image_gallery_saver_plus: 782ade975fe6a4600b53e7c1983e3a2979d1e9e5
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1

Wyświetl plik

@ -26,6 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
@ -43,6 +44,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"

Wyświetl plik

@ -27,9 +27,7 @@ class AppBottomNavBar extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final profile = ref.watch(activeProfileProvider);
ref.watch(notificationsManagerProvider(profile));
final hasNotifications = ref
.read(notificationsManagerProvider(profile).notifier)
.hasNotifications;
final hasNotifications = ref.read(hasNotificationsProvider(profile, false));
return BottomNavigationBar(
onTap: (index) {

Wyświetl plik

@ -95,7 +95,7 @@ class _StatusControlState extends ConsumerState<FlattenedTreeEntryControl> {
otherPadding + (min(maxDepth, widget.originalItem.level) * 15.0);
final color = widget.originalItem.level.isOdd
? Theme.of(context).secondaryHeaderColor
: Theme.of(context).dialogTheme.backgroundColor;
: Theme.of(context).scaffoldBackgroundColor;
if (filteringInfo.isFiltered &&
filteringInfo.action == TimelineEntryFilterAction.hide) {

Wyświetl plik

@ -111,6 +111,21 @@ class UserNotification implements Comparable<UserNotification> {
return -1;
}
bool exactlyEqual(Object other) =>
identical(this, other) ||
other is UserNotification &&
runtimeType == other.runtimeType &&
id == other.id &&
type == other.type &&
fromId == other.fromId &&
fromName == other.fromName &&
fromUrl == other.fromUrl &&
timestamp == other.timestamp &&
iid == other.iid &&
dismissed == other.dismissed &&
content == other.content &&
link == other.link;
@override
bool operator ==(Object other) =>
identical(this, other) ||

Wyświetl plik

@ -13,12 +13,12 @@ import '../models/networking/pages_manager.dart';
import '../models/networking/paging_data.dart';
import '../models/user_notification.dart';
import '../serializers/mastodon/follow_request_mastodon_extensions.dart';
import '../utils/list_utils.dart';
import 'connection_manager_services.dart';
import 'direct_message_services.dart';
import 'feature_checker_services.dart';
import 'follow_requests_services.dart';
import 'networking/friendica_notifications_client_services.dart';
import 'settings_services.dart';
part 'notification_services.g.dart';
@ -26,45 +26,145 @@ const _itemsPerQuery = 50;
const _minimumDmsAndCrsUpdateDuration = Duration(seconds: 30);
final _logger = Logger('NotificationManager');
@Riverpod(keepAlive: true)
class _NotificationsStore extends _$NotificationsStore {
final _values = <UserNotification>{};
@override
List<UserNotification> build(Profile profile, NotificationType type) {
_logger.fine('Build _NotificationStoreProvider($profile) for $type');
return _rval;
}
void upsert(UserNotification notification) {
_values.remove(notification);
_values.add(notification);
state = _rval;
}
void clear() {
_values.clear();
state = _rval;
}
void markAllRead() {
final updated = _values.map((n) => n.copy(dismissed: true)).toList();
_values.clear();
_values.addAll(updated);
state = _rval;
}
List<UserNotification> get _rval => _values.toList()..sort();
@override
bool updateShouldNotify(
List<UserNotification> previous, List<UserNotification> next) {
final rval = !listEqualsWithComparer(
previous,
next,
equals: (u1, u2) => u1.exactlyEqual(u2),
);
return rval;
}
}
@riverpod
List<UserNotification> userNotificationsByType(
Ref ref, Profile profile, NotificationType type, bool isRead) {
_logger.fine('Build userNotificationsByTypeProvider($type,$isRead,$profile)');
final notifications = ref.watch(_NotificationsStoreProvider(profile, type));
return notifications.where((n) => n.dismissed == isRead).toList();
}
@riverpod
List<UserNotification> allNotifications(Ref ref, Profile profile, bool isRead) {
_logger.fine('Build allNotificationsProvider($profile)');
final notifications = <UserNotification>[];
for (NotificationType type in NotificationType.values) {
notifications.addAll(
ref.watch(userNotificationsByTypeProvider(profile, type, isRead)));
}
return notifications..sort();
}
@riverpod
bool hasNotifications(Ref ref, Profile profile, bool isRead) {
_logger.info('Build hasNotifications($profile)');
var hasNotifications = false;
// Go through all to watch all for changes
for (NotificationType type in NotificationType.values) {
hasNotifications |= ref
.watch(userNotificationsByTypeProvider(profile, type, isRead))
.isNotEmpty;
}
return hasNotifications;
}
@riverpod
bool hasAnyNotifications(Ref ref, Profile profile) {
_logger.info('Build hasNotifications($profile)');
final hasRead = ref.watch(hasNotificationsProvider(profile, true));
final hasUnread = ref.watch(hasNotificationsProvider(profile, false));
return hasRead || hasUnread;
}
@riverpod
(int low, int high) lowHighId(Ref ref, Profile profile, bool isRead) {
_logger.fine('Build lowHighIdProvider($profile) for isRead? $isRead');
final notifications = <UserNotification>[];
for (NotificationType type in NotificationType.values) {
notifications.addAll(
ref.watch(userNotificationsByTypeProvider(profile, type, isRead)));
}
final result = calcLowHigh(notifications);
_logger.finest(
'Result lowHighIdProvider($profile) for isRead? $isRead: $result');
return result;
}
@Riverpod(keepAlive: true)
class NotificationsManager extends _$NotificationsManager {
final dms = <UserNotification>[];
final connectionRequests = <UserNotification>[];
final unread = <UserNotification>[];
final read = <UserNotification>[];
var lastDmsUpdate = DateTime(1900);
var lastCrUpdate = DateTime(1900);
bool get hasNotifications =>
dms.isNotEmpty || connectionRequests.isNotEmpty || unread.isNotEmpty;
List<UserNotification> get notifications =>
[...connectionRequests, ...dms, ...unread, ...read];
@override
Future<Result<List<UserNotification>, ExecError>> build(
Profile profile) async {
Future<bool> build(Profile profile) async {
_logger.info('Building');
await _initialize();
return Result.ok(notifications);
return true;
}
Future<void> _initialize() async {
final result = await loadUnreadNotifications(false);
if (result.isSuccess && unread.isEmpty && read.isEmpty) {
await loadOlderNotifications(withListenerNotification: false);
final result = await loadUnreadNotifications();
final hasNoNotifications = !ref.read(hasAnyNotificationsProvider(profile));
if (result.isSuccess && hasNoNotifications) {
await loadOlderNotifications();
}
}
void refreshNotifications() async {
clear(withListenerNotification: true);
for (final t in NotificationType.values) {
ref.read(_NotificationsStoreProvider(profile, t).notifier).clear();
}
_initialize();
}
Future<void> clearConnectionRequestNotifications() async {
_logger.info('clearConnectionRequestNotifications');
connectionRequests.clear();
state = AsyncData(Result.ok(notifications));
ref
.read(_NotificationsStoreProvider(
profile, NotificationType.follow_request)
.notifier)
.clear();
state = const AsyncData(true);
}
Future<void> refreshConnectionRequestNotifications() async {
@ -72,7 +172,6 @@ class NotificationsManager extends _$NotificationsManager {
clearConnectionRequestNotifications();
await _postFetchOperations(
[],
true,
updateDms: false,
updateFollowRequests: true,
);
@ -80,25 +179,19 @@ class NotificationsManager extends _$NotificationsManager {
Future<void> refreshDms() async {
_logger.info('refreshDms');
dms.clear();
ref
.read(_NotificationsStoreProvider(
profile, NotificationType.direct_message)
.notifier)
.clear();
await _postFetchOperations(
[],
true,
updateDms: true,
updateFollowRequests: false,
);
}
void clear({bool withListenerNotification = true}) {
dms.clear();
connectionRequests.clear();
unread.clear();
read.clear();
_initialize();
}
FutureResult<bool, ExecError> loadUnreadNotifications(
bool withListenerNotification) async {
FutureResult<bool, ExecError> loadUnreadNotifications() async {
final notificationsFromRefresh = <UserNotification>[];
final pm = _buildPageManager(ref, profile, false);
@ -138,13 +231,11 @@ class NotificationsManager extends _$NotificationsManager {
// query unread notifications in increments of 25 after the latest ID
return await _postFetchOperations(
notificationsFromRefresh,
withListenerNotification,
).mapValue((value) => value.isNotEmpty);
}
FutureResult<List<UserNotification>, ExecError> _postFetchOperations(
List<UserNotification> notificationsFromRefresh,
bool withListenerNotification, {
List<UserNotification> notificationsFromRefresh, {
bool updateDms = true,
bool updateFollowRequests = true,
}) async {
@ -190,16 +281,15 @@ class NotificationsManager extends _$NotificationsManager {
return Result.ok(notifications.values.toList());
}
FutureResult<bool, ExecError> loadNewerNotifications({
bool withListenerNotification = true,
}) async {
final (_, highestId) =
unread.isNotEmpty ? calcLowHigh(unread) : calcLowHigh(read);
FutureResult<bool, ExecError> loadNewerNotifications() async {
final hasNoNotifications = !ref.read(hasAnyNotificationsProvider(profile));
final useIsRead = !ref.read(hasNotificationsProvider(profile, false));
final (_, highestId) = ref.read(lowHighIdProvider(profile, useIsRead));
final pm = _buildPageManager(
ref,
profile,
true,
initialPages: read.isEmpty && unread.isEmpty
initialPages: hasNoNotifications
? []
: [
PagedResponse(
@ -212,23 +302,23 @@ class NotificationsManager extends _$NotificationsManager {
],
);
final result = await (unread.isEmpty && read.isEmpty
final stillNoNotifications =
!ref.read(hasAnyNotificationsProvider(profile));
final result = await (stillNoNotifications
? pm.initialize(_itemsPerQuery)
: pm.previousFromBeginning())
.andThenAsync(
(page) async =>
await _postFetchOperations(page.data, withListenerNotification),
(page) async => await _postFetchOperations(page.data),
)
.withError(
(error) => _logger.info('Error getting more updates: $error'));
return result.mapValue((value) => value.isNotEmpty).execErrorCast();
}
FutureResult<bool, ExecError> loadOlderNotifications(
{bool withListenerNotification = true}) async {
if (unread.isNotEmpty) {
final result =
await _loadOlderUnreadNotifications(withListenerNotification);
FutureResult<bool, ExecError> loadOlderNotifications() async {
final hasUnread = ref.read(hasNotificationsProvider(profile, false));
if (hasUnread) {
final result = await _loadOlderUnreadNotifications();
final nonDmAndConnectionNotifications = result
.getValueOrElse(() => [])
.where((n) =>
@ -240,18 +330,19 @@ class NotificationsManager extends _$NotificationsManager {
}
}
return _loadOlderReadAndUnreadNotifications(withListenerNotification)
return _loadOlderReadAndUnreadNotifications()
.mapValue((value) => value.isNotEmpty);
}
FutureResult<bool, ExecError> markSeen(UserNotification notification) async {
_logger.fine('Marking Notification Seen: $notification');
final result = await ref
.read(clearNotificationProvider(profile, notification).future)
.withResult((_) {
unread.remove(notification);
read.add(notification.copy(dismissed: true));
read.sort();
state = AsyncData(Result.ok(notifications));
ref
.read(
_NotificationsStoreProvider(profile, notification.type).notifier)
.upsert(notification.copy(dismissed: true));
});
return result.execErrorCast();
@ -261,10 +352,11 @@ class NotificationsManager extends _$NotificationsManager {
final result = await ref
.read(clearNotificationsProvider(profile).future)
.withResult((_) {
unread.map((n) => n.copy(dismissed: true)).forEach(read.add);
unread.clear();
read.sort();
state = AsyncData(Result.ok(notifications));
for (final t in NotificationType.values) {
ref
.read(_NotificationsStoreProvider(profile, t).notifier)
.markAllRead();
}
});
return result.execErrorCast();
@ -275,7 +367,7 @@ class NotificationsManager extends _$NotificationsManager {
final myId = profile.userId;
final dmsResult = ref
.watch(directMessageThreadIdsProvider(profile))
.map((id) => ref.watch(directMessageThreadServiceProvider(profile, id)))
.map((id) => ref.read(directMessageThreadServiceProvider(profile, id)))
.where((t) => !t.allSeen)
.map((t) {
final fromAccountId = t.participantIds.firstWhere((pid) => pid != myId);
@ -310,93 +402,20 @@ class NotificationsManager extends _$NotificationsManager {
Future<void> _processNewNotifications(
Iterable<UserNotification> notifications) async {
final groupNotifications = ref.watch(notificationGroupingSettingProvider);
final dmsMap = <String, UserNotification>{};
final crMap = <String, UserNotification>{};
final unreadMap = <String, UserNotification>{};
final readMap = <String, UserNotification>{};
final st = Stopwatch()..start();
for (int i = 0; i < dms.length; i++) {
dmsMap[dms[i].id] = dms[i];
}
if (st.elapsedMilliseconds > maxProcessingMillis) {
await Future.delayed(processingSleep, () => st.reset());
}
for (int i = 0; i < connectionRequests.length; i++) {
crMap[connectionRequests[i].id] = connectionRequests[i];
}
if (st.elapsedMilliseconds > maxProcessingMillis) {
await Future.delayed(processingSleep, () => st.reset());
}
for (int i = 0; i < unread.length; i++) {
unreadMap[unread[i].id] = unread[i];
}
if (st.elapsedMilliseconds > maxProcessingMillis) {
await Future.delayed(processingSleep, () => st.reset());
}
for (int i = 0; i < read.length; i++) {
readMap[read[i].id] = read[i];
}
dms.clear();
connectionRequests.clear();
unread.clear();
read.clear();
for (final n in notifications) {
if (st.elapsedMilliseconds > maxProcessingMillis) {
await Future.delayed(processingSleep, () => st.reset());
}
dmsMap.remove(n.id);
crMap.remove(n.id);
unreadMap.remove(n.id);
readMap.remove(n.id);
if (n.dismissed) {
readMap[n.id] = n;
continue;
}
switch (n.type) {
case NotificationType.direct_message:
dmsMap[n.id] = n;
break;
case NotificationType.follow_request:
crMap[n.id] = n;
break;
default:
unreadMap[n.id] = n;
}
ref.read(_NotificationsStoreProvider(profile, n.type).notifier).upsert(n);
}
dms
..addAll(dmsMap.values)
..sort();
connectionRequests
..addAll(crMap.values)
..sort();
unread
..addAll(unreadMap.values)
..sort(
(n1, n2) => _compareByTypeStatusAndDate(n1, n2, groupNotifications));
read
..addAll(readMap.values)
..sort(
(n1, n2) => _compareByTypeStatusAndDate(n1, n2, groupNotifications));
state = AsyncData(Result.ok(this.notifications));
}
FutureResult<List<UserNotification>, ExecError> _loadOlderUnreadNotifications(
bool withListenerNotification) async {
FutureResult<List<UserNotification>, ExecError>
_loadOlderUnreadNotifications() async {
_logger.finest('Loading Older Unread Notifications');
final (lowestId, _) = calcLowHigh(unread);
final (lowestId, _) = ref.read(lowHighIdProvider(profile, false));
final pm = _buildPageManager(
ref,
profile,
@ -415,8 +434,7 @@ class NotificationsManager extends _$NotificationsManager {
final result = await pm
.nextFromEnd()
.andThenAsync(
(page) async =>
await _postFetchOperations(page.data, withListenerNotification),
(page) async => await _postFetchOperations(page.data),
)
.withError(
(error) => _logger.info('Error getting more updates: $error'));
@ -427,16 +445,16 @@ class NotificationsManager extends _$NotificationsManager {
}
FutureResult<List<UserNotification>, ExecError>
_loadOlderReadAndUnreadNotifications(
bool withListenerNotification) async {
_loadOlderReadAndUnreadNotifications() async {
_logger.finest('Loading Older Read and Unread Notifications');
final (lowestId, _) =
read.isNotEmpty ? calcLowHigh(read) : calcLowHigh(unread);
final hasNoNotifications = !ref.read(hasAnyNotificationsProvider(profile));
final useIsRead = ref.read(hasNotificationsProvider(profile, true));
final (lowestId, _) = ref.read(lowHighIdProvider(profile, useIsRead));
final pm = _buildPageManager(
ref,
profile,
true,
initialPages: read.isEmpty && unread.isEmpty
initialPages: hasNoNotifications
? []
: [
PagedResponse(
@ -448,12 +466,11 @@ class NotificationsManager extends _$NotificationsManager {
)
],
);
final result = await (read.isEmpty && unread.isEmpty
final result = await (hasNoNotifications
? pm.initialize(_itemsPerQuery)
: pm.nextFromEnd())
.andThenAsync(
(page) async =>
await _postFetchOperations(page.data, withListenerNotification),
(page) async => await _postFetchOperations(page.data),
)
.withError(
(error) => _logger.info('Error getting more updates: $error'));
@ -497,28 +514,3 @@ PagesManager<List<UserNotification>, String> _buildPageManager(
onRequest: (pd) async => await ref
.read(notificationsClientProvider(profile, pd, includeAll).future),
);
int _compareByTypeStatusAndDate(
UserNotification n1, UserNotification n2, bool groupNotifications) {
final n1Weight = _notificationTypeToWeight(n1.type);
final n2Weight = _notificationTypeToWeight(n2.type);
if (!groupNotifications || n1Weight == n2Weight) {
return n1.compareTo(n2);
}
return (n2Weight - n1Weight).sign.toInt();
}
num _notificationTypeToWeight(NotificationType type) {
return switch (type) {
NotificationType.follow_request => 1000,
NotificationType.follow => 100,
NotificationType.direct_message => 50,
NotificationType.mention => 10,
NotificationType.status => 4,
NotificationType.reshare => 3,
NotificationType.reblog => 3,
NotificationType.favourite => 2,
NotificationType.unknown => 1,
};
}

Wyświetl plik

@ -6,8 +6,8 @@ part of 'notification_services.dart';
// RiverpodGenerator
// **************************************************************************
String _$notificationsManagerHash() =>
r'c92bf43255b0c2b6b4a94c4239e545ee1c9bf90f';
String _$userNotificationsByTypeHash() =>
r'fe1e04eab091d6e92de866a9888c389024ca3da3';
/// Copied from Dart SDK
class _SystemHash {
@ -30,11 +30,920 @@ class _SystemHash {
}
}
abstract class _$NotificationsManager
extends BuildlessAsyncNotifier<Result<List<UserNotification>, ExecError>> {
/// See also [userNotificationsByType].
@ProviderFor(userNotificationsByType)
const userNotificationsByTypeProvider = UserNotificationsByTypeFamily();
/// See also [userNotificationsByType].
class UserNotificationsByTypeFamily extends Family<List<UserNotification>> {
/// See also [userNotificationsByType].
const UserNotificationsByTypeFamily();
/// See also [userNotificationsByType].
UserNotificationsByTypeProvider call(
Profile profile,
NotificationType type,
bool isRead,
) {
return UserNotificationsByTypeProvider(
profile,
type,
isRead,
);
}
@override
UserNotificationsByTypeProvider getProviderOverride(
covariant UserNotificationsByTypeProvider provider,
) {
return call(
provider.profile,
provider.type,
provider.isRead,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'userNotificationsByTypeProvider';
}
/// See also [userNotificationsByType].
class UserNotificationsByTypeProvider
extends AutoDisposeProvider<List<UserNotification>> {
/// See also [userNotificationsByType].
UserNotificationsByTypeProvider(
Profile profile,
NotificationType type,
bool isRead,
) : this._internal(
(ref) => userNotificationsByType(
ref as UserNotificationsByTypeRef,
profile,
type,
isRead,
),
from: userNotificationsByTypeProvider,
name: r'userNotificationsByTypeProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$userNotificationsByTypeHash,
dependencies: UserNotificationsByTypeFamily._dependencies,
allTransitiveDependencies:
UserNotificationsByTypeFamily._allTransitiveDependencies,
profile: profile,
type: type,
isRead: isRead,
);
UserNotificationsByTypeProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.profile,
required this.type,
required this.isRead,
}) : super.internal();
final Profile profile;
final NotificationType type;
final bool isRead;
@override
Override overrideWith(
List<UserNotification> Function(UserNotificationsByTypeRef provider) create,
) {
return ProviderOverride(
origin: this,
override: UserNotificationsByTypeProvider._internal(
(ref) => create(ref as UserNotificationsByTypeRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
profile: profile,
type: type,
isRead: isRead,
),
);
}
@override
AutoDisposeProviderElement<List<UserNotification>> createElement() {
return _UserNotificationsByTypeProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is UserNotificationsByTypeProvider &&
other.profile == profile &&
other.type == type &&
other.isRead == isRead;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, profile.hashCode);
hash = _SystemHash.combine(hash, type.hashCode);
hash = _SystemHash.combine(hash, isRead.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin UserNotificationsByTypeRef
on AutoDisposeProviderRef<List<UserNotification>> {
/// The parameter `profile` of this provider.
Profile get profile;
/// The parameter `type` of this provider.
NotificationType get type;
/// The parameter `isRead` of this provider.
bool get isRead;
}
class _UserNotificationsByTypeProviderElement
extends AutoDisposeProviderElement<List<UserNotification>>
with UserNotificationsByTypeRef {
_UserNotificationsByTypeProviderElement(super.provider);
@override
Profile get profile => (origin as UserNotificationsByTypeProvider).profile;
@override
NotificationType get type => (origin as UserNotificationsByTypeProvider).type;
@override
bool get isRead => (origin as UserNotificationsByTypeProvider).isRead;
}
String _$allNotificationsHash() => r'b353d331c19ca184cb47b221b88be98ebf7b365d';
/// See also [allNotifications].
@ProviderFor(allNotifications)
const allNotificationsProvider = AllNotificationsFamily();
/// See also [allNotifications].
class AllNotificationsFamily extends Family<List<UserNotification>> {
/// See also [allNotifications].
const AllNotificationsFamily();
/// See also [allNotifications].
AllNotificationsProvider call(
Profile profile,
bool isRead,
) {
return AllNotificationsProvider(
profile,
isRead,
);
}
@override
AllNotificationsProvider getProviderOverride(
covariant AllNotificationsProvider provider,
) {
return call(
provider.profile,
provider.isRead,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'allNotificationsProvider';
}
/// See also [allNotifications].
class AllNotificationsProvider
extends AutoDisposeProvider<List<UserNotification>> {
/// See also [allNotifications].
AllNotificationsProvider(
Profile profile,
bool isRead,
) : this._internal(
(ref) => allNotifications(
ref as AllNotificationsRef,
profile,
isRead,
),
from: allNotificationsProvider,
name: r'allNotificationsProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$allNotificationsHash,
dependencies: AllNotificationsFamily._dependencies,
allTransitiveDependencies:
AllNotificationsFamily._allTransitiveDependencies,
profile: profile,
isRead: isRead,
);
AllNotificationsProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.profile,
required this.isRead,
}) : super.internal();
final Profile profile;
final bool isRead;
@override
Override overrideWith(
List<UserNotification> Function(AllNotificationsRef provider) create,
) {
return ProviderOverride(
origin: this,
override: AllNotificationsProvider._internal(
(ref) => create(ref as AllNotificationsRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
profile: profile,
isRead: isRead,
),
);
}
@override
AutoDisposeProviderElement<List<UserNotification>> createElement() {
return _AllNotificationsProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is AllNotificationsProvider &&
other.profile == profile &&
other.isRead == isRead;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, profile.hashCode);
hash = _SystemHash.combine(hash, isRead.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin AllNotificationsRef on AutoDisposeProviderRef<List<UserNotification>> {
/// The parameter `profile` of this provider.
Profile get profile;
/// The parameter `isRead` of this provider.
bool get isRead;
}
class _AllNotificationsProviderElement
extends AutoDisposeProviderElement<List<UserNotification>>
with AllNotificationsRef {
_AllNotificationsProviderElement(super.provider);
@override
Profile get profile => (origin as AllNotificationsProvider).profile;
@override
bool get isRead => (origin as AllNotificationsProvider).isRead;
}
String _$hasNotificationsHash() => r'75fd7e12fef88aee9bfabb94ca30959a13b600f2';
/// See also [hasNotifications].
@ProviderFor(hasNotifications)
const hasNotificationsProvider = HasNotificationsFamily();
/// See also [hasNotifications].
class HasNotificationsFamily extends Family<bool> {
/// See also [hasNotifications].
const HasNotificationsFamily();
/// See also [hasNotifications].
HasNotificationsProvider call(
Profile profile,
bool isRead,
) {
return HasNotificationsProvider(
profile,
isRead,
);
}
@override
HasNotificationsProvider getProviderOverride(
covariant HasNotificationsProvider provider,
) {
return call(
provider.profile,
provider.isRead,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'hasNotificationsProvider';
}
/// See also [hasNotifications].
class HasNotificationsProvider extends AutoDisposeProvider<bool> {
/// See also [hasNotifications].
HasNotificationsProvider(
Profile profile,
bool isRead,
) : this._internal(
(ref) => hasNotifications(
ref as HasNotificationsRef,
profile,
isRead,
),
from: hasNotificationsProvider,
name: r'hasNotificationsProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$hasNotificationsHash,
dependencies: HasNotificationsFamily._dependencies,
allTransitiveDependencies:
HasNotificationsFamily._allTransitiveDependencies,
profile: profile,
isRead: isRead,
);
HasNotificationsProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.profile,
required this.isRead,
}) : super.internal();
final Profile profile;
final bool isRead;
@override
Override overrideWith(
bool Function(HasNotificationsRef provider) create,
) {
return ProviderOverride(
origin: this,
override: HasNotificationsProvider._internal(
(ref) => create(ref as HasNotificationsRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
profile: profile,
isRead: isRead,
),
);
}
@override
AutoDisposeProviderElement<bool> createElement() {
return _HasNotificationsProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is HasNotificationsProvider &&
other.profile == profile &&
other.isRead == isRead;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, profile.hashCode);
hash = _SystemHash.combine(hash, isRead.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin HasNotificationsRef on AutoDisposeProviderRef<bool> {
/// The parameter `profile` of this provider.
Profile get profile;
/// The parameter `isRead` of this provider.
bool get isRead;
}
class _HasNotificationsProviderElement extends AutoDisposeProviderElement<bool>
with HasNotificationsRef {
_HasNotificationsProviderElement(super.provider);
@override
Profile get profile => (origin as HasNotificationsProvider).profile;
@override
bool get isRead => (origin as HasNotificationsProvider).isRead;
}
String _$hasAnyNotificationsHash() =>
r'02f8ed9e0dc829753d60c81ada195d909e223104';
/// See also [hasAnyNotifications].
@ProviderFor(hasAnyNotifications)
const hasAnyNotificationsProvider = HasAnyNotificationsFamily();
/// See also [hasAnyNotifications].
class HasAnyNotificationsFamily extends Family<bool> {
/// See also [hasAnyNotifications].
const HasAnyNotificationsFamily();
/// See also [hasAnyNotifications].
HasAnyNotificationsProvider call(
Profile profile,
) {
return HasAnyNotificationsProvider(
profile,
);
}
@override
HasAnyNotificationsProvider getProviderOverride(
covariant HasAnyNotificationsProvider provider,
) {
return call(
provider.profile,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'hasAnyNotificationsProvider';
}
/// See also [hasAnyNotifications].
class HasAnyNotificationsProvider extends AutoDisposeProvider<bool> {
/// See also [hasAnyNotifications].
HasAnyNotificationsProvider(
Profile profile,
) : this._internal(
(ref) => hasAnyNotifications(
ref as HasAnyNotificationsRef,
profile,
),
from: hasAnyNotificationsProvider,
name: r'hasAnyNotificationsProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$hasAnyNotificationsHash,
dependencies: HasAnyNotificationsFamily._dependencies,
allTransitiveDependencies:
HasAnyNotificationsFamily._allTransitiveDependencies,
profile: profile,
);
HasAnyNotificationsProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.profile,
}) : super.internal();
final Profile profile;
@override
Override overrideWith(
bool Function(HasAnyNotificationsRef provider) create,
) {
return ProviderOverride(
origin: this,
override: HasAnyNotificationsProvider._internal(
(ref) => create(ref as HasAnyNotificationsRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
profile: profile,
),
);
}
@override
AutoDisposeProviderElement<bool> createElement() {
return _HasAnyNotificationsProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is HasAnyNotificationsProvider && other.profile == profile;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, profile.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin HasAnyNotificationsRef on AutoDisposeProviderRef<bool> {
/// The parameter `profile` of this provider.
Profile get profile;
}
class _HasAnyNotificationsProviderElement
extends AutoDisposeProviderElement<bool> with HasAnyNotificationsRef {
_HasAnyNotificationsProviderElement(super.provider);
@override
Profile get profile => (origin as HasAnyNotificationsProvider).profile;
}
String _$lowHighIdHash() => r'822098121e657a46ec5dc57ed5cee245354c0bd0';
/// See also [lowHighId].
@ProviderFor(lowHighId)
const lowHighIdProvider = LowHighIdFamily();
/// See also [lowHighId].
class LowHighIdFamily extends Family<(int low, int high)> {
/// See also [lowHighId].
const LowHighIdFamily();
/// See also [lowHighId].
LowHighIdProvider call(
Profile profile,
bool isRead,
) {
return LowHighIdProvider(
profile,
isRead,
);
}
@override
LowHighIdProvider getProviderOverride(
covariant LowHighIdProvider provider,
) {
return call(
provider.profile,
provider.isRead,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'lowHighIdProvider';
}
/// See also [lowHighId].
class LowHighIdProvider extends AutoDisposeProvider<(int low, int high)> {
/// See also [lowHighId].
LowHighIdProvider(
Profile profile,
bool isRead,
) : this._internal(
(ref) => lowHighId(
ref as LowHighIdRef,
profile,
isRead,
),
from: lowHighIdProvider,
name: r'lowHighIdProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$lowHighIdHash,
dependencies: LowHighIdFamily._dependencies,
allTransitiveDependencies: LowHighIdFamily._allTransitiveDependencies,
profile: profile,
isRead: isRead,
);
LowHighIdProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.profile,
required this.isRead,
}) : super.internal();
final Profile profile;
final bool isRead;
@override
Override overrideWith(
(int low, int high) Function(LowHighIdRef provider) create,
) {
return ProviderOverride(
origin: this,
override: LowHighIdProvider._internal(
(ref) => create(ref as LowHighIdRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
profile: profile,
isRead: isRead,
),
);
}
@override
AutoDisposeProviderElement<(int low, int high)> createElement() {
return _LowHighIdProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is LowHighIdProvider &&
other.profile == profile &&
other.isRead == isRead;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, profile.hashCode);
hash = _SystemHash.combine(hash, isRead.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin LowHighIdRef on AutoDisposeProviderRef<(int low, int high)> {
/// The parameter `profile` of this provider.
Profile get profile;
/// The parameter `isRead` of this provider.
bool get isRead;
}
class _LowHighIdProviderElement
extends AutoDisposeProviderElement<(int low, int high)> with LowHighIdRef {
_LowHighIdProviderElement(super.provider);
@override
Profile get profile => (origin as LowHighIdProvider).profile;
@override
bool get isRead => (origin as LowHighIdProvider).isRead;
}
String _$notificationsStoreHash() =>
r'f0fd5f5bfdf129fe805d0d2cbb753fde83106cb6';
abstract class _$NotificationsStore
extends BuildlessNotifier<List<UserNotification>> {
late final Profile profile;
late final NotificationType type;
List<UserNotification> build(
Profile profile,
NotificationType type,
);
}
/// See also [_NotificationsStore].
@ProviderFor(_NotificationsStore)
const _notificationsStoreProvider = _NotificationsStoreFamily();
/// See also [_NotificationsStore].
class _NotificationsStoreFamily extends Family<List<UserNotification>> {
/// See also [_NotificationsStore].
const _NotificationsStoreFamily();
/// See also [_NotificationsStore].
_NotificationsStoreProvider call(
Profile profile,
NotificationType type,
) {
return _NotificationsStoreProvider(
profile,
type,
);
}
@override
_NotificationsStoreProvider getProviderOverride(
covariant _NotificationsStoreProvider provider,
) {
return call(
provider.profile,
provider.type,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'_notificationsStoreProvider';
}
/// See also [_NotificationsStore].
class _NotificationsStoreProvider
extends NotifierProviderImpl<_NotificationsStore, List<UserNotification>> {
/// See also [_NotificationsStore].
_NotificationsStoreProvider(
Profile profile,
NotificationType type,
) : this._internal(
() => _NotificationsStore()
..profile = profile
..type = type,
from: _notificationsStoreProvider,
name: r'_notificationsStoreProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$notificationsStoreHash,
dependencies: _NotificationsStoreFamily._dependencies,
allTransitiveDependencies:
_NotificationsStoreFamily._allTransitiveDependencies,
profile: profile,
type: type,
);
_NotificationsStoreProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.profile,
required this.type,
}) : super.internal();
final Profile profile;
final NotificationType type;
@override
List<UserNotification> runNotifierBuild(
covariant _NotificationsStore notifier,
) {
return notifier.build(
profile,
type,
);
}
@override
Override overrideWith(_NotificationsStore Function() create) {
return ProviderOverride(
origin: this,
override: _NotificationsStoreProvider._internal(
() => create()
..profile = profile
..type = type,
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
profile: profile,
type: type,
),
);
}
@override
NotifierProviderElement<_NotificationsStore, List<UserNotification>>
createElement() {
return _NotificationsStoreProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is _NotificationsStoreProvider &&
other.profile == profile &&
other.type == type;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, profile.hashCode);
hash = _SystemHash.combine(hash, type.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin _NotificationsStoreRef on NotifierProviderRef<List<UserNotification>> {
/// The parameter `profile` of this provider.
Profile get profile;
/// The parameter `type` of this provider.
NotificationType get type;
}
class _NotificationsStoreProviderElement
extends NotifierProviderElement<_NotificationsStore, List<UserNotification>>
with _NotificationsStoreRef {
_NotificationsStoreProviderElement(super.provider);
@override
Profile get profile => (origin as _NotificationsStoreProvider).profile;
@override
NotificationType get type => (origin as _NotificationsStoreProvider).type;
}
String _$notificationsManagerHash() =>
r'e6946d8411c01f8e5d6dbdbbff612f552eea2c03';
abstract class _$NotificationsManager extends BuildlessAsyncNotifier<bool> {
late final Profile profile;
FutureOr<Result<List<UserNotification>, ExecError>> build(
FutureOr<bool> build(
Profile profile,
);
}
@ -44,8 +953,7 @@ abstract class _$NotificationsManager
const notificationsManagerProvider = NotificationsManagerFamily();
/// See also [NotificationsManager].
class NotificationsManagerFamily
extends Family<AsyncValue<Result<List<UserNotification>, ExecError>>> {
class NotificationsManagerFamily extends Family<AsyncValue<bool>> {
/// See also [NotificationsManager].
const NotificationsManagerFamily();
@ -83,8 +991,8 @@ class NotificationsManagerFamily
}
/// See also [NotificationsManager].
class NotificationsManagerProvider extends AsyncNotifierProviderImpl<
NotificationsManager, Result<List<UserNotification>, ExecError>> {
class NotificationsManagerProvider
extends AsyncNotifierProviderImpl<NotificationsManager, bool> {
/// See also [NotificationsManager].
NotificationsManagerProvider(
Profile profile,
@ -115,7 +1023,7 @@ class NotificationsManagerProvider extends AsyncNotifierProviderImpl<
final Profile profile;
@override
FutureOr<Result<List<UserNotification>, ExecError>> runNotifierBuild(
FutureOr<bool> runNotifierBuild(
covariant NotificationsManager notifier,
) {
return notifier.build(
@ -140,8 +1048,7 @@ class NotificationsManagerProvider extends AsyncNotifierProviderImpl<
}
@override
AsyncNotifierProviderElement<NotificationsManager,
Result<List<UserNotification>, ExecError>> createElement() {
AsyncNotifierProviderElement<NotificationsManager, bool> createElement() {
return _NotificationsManagerProviderElement(this);
}
@ -161,15 +1068,14 @@ class NotificationsManagerProvider extends AsyncNotifierProviderImpl<
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin NotificationsManagerRef
on AsyncNotifierProviderRef<Result<List<UserNotification>, ExecError>> {
mixin NotificationsManagerRef on AsyncNotifierProviderRef<bool> {
/// The parameter `profile` of this provider.
Profile get profile;
}
class _NotificationsManagerProviderElement extends AsyncNotifierProviderElement<
NotificationsManager,
Result<List<UserNotification>, ExecError>> with NotificationsManagerRef {
class _NotificationsManagerProviderElement
extends AsyncNotifierProviderElement<NotificationsManager, bool>
with NotificationsManagerRef {
_NotificationsManagerProviderElement(super.provider);
@override

Wyświetl plik

@ -3,8 +3,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:logging/logging.dart';
import '../controls/app_bottom_nav_bar.dart';
import '../controls/async_value_widget.dart';
import '../controls/error_message_widget.dart';
import '../controls/notifications_control.dart';
import '../controls/padding.dart';
import '../controls/responsive_max_width.dart';
@ -13,9 +11,11 @@ import '../controls/standard_appbar.dart';
import '../controls/status_and_refresh_button.dart';
import '../globals.dart';
import '../models/auth/profile.dart';
import '../models/user_notification.dart';
import '../riverpod_controllers/account_services.dart';
import '../riverpod_controllers/networking/network_status_services.dart';
import '../riverpod_controllers/notification_services.dart';
import '../riverpod_controllers/settings_services.dart';
import '../utils/snackbar_builder.dart';
class NotificationsScreen extends ConsumerWidget {
@ -28,13 +28,8 @@ class NotificationsScreen extends ConsumerWidget {
_logger.finer('Building');
final profile = ref.watch(activeProfileProvider);
const title = 'Notifications';
final loading = ref.watch(notificationsLoadingProvider(profile));
late final Widget body;
final notificationsValue = ref.watch(notificationsManagerProvider(profile));
final loading = ref.watch(notificationsLoadingProvider(profile)) ||
switch (notificationsValue) {
AsyncData() => false,
_ => true,
};
final actions = [
if (platformIsDesktop)
@ -50,96 +45,78 @@ class NotificationsScreen extends ConsumerWidget {
),
];
body = AsyncValueWidget(notificationsValue,
valueBuilder: (context, ref, result) {
if (result.isFailure) {
return ErrorMessageWidget(
message: 'Error getting notifications: ${result.error.message}');
}
final hasNoNotifications = !ref.watch(hasAnyNotificationsProvider(profile));
if (hasNoNotifications) {
body = RefreshIndicator(
onRefresh: () async => ref
.read(notificationsManagerProvider(profile).notifier)
.refreshNotifications(),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: loading
? [
const Center(child: Text('Loading Notifications')),
]
: [
const Center(child: Text('No notifications')),
const VerticalPadding(),
ElevatedButton(
onPressed: () => ref
.read(notificationsManagerProvider(profile).notifier)
.refreshNotifications(),
child: const Text('Load Notifications'),
)
],
)),
);
} else {
final groupNotifications = ref.watch(notificationGroupingSettingProvider);
final notificationEntries = groupNotifications
? [
...orderedNotificationTypes.map((type) =>
_NotificationsByTypeListElement(
profile: profile, type: type, isRead: false)),
_NotificationsByReadStatusListElement(
profile: profile, isRead: true),
]
: [
_NotificationsByReadStatusListElement(
profile: profile, isRead: false),
_NotificationsByReadStatusListElement(
profile: profile, isRead: true),
];
final notifications = result.value;
final allEntries = [
TextButton(
onPressed: () async {
await ref
.read(notificationsManagerProvider(profile).notifier)
.loadNewerNotifications();
},
child: const Text('Load newer notifications')),
...notificationEntries,
TextButton(
onPressed: () async {
await ref
.read(notificationsManagerProvider(profile).notifier)
.loadOlderNotifications();
},
child: const Text('Load older notifications'))
];
if (notifications.isEmpty) {
return RefreshIndicator(
onRefresh: () async => ref
.read(notificationsManagerProvider(profile).notifier)
.refreshNotifications(),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: loading
? [
const Center(child: Text('Loading Notifications')),
]
: [
const Center(child: Text('No notifications')),
const VerticalPadding(),
ElevatedButton(
onPressed: () => ref
.read(notificationsManagerProvider(profile).notifier)
.refreshNotifications(),
child: const Text('Load Notifications'),
)
],
)),
);
} else {
return RefreshIndicator(
body = RefreshIndicator(
onRefresh: () async => ref
.read(notificationsManagerProvider(profile).notifier)
.refreshNotifications(),
child: ResponsiveMaxWidth(
child: ListView.separated(
physics: const AlwaysScrollableScrollPhysics(),
itemBuilder: (context, index) {
if (index == 0) {
return TextButton(
onPressed: () async {
final result = await ref
.read(notificationsManagerProvider(profile)
.notifier)
.loadNewerNotifications();
final noMore = result.fold(
onSuccess: (value) => !value,
onError: (_) => true);
if (context.mounted && noMore) {
buildSnackbar(
context, 'No newer notifications to load');
}
},
child: const Text('Load newer notifications'));
}
if (index == notifications.length + 1) {
return TextButton(
onPressed: () async {
final result = await ref
.read(notificationsManagerProvider(profile)
.notifier)
.loadOlderNotifications();
final noMore = result.fold(
onSuccess: (value) => !value,
onError: (_) => true);
if (context.mounted && noMore) {
buildSnackbar(
context, 'No older notifications to load');
}
},
child: const Text('Load older notifications'));
}
return NotificationControl(
notification: notifications[index - 1]);
},
separatorBuilder: (context, index) {
return const Divider(
color: Colors.black54,
height: 0.0,
);
},
itemCount: notifications.length + 2),
),
);
}
});
itemBuilder: (_, index) => allEntries[index],
separatorBuilder: (_, __) => _standardDivider,
itemCount: allEntries.length,
),
));
}
return Scaffold(
appBar: StandardAppBar.build(
@ -183,3 +160,64 @@ class NotificationsScreen extends ConsumerWidget {
}
}
}
class _NotificationsByTypeListElement extends ConsumerWidget {
final Profile profile;
final NotificationType type;
final bool isRead;
const _NotificationsByTypeListElement(
{required this.profile, required this.type, required this.isRead});
@override
Widget build(BuildContext context, WidgetRef ref) {
final notifications =
ref.watch(userNotificationsByTypeProvider(profile, type, isRead));
return ListView.separated(
primary: false,
shrinkWrap: true,
itemBuilder: (_, index) =>
NotificationControl(notification: notifications[index]),
separatorBuilder: (_, __) => _standardDivider,
itemCount: notifications.length,
);
}
}
class _NotificationsByReadStatusListElement extends ConsumerWidget {
final Profile profile;
final bool isRead;
const _NotificationsByReadStatusListElement(
{required this.profile, required this.isRead});
@override
Widget build(BuildContext context, WidgetRef ref) {
final notifications = ref.watch(allNotificationsProvider(profile, isRead));
return ListView.separated(
primary: false,
shrinkWrap: true,
itemBuilder: (_, index) =>
NotificationControl(notification: notifications[index]),
separatorBuilder: (_, __) => _standardDivider,
itemCount: notifications.length,
);
}
}
final orderedNotificationTypes = [
NotificationType.follow_request,
NotificationType.follow,
NotificationType.direct_message,
NotificationType.mention,
NotificationType.status,
NotificationType.reshare,
NotificationType.reblog,
NotificationType.favourite,
NotificationType.unknown,
];
const _standardDivider = Divider(
color: Colors.black54,
height: 0.0,
);

Wyświetl plik

@ -0,0 +1,18 @@
bool listEqualsWithComparer<T>(List<T> a, List<T> b,
{required bool Function(T i1, T i2) equals}) {
if (identical(a, b)) {
return true;
}
if (a.length != b.length) {
return false;
}
for (int index = 0; index < a.length; index++) {
if (!equals(a[index], b[index])) {
return false;
}
}
return true;
}

Wyświetl plik

@ -6,6 +6,7 @@
#include "generated_plugin_registrant.h"
#include <desktop_webview_window/desktop_webview_window_plugin.h>
#include <desktop_window/desktop_window_plugin.h>
#include <file_selector_linux/file_selector_plugin.h>
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
@ -16,6 +17,9 @@
#include <window_to_front/window_to_front_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) desktop_webview_window_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopWebviewWindowPlugin");
desktop_webview_window_plugin_register_with_registrar(desktop_webview_window_registrar);
g_autoptr(FlPluginRegistrar) desktop_window_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopWindowPlugin");
desktop_window_plugin_register_with_registrar(desktop_window_registrar);

Wyświetl plik

@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
desktop_webview_window
desktop_window
file_selector_linux
flutter_secure_storage_linux

Wyświetl plik

@ -5,6 +5,7 @@
import FlutterMacOS
import Foundation
import desktop_webview_window
import desktop_window
import device_info_plus
import file_selector_macos
@ -24,6 +25,7 @@ import wakelock_plus
import window_to_front
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
DesktopWebviewWindowPlugin.register(with: registry.registrar(forPlugin: "DesktopWebviewWindowPlugin"))
DesktopWindowPlugin.register(with: registry.registrar(forPlugin: "DesktopWindowPlugin"))
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))

Wyświetl plik

@ -1,4 +1,6 @@
PODS:
- desktop_webview_window (0.0.1):
- FlutterMacOS
- desktop_window (0.0.1):
- FlutterMacOS
- device_info_plus (0.0.1):
@ -44,6 +46,7 @@ PODS:
- FlutterMacOS
DEPENDENCIES:
- desktop_webview_window (from `Flutter/ephemeral/.symlinks/plugins/desktop_webview_window/macos`)
- desktop_window (from `Flutter/ephemeral/.symlinks/plugins/desktop_window/macos`)
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
- file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`)
@ -69,6 +72,8 @@ SPEC REPOS:
- ObjectBox
EXTERNAL SOURCES:
desktop_webview_window:
:path: Flutter/ephemeral/.symlinks/plugins/desktop_webview_window/macos
desktop_window:
:path: Flutter/ephemeral/.symlinks/plugins/desktop_window/macos
device_info_plus:
@ -109,6 +114,7 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/window_to_front/macos
SPEC CHECKSUMS:
desktop_webview_window: d4365e71bcd4e1aa0c14cf0377aa24db0c16a7e2
desktop_window: fb7c4f12c1129f947ac482296b6f14059d57a3c3
device_info_plus: ce1b7762849d3ec103d0e0517299f2db7ad60720
file_selector_macos: cc3858c981fe6889f364731200d6232dac1d812d

Wyświetl plik

@ -50,10 +50,10 @@ packages:
dependency: transitive
description:
name: async
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
url: "https://pub.dev"
source: hosted
version: "2.12.0"
version: "2.13.0"
boolean_selector:
dependency: transitive
description:
@ -310,6 +310,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.10"
desktop_webview_window:
dependency: transitive
description:
name: desktop_webview_window
sha256: "57cf20d81689d5cbb1adfd0017e96b669398a669d927906073b0e42fc64111c0"
url: "https://pub.dev"
source: hosted
version: "0.2.3"
desktop_window:
dependency: "direct main"
description:
@ -338,10 +346,10 @@ packages:
dependency: transitive
description:
name: fake_async
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.dev"
source: hosted
version: "1.3.2"
version: "1.3.3"
ffi:
dependency: transitive
description:
@ -540,18 +548,18 @@ packages:
dependency: "direct main"
description:
name: flutter_web_auth_2
sha256: "4d3d2fd3d26bf1a26b3beafd4b4b899c0ffe10dc99af25abc58ffe24e991133c"
sha256: "3c14babeaa066c371f3a743f204dd0d348b7d42ffa6fae7a9847a521aff33696"
url: "https://pub.dev"
source: hosted
version: "3.1.2"
version: "4.1.0"
flutter_web_auth_2_platform_interface:
dependency: transitive
description:
name: flutter_web_auth_2_platform_interface
sha256: e8669e262005a8354389ba2971f0fc1c36188481234ff50d013aaf993f30f739
sha256: c63a472c8070998e4e422f6b34a17070e60782ac442107c70000dd1bed645f4d
url: "https://pub.dev"
source: hosted
version: "3.1.0"
version: "4.1.0"
flutter_web_plugins:
dependency: transitive
description: flutter
@ -609,10 +617,10 @@ packages:
dependency: transitive
description:
name: hotreloader
sha256: ed56fdc1f3a8ac924e717257621d09e9ec20e308ab6352a73a50a1d7a4d9158e
sha256: bc167a1163807b03bada490bfe2df25b0d744df359227880220a5cbd04e5734b
url: "https://pub.dev"
source: hosted
version: "4.2.0"
version: "4.3.0"
html:
dependency: "direct main"
description:
@ -657,10 +665,10 @@ packages:
dependency: "direct main"
description:
name: image_gallery_saver_plus
sha256: "05a5dfd60b0ecd9f990f578f4ff55fe2b6df57a03bb34e3d0963639584d76153"
sha256: "199b9e24f8d85e98f11e3d35571ab68ae50626ad40e2bb85c84383f69a6950ad"
url: "https://pub.dev"
source: hosted
version: "3.0.5"
version: "4.0.1"
image_picker:
dependency: "direct main"
description:
@ -769,10 +777,10 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
url: "https://pub.dev"
source: hosted
version: "10.0.8"
version: "10.0.9"
leak_tracker_flutter_testing:
dependency: transitive
description:
@ -1654,10 +1662,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
url: "https://pub.dev"
source: hosted
version: "14.3.1"
version: "15.0.0"
volume_controller:
dependency: transitive
description:

Wyświetl plik

@ -2,7 +2,7 @@ name: relatica
description: A mobile and desktop client for interacting with the Friendica social network
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+15
version: 1.1.0+17
environment:
sdk: '>=3.2.0 <4.0.0'
@ -23,14 +23,14 @@ dependencies:
flutter_riverpod: ^2.5.1
flutter_secure_storage: ^9.2.2
flutter_svg: ^2.0.10+1
flutter_web_auth_2: ^3.1.2
flutter_web_auth_2: ^4.1.0
flutter_widget_from_html_core: ^0.15.2
go_router: ^14.1.2
html: ^0.15.4
http: any
http_parser: any
image: ^4.2.0
image_gallery_saver_plus: ^3.0.5
image_gallery_saver_plus: ^4.0.1
image_picker: ^1.1.2
logging: ^1.2.0
markdown: ^7.2.2

Wyświetl plik

@ -6,6 +6,7 @@
#include "generated_plugin_registrant.h"
#include <desktop_webview_window/desktop_webview_window_plugin.h>
#include <desktop_window/desktop_window_plugin.h>
#include <file_selector_windows/file_selector_windows.h>
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
@ -17,6 +18,8 @@
#include <window_to_front/window_to_front_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
DesktopWebviewWindowPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("DesktopWebviewWindowPlugin"));
DesktopWindowPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("DesktopWindowPlugin"));
FileSelectorWindowsRegisterWithRegistrar(

Wyświetl plik

@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
desktop_webview_window
desktop_window
file_selector_windows
flutter_secure_storage_windows