Fix DM status update reflecting in notifications, etc.

merge-requests/67/merge
Hank Grabowski 2024-12-19 20:23:23 -05:00
rodzic 571f690320
commit 648d6e29dd
23 zmienionych plików z 406 dodań i 125 usunięć

Wyświetl plik

@ -101,14 +101,6 @@ class StandardAppDrawer extends ConsumerWidget {
'Settings', 'Settings',
() => context.pushNamed(ScreenPaths.settings), () => context.pushNamed(ScreenPaths.settings),
), ),
// TODO Add back in clearing ability but has to do disk caches too
// buildMenuButton(context, 'Clear Caches', () async {
// final confirm = await showYesNoDialog(
// context, 'You want to clear all memory and disk cache data?');
// if (confirm == true) {
// clearCaches();
// }
// }),
], ],
), ),
), ),

Wyświetl plik

@ -1,15 +1,12 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../riverpod_controllers/connection_manager_services.dart';
import 'auth/profile.dart'; import 'auth/profile.dart';
import 'connection.dart';
import 'direct_message.dart'; import 'direct_message.dart';
class DirectMessageThread { class DirectMessageThread {
final List<DirectMessage> messages; final List<DirectMessage> messages;
final List<Connection> participants; final List<String> participantIds;
final String title; final String title;
@ -17,14 +14,22 @@ class DirectMessageThread {
DirectMessageThread({ DirectMessageThread({
required this.messages, required this.messages,
required this.participants, required this.participantIds,
required this.title, required this.title,
required this.parentUri, required this.parentUri,
}); });
DirectMessageThread deepCopy() => DirectMessageThread( DirectMessageThread deepCopy() => DirectMessageThread(
messages: List.from(messages), messages: List.from(messages),
participants: List.from(participants), participantIds: List.from(participantIds),
title: title,
parentUri: parentUri,
);
DirectMessageThread copy({List<DirectMessage>? messages}) =>
DirectMessageThread(
messages: messages ?? this.messages,
participantIds: participantIds,
title: title, title: title,
parentUri: parentUri, parentUri: parentUri,
); );
@ -36,7 +41,6 @@ class DirectMessageThread {
.reduce((allUnseen, thisUnseen) => allUnseen && thisUnseen); .reduce((allUnseen, thisUnseen) => allUnseen && thisUnseen);
static List<DirectMessageThread> createThreads( static List<DirectMessageThread> createThreads(
Ref ref,
Profile profile, Profile profile,
Iterable<DirectMessage> messages, Iterable<DirectMessage> messages,
) { ) {
@ -60,14 +64,9 @@ class DirectMessageThread {
title = m.title; title = m.title;
} }
} }
final participants = participantIds
.map((pid) => ref.read(connectionByIdProvider(profile, pid)))
.where((pr) => pr.isSuccess)
.map((pr) => pr.value)
.toList();
final thread = DirectMessageThread( final thread = DirectMessageThread(
messages: threadMessages, messages: threadMessages,
participants: participants, participantIds: participantIds.toList(),
title: title, title: title,
parentUri: parentUri, parentUri: parentUri,
); );
@ -88,12 +87,12 @@ class DirectMessageThread {
title == other.title && title == other.title &&
parentUri == other.parentUri && parentUri == other.parentUri &&
listEquals(messages, other.messages) && listEquals(messages, other.messages) &&
listEquals(participants, other.participants); listEquals(participantIds, other.participantIds);
@override @override
int get hashCode => int get hashCode =>
title.hashCode ^ title.hashCode ^
parentUri.hashCode ^ parentUri.hashCode ^
Object.hashAll(messages) ^ Object.hashAll(messages) ^
Object.hashAll(participants); Object.hashAll(participantIds);
} }

Wyświetl plik

@ -69,7 +69,7 @@ final activeProfileProvider = NotifierProvider<ActiveProfile, Profile>.internal(
); );
typedef _$ActiveProfile = Notifier<Profile>; typedef _$ActiveProfile = Notifier<Profile>;
String _$credentialSigninHash() => r'9c6515a30bbdd3d8272ad4bba57c98a9391866c9'; String _$credentialSigninHash() => r'e9731ad5b916838122a2a86961af4cfe4bef57b5';
/// Copied from Dart SDK /// Copied from Dart SDK
class _SystemHash { class _SystemHash {
@ -236,7 +236,7 @@ class _CredentialSigninProviderElement
(origin as CredentialSigninProvider).credentials; (origin as CredentialSigninProvider).credentials;
} }
String _$profileManagerHash() => r'2bd46dca2f9307fa0197d21ba55627b89b297884'; String _$profileManagerHash() => r'5f37162a9b7b19f3cd9e804d83dbb37cda96568b';
abstract class _$ProfileManager extends BuildlessNotifier<bool> { abstract class _$ProfileManager extends BuildlessNotifier<bool> {
late final Profile profile; late final Profile profile;
@ -381,7 +381,7 @@ class _ProfileManagerProviderElement
} }
String _$accountServicesInitializerHash() => String _$accountServicesInitializerHash() =>
r'1872c6987b80737764687a679eb8b987f42c4ff3'; r'386b364d2fe41581ad17d5823a7f47dc12bc4760';
/// See also [AccountServicesInitializer]. /// See also [AccountServicesInitializer].
@ProviderFor(AccountServicesInitializer) @ProviderFor(AccountServicesInitializer)

Wyświetl plik

@ -6,7 +6,7 @@ part of 'blocks_services.dart';
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$blocksManagerHash() => r'd04635c7bf3072984d3575a2fdb6c84056729e5e'; String _$blocksManagerHash() => r'e8285887e48ab751515c9a2e9fdc0a90ce12e437';
/// Copied from Dart SDK /// Copied from Dart SDK
class _SystemHash { class _SystemHash {

Wyświetl plik

@ -332,7 +332,7 @@ class _TimelineGroupingListProviderElement
GroupingType get type => (origin as TimelineGroupingListProvider).type; GroupingType get type => (origin as TimelineGroupingListProvider).type;
} }
String _$circlesRepoHash() => r'f02b6233914071968c47213c79bb64c81f562dd3'; String _$circlesRepoHash() => r'82b95a626e70888ea4a4f0a2c1a736cd0bdcf9c9';
abstract class _$CirclesRepo extends BuildlessNotifier<ICirclesRepo> { abstract class _$CirclesRepo extends BuildlessNotifier<ICirclesRepo> {
late final Profile profile; late final Profile profile;
@ -476,7 +476,7 @@ class _CirclesRepoProviderElement
Profile get profile => (origin as _CirclesRepoProvider).profile; Profile get profile => (origin as _CirclesRepoProvider).profile;
} }
String _$circlesHash() => r'fbd0b71fa7027a8c1cf16413df288b666734b180'; String _$circlesHash() => r'84741e2bec4232522eadd6f83046b4d147645985';
abstract class _$Circles abstract class _$Circles
extends BuildlessNotifier<List<TimelineGroupingListData>> { extends BuildlessNotifier<List<TimelineGroupingListData>> {

Wyświetl plik

@ -1312,7 +1312,7 @@ class _ConnectionsRepoClearerProviderElement
} }
String _$connectionModifierHash() => String _$connectionModifierHash() =>
r'35d92e5d4c0162ca7c44ab9b9f296856a58704c0'; r'75d5a90c167916273292f8e03d462f95a0fe04a3';
abstract class _$ConnectionModifier abstract class _$ConnectionModifier
extends BuildlessAutoDisposeNotifier<Connection> { extends BuildlessAutoDisposeNotifier<Connection> {
@ -1628,7 +1628,7 @@ class _UpdateStatusInternalProviderElement
} }
String _$allContactsUpdaterHash() => String _$allContactsUpdaterHash() =>
r'cfab7072e13b11396b4e3aaaed75148fe97f0b55'; r'84f97838de84166a1ed907d75eedaa1decf56daf';
abstract class _$AllContactsUpdater extends BuildlessNotifier<bool> { abstract class _$AllContactsUpdater extends BuildlessNotifier<bool> {
late final Profile profile; late final Profile profile;

Wyświetl plik

@ -12,6 +12,7 @@ import '../models/exec_error.dart';
import '../models/networking/paging_data.dart'; import '../models/networking/paging_data.dart';
import 'feature_checker_services.dart'; import 'feature_checker_services.dart';
import 'networking/friendica_dms_client_services.dart'; import 'networking/friendica_dms_client_services.dart';
import 'notification_services.dart';
part 'direct_message_services.g.dart'; part 'direct_message_services.g.dart';
@ -30,8 +31,7 @@ class DirectMessageThreadIds extends _$DirectMessageThreadIds {
await ref.read(directMessagesProvider(profile, PagingData()).future).match( await ref.read(directMessagesProvider(profile, PagingData()).future).match(
onSuccess: (update) { onSuccess: (update) {
final newThreads = final newThreads = DirectMessageThread.createThreads(profile, update);
DirectMessageThread.createThreads(ref, profile, update);
for (final t in newThreads) { for (final t in newThreads) {
threads.add(t.parentUri); threads.add(t.parentUri);
ref ref
@ -69,7 +69,7 @@ class DirectMessageThreadIds extends _$DirectMessageThreadIds {
text, text,
).future); ).future);
result.match(onSuccess: (newMessage) { result.match(onSuccess: (newMessage) {
DirectMessageThread.createThreads(ref, profile, [newMessage]) DirectMessageThread.createThreads(profile, [newMessage])
.forEach((thread) { .forEach((thread) {
state = [...state, thread.parentUri]; state = [...state, thread.parentUri];
ref ref
@ -97,7 +97,7 @@ class DirectMessageThreadService extends _$DirectMessageThreadService {
threadId = id; threadId = id;
state = DirectMessageThread( state = DirectMessageThread(
messages: [], messages: [],
participants: [], participantIds: [],
title: 'Uninitialized', title: 'Uninitialized',
parentUri: '', parentUri: '',
); );
@ -108,6 +108,16 @@ class DirectMessageThreadService extends _$DirectMessageThreadService {
state = thread; state = thread;
} }
Future<void> refresh() async {
await ref
.read(
updateThreadProvider(profile, state.parentUri, PagingData()).future)
.withResult((messages) {
messages.sort((m1, m2) => m1.createdAt.compareTo(m2.createdAt));
state = state.copy(messages: messages);
});
}
FutureResult<DirectMessage, ExecError> newReplyMessage( FutureResult<DirectMessage, ExecError> newReplyMessage(
DirectMessage original, String text) async { DirectMessage original, String text) async {
if (!state.messages.contains(original)) { if (!state.messages.contains(original)) {
@ -159,6 +169,7 @@ class DirectMessageThreadService extends _$DirectMessageThreadService {
final newState = state.deepCopy(); final newState = state.deepCopy();
newState.messages[oldIndex] = updatedItem; newState.messages[oldIndex] = updatedItem;
update(newState); update(newState);
ref.read(notificationsManagerProvider(profile).notifier).refreshDms();
}, },
onError: (error) { onError: (error) {
_logger.severe( _logger.severe(

Wyświetl plik

@ -7,7 +7,7 @@ part of 'direct_message_services.dart';
// ************************************************************************** // **************************************************************************
String _$directMessageThreadIdsHash() => String _$directMessageThreadIdsHash() =>
r'76d081ab346af0247a62dc58f270a064b355f26f'; r'ce42c0a00ab785c7685f8c135bada67bea2fcb14';
/// Copied from Dart SDK /// Copied from Dart SDK
class _SystemHash { class _SystemHash {
@ -175,7 +175,7 @@ class _DirectMessageThreadIdsProviderElement
} }
String _$directMessageThreadServiceHash() => String _$directMessageThreadServiceHash() =>
r'83940611b62a1bb4616f5ebbb93e1398f34db607'; r'5ef67edc0bb13c3834ed998bd107af36762fde79';
abstract class _$DirectMessageThreadService abstract class _$DirectMessageThreadService
extends BuildlessNotifier<DirectMessageThread> { extends BuildlessNotifier<DirectMessageThread> {

Wyświetl plik

@ -739,7 +739,7 @@ class _EntryTreeManagerProviderElement extends NotifierProviderElement<
String get id => (origin as EntryTreeManagerProvider).id; String get id => (origin as EntryTreeManagerProvider).id;
} }
String _$timelineUpdaterHash() => r'8359f110e4718fb2048d8ce3c0a885bd3d064d28'; String _$timelineUpdaterHash() => r'38d091cafcfa347f52bd3260d7ab72178bd2dc7b';
abstract class _$TimelineUpdater extends BuildlessAutoDisposeNotifier<bool> { abstract class _$TimelineUpdater extends BuildlessAutoDisposeNotifier<bool> {
late final Profile profile; late final Profile profile;

Wyświetl plik

@ -294,7 +294,7 @@ class _VersionErrorStringProviderElement
(origin as VersionErrorStringProvider).feature; (origin as VersionErrorStringProvider).feature;
} }
String _$featureCheckHash() => r'efedd8bae092a28579b6e750088c07e1b35357d6'; String _$featureCheckHash() => r'8e60d2e4278f205aac2e0e434b595cb7aa5b192b';
/// See also [featureCheck]. /// See also [featureCheck].
@ProviderFor(featureCheck) @ProviderFor(featureCheck)

Wyświetl plik

@ -7,7 +7,7 @@ part of 'instance_info_services.dart';
// ************************************************************************** // **************************************************************************
String _$instanceInfoManagerHash() => String _$instanceInfoManagerHash() =>
r'8d591a07234dadde0560ded347cb3499779c4070'; r'88f181cbbee4b4043bf0be55e8379fcc8ad2b8b3';
/// Copied from Dart SDK /// Copied from Dart SDK
class _SystemHash { class _SystemHash {

Wyświetl plik

@ -46,6 +46,36 @@ Future<Result<List<DirectMessage>, ExecError>> directMessages(
return result; return result;
} }
@riverpod
Future<Result<List<DirectMessage>, ExecError>> updateThread(
Ref ref,
Profile profile,
String threadUri,
PagingData page,
) async {
ref.read(directMessageLoadingProvider(profile));
Future.microtask(
() async =>
ref.read(directMessageLoadingProvider(profile).notifier).begin(),
);
final baseUrl =
'https://${profile.serverName}/api/direct_messages/conversation';
final pagingQP = page.toQueryParameters(limitKeyword: 'count');
final url = '$baseUrl?$pagingQP&uri=$threadUri';
final request = Uri.parse(url);
_logger.finest(() => 'Getting direct messages with paging data $page');
final result = (await ref
.read(getApiListRequestProvider(profile, request).future)
.andThenSuccessAsync((response) async => response.data
.map((json) => DirectMessageFriendicaExtension.fromJson(json))
.toList()))
.execErrorCast();
ref.read(directMessageLoadingProvider(profile).notifier).end();
return result;
}
@riverpod @riverpod
Future<Result<DirectMessage, ExecError>> markDirectMessageRead( Future<Result<DirectMessage, ExecError>> markDirectMessageRead(
Ref ref, Ref ref,

Wyświetl plik

@ -180,6 +180,175 @@ class _DirectMessagesProviderElement extends AutoDisposeFutureProviderElement<
PagingData get page => (origin as DirectMessagesProvider).page; PagingData get page => (origin as DirectMessagesProvider).page;
} }
String _$updateThreadHash() => r'425416c431e9f0c0f592c07eef2c3f3618c6fcfb';
/// See also [updateThread].
@ProviderFor(updateThread)
const updateThreadProvider = UpdateThreadFamily();
/// See also [updateThread].
class UpdateThreadFamily
extends Family<AsyncValue<Result<List<DirectMessage>, ExecError>>> {
/// See also [updateThread].
const UpdateThreadFamily();
/// See also [updateThread].
UpdateThreadProvider call(
Profile profile,
String threadUri,
PagingData page,
) {
return UpdateThreadProvider(
profile,
threadUri,
page,
);
}
@override
UpdateThreadProvider getProviderOverride(
covariant UpdateThreadProvider provider,
) {
return call(
provider.profile,
provider.threadUri,
provider.page,
);
}
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'updateThreadProvider';
}
/// See also [updateThread].
class UpdateThreadProvider
extends AutoDisposeFutureProvider<Result<List<DirectMessage>, ExecError>> {
/// See also [updateThread].
UpdateThreadProvider(
Profile profile,
String threadUri,
PagingData page,
) : this._internal(
(ref) => updateThread(
ref as UpdateThreadRef,
profile,
threadUri,
page,
),
from: updateThreadProvider,
name: r'updateThreadProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$updateThreadHash,
dependencies: UpdateThreadFamily._dependencies,
allTransitiveDependencies:
UpdateThreadFamily._allTransitiveDependencies,
profile: profile,
threadUri: threadUri,
page: page,
);
UpdateThreadProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.profile,
required this.threadUri,
required this.page,
}) : super.internal();
final Profile profile;
final String threadUri;
final PagingData page;
@override
Override overrideWith(
FutureOr<Result<List<DirectMessage>, ExecError>> Function(
UpdateThreadRef provider)
create,
) {
return ProviderOverride(
origin: this,
override: UpdateThreadProvider._internal(
(ref) => create(ref as UpdateThreadRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
profile: profile,
threadUri: threadUri,
page: page,
),
);
}
@override
AutoDisposeFutureProviderElement<Result<List<DirectMessage>, ExecError>>
createElement() {
return _UpdateThreadProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is UpdateThreadProvider &&
other.profile == profile &&
other.threadUri == threadUri &&
other.page == page;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, profile.hashCode);
hash = _SystemHash.combine(hash, threadUri.hashCode);
hash = _SystemHash.combine(hash, page.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin UpdateThreadRef
on AutoDisposeFutureProviderRef<Result<List<DirectMessage>, ExecError>> {
/// The parameter `profile` of this provider.
Profile get profile;
/// The parameter `threadUri` of this provider.
String get threadUri;
/// The parameter `page` of this provider.
PagingData get page;
}
class _UpdateThreadProviderElement extends AutoDisposeFutureProviderElement<
Result<List<DirectMessage>, ExecError>> with UpdateThreadRef {
_UpdateThreadProviderElement(super.provider);
@override
Profile get profile => (origin as UpdateThreadProvider).profile;
@override
String get threadUri => (origin as UpdateThreadProvider).threadUri;
@override
PagingData get page => (origin as UpdateThreadProvider).page;
}
String _$markDirectMessageReadHash() => String _$markDirectMessageReadHash() =>
r'62d62dd9e1cf638b0364cdeab742df00da685ccc'; r'62d62dd9e1cf638b0364cdeab742df00da685ccc';

Wyświetl plik

@ -645,7 +645,7 @@ class _GetMyFollowersProviderElement extends AutoDisposeFutureProviderElement<
} }
String _$connectionWithStatusClientHash() => String _$connectionWithStatusClientHash() =>
r'65dde1fad60635c3de95b440ae3046ce47676a9d'; r'687d060bab00296f5aa802713af4ea47989536b4';
/// See also [connectionWithStatusClient]. /// See also [connectionWithStatusClient].
@ProviderFor(connectionWithStatusClient) @ProviderFor(connectionWithStatusClient)

Wyświetl plik

@ -350,7 +350,7 @@ class _DeleteStatusEntryByIdProviderElement
String get id => (origin as DeleteStatusEntryByIdProvider).id; String get id => (origin as DeleteStatusEntryByIdProvider).id;
} }
String _$createNewStatusHash() => r'6c2ddd3e4acd4f01d30c2c8d85dc15e36db83501'; String _$createNewStatusHash() => r'c140a6e3a2776edb93a0e389ef5115d3b2e85fb0';
/// See also [createNewStatus]. /// See also [createNewStatus].
@ProviderFor(createNewStatus) @ProviderFor(createNewStatus)
@ -568,7 +568,7 @@ class _CreateNewStatusProviderElement
Visibility get visibility => (origin as CreateNewStatusProvider).visibility; Visibility get visibility => (origin as CreateNewStatusProvider).visibility;
} }
String _$editStatusHash() => r'94d0d20e834b16f2d8a09feba003ec89b628bf22'; String _$editStatusHash() => r'cad5eeef14a26a7886c129230fa5e7f1c78da7e7';
/// See also [editStatus]. /// See also [editStatus].
@ProviderFor(editStatus) @ProviderFor(editStatus)
@ -922,7 +922,7 @@ class _ResharePostProviderElement
String get id => (origin as ResharePostProvider).id; String get id => (origin as ResharePostProvider).id;
} }
String _$unResharePostHash() => r'4b7ca0426ec3c016624c825af43495ba2a21f17d'; String _$unResharePostHash() => r'6f8ea49b584ca01a2a6d87064fa682d3dc78311d';
/// See also [unResharePost]. /// See also [unResharePost].
@ProviderFor(unResharePost) @ProviderFor(unResharePost)

Wyświetl plik

@ -6,12 +6,14 @@ import 'package:stack_trace/stack_trace.dart';
import '../globals.dart'; import '../globals.dart';
import '../models/auth/profile.dart'; import '../models/auth/profile.dart';
import '../models/connection.dart';
import '../models/exec_error.dart'; import '../models/exec_error.dart';
import '../models/networking/paged_response.dart'; import '../models/networking/paged_response.dart';
import '../models/networking/pages_manager.dart'; import '../models/networking/pages_manager.dart';
import '../models/networking/paging_data.dart'; import '../models/networking/paging_data.dart';
import '../models/user_notification.dart'; import '../models/user_notification.dart';
import '../serializers/mastodon/follow_request_mastodon_extensions.dart'; import '../serializers/mastodon/follow_request_mastodon_extensions.dart';
import 'connection_manager_services.dart';
import 'direct_message_services.dart'; import 'direct_message_services.dart';
import 'feature_checker_services.dart'; import 'feature_checker_services.dart';
import 'follow_requests_services.dart'; import 'follow_requests_services.dart';
@ -62,7 +64,23 @@ class NotificationsManager extends _$NotificationsManager {
Future<void> refreshConnectionRequestNotifications() async { Future<void> refreshConnectionRequestNotifications() async {
_logger.info('refreshConnectionRequestNotifications'); _logger.info('refreshConnectionRequestNotifications');
connectionRequests.clear(); connectionRequests.clear();
await _postFetchOperations([], true, updateDms: false); await _postFetchOperations(
[],
true,
updateDms: false,
updateFollowRequests: true,
);
}
Future<void> refreshDms() async {
_logger.info('refreshDms');
dms.clear();
await _postFetchOperations(
[],
true,
updateDms: true,
updateFollowRequests: false,
);
} }
void clear({bool withListenerNotification = true}) { void clear({bool withListenerNotification = true}) {
@ -254,7 +272,10 @@ class NotificationsManager extends _$NotificationsManager {
.map((id) => ref.watch(directMessageThreadServiceProvider(profile, id))) .map((id) => ref.watch(directMessageThreadServiceProvider(profile, id)))
.where((t) => !t.allSeen) .where((t) => !t.allSeen)
.map((t) { .map((t) {
final fromAccount = t.participants.firstWhere((p) => p.id != myId); final fromAccountId = t.participantIds.firstWhere((pid) => pid != myId);
final fromAccount = ref
.watch(connectionByIdProvider(profile, fromAccountId))
.getValueOrElse(() => Connection());
final latestMessage = final latestMessage =
t.messages.reduce((s, m) => s.createdAt > m.createdAt ? s : m); t.messages.reduce((s, m) => s.createdAt > m.createdAt ? s : m);
return UserNotification( return UserNotification(

Wyświetl plik

@ -7,7 +7,7 @@ part of 'notification_services.dart';
// ************************************************************************** // **************************************************************************
String _$notificationsManagerHash() => String _$notificationsManagerHash() =>
r'c7a691dce74c2a2db04fd78a459a13c9d8256e18'; r'dc6a88e34b4d6bc3cea3541aba7453e223374842';
/// Copied from Dart SDK /// Copied from Dart SDK
class _SystemHash { class _SystemHash {

Wyświetl plik

@ -6,7 +6,7 @@ part of 'persistent_info_services.dart';
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$persistentInfoHash() => r'8881204fcc60dcf435340cc5c28e1e293c97dbe3'; String _$persistentInfoHash() => r'62bf2f76f88d746cf75f4b663d76302d4c3213fd';
/// Copied from Dart SDK /// Copied from Dart SDK
class _SystemHash { class _SystemHash {

Wyświetl plik

@ -329,7 +329,7 @@ class _CheckTimelineEntryProviderElement
} }
String _$timelineEntryFiltersHash() => String _$timelineEntryFiltersHash() =>
r'dd386f6ecd047a1dcd8602451c7a445510b03f7d'; r'cd4344f10166b5cabb7869c350e6d83f571cb20d';
abstract class _$TimelineEntryFilters abstract class _$TimelineEntryFilters
extends BuildlessNotifier<Map<String, TimelineEntryFilter>> { extends BuildlessNotifier<Map<String, TimelineEntryFilter>> {

Wyświetl plik

@ -173,7 +173,7 @@ class _TimelineMaintainerProviderElement extends NotifierProviderElement<
Profile get profile => (origin as TimelineMaintainerProvider).profile; Profile get profile => (origin as TimelineMaintainerProvider).profile;
} }
String _$timelineManagerHash() => r'fa6c46745b69e61ce3dd8cb07822d428ea8b66f5'; String _$timelineManagerHash() => r'9f806c36aeb207547fde5a352df1c1fb051cc9af';
abstract class _$TimelineManager extends BuildlessNotifier<Timeline> { abstract class _$TimelineManager extends BuildlessNotifier<Timeline> {
late final Profile profile; late final Profile profile;

Wyświetl plik

@ -6,10 +6,14 @@ import '../controls/image_control.dart';
import '../controls/padding.dart'; import '../controls/padding.dart';
import '../controls/responsive_max_width.dart'; import '../controls/responsive_max_width.dart';
import '../controls/standard_appbar.dart'; import '../controls/standard_appbar.dart';
import '../controls/status_and_refresh_button.dart';
import '../models/auth/profile.dart'; import '../models/auth/profile.dart';
import '../models/direct_message.dart';
import '../models/direct_message_thread.dart'; import '../models/direct_message_thread.dart';
import '../riverpod_controllers/account_services.dart'; import '../riverpod_controllers/account_services.dart';
import '../riverpod_controllers/connection_manager_services.dart';
import '../riverpod_controllers/direct_message_services.dart'; import '../riverpod_controllers/direct_message_services.dart';
import '../riverpod_controllers/networking/network_status_services.dart';
import '../utils/clipboard_utils.dart'; import '../utils/clipboard_utils.dart';
import '../utils/snackbar_builder.dart'; import '../utils/snackbar_builder.dart';
@ -29,63 +33,57 @@ class MessageThreadScreen extends ConsumerStatefulWidget {
class _MessageThreadScreenState extends ConsumerState<MessageThreadScreen> { class _MessageThreadScreenState extends ConsumerState<MessageThreadScreen> {
final textController = TextEditingController(); final textController = TextEditingController();
@override
void initState() {
super.initState();
ref
.read(directMessageThreadServiceProvider(
ref.read(activeProfileProvider),
widget.parentThreadId,
).notifier)
.refresh();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final profile = ref.watch(activeProfileProvider); final profile = ref.watch(activeProfileProvider);
final t = ref.watch( final loading = ref.watch(directMessageLoadingProvider(profile));
final thread = ref.watch(
directMessageThreadServiceProvider(profile, widget.parentThreadId)); directMessageThreadServiceProvider(profile, widget.parentThreadId));
final title = t.title.isEmpty ? 'Thread' : t.title; final title = thread.title.isEmpty ? 'Thread' : thread.title;
return Scaffold( return Scaffold(
appBar: StandardAppBar.build(context, title), appBar: StandardAppBar.build(context, title, actions: [
body: buildBody(profile, t), StatusAndRefreshButton3(
executing: loading,
refreshFunction: () async => await ref
.read(directMessageThreadServiceProvider(
profile,
widget.parentThreadId,
).notifier)
.refresh(),
busyColor: Theme.of(context).colorScheme.surface,
),
]),
body: buildBody(profile, thread, loading),
); );
} }
Widget buildBody( Widget buildBody(
Profile profile, Profile profile,
DirectMessageThread thread, DirectMessageThread thread,
bool loading,
) { ) {
final service = ref.read(
directMessageThreadServiceProvider(profile, thread.parentUri).notifier);
final yourId = profile.userId;
final yourAvatarUrl = profile.avatar;
final participants =
Map.fromEntries(thread.participants.map((p) => MapEntry(p.id, p)));
return Center( return Center(
child: Column( child: Column(
children: [ children: [
if (loading) const LinearProgressIndicator(),
Expanded( Expanded(
child: ResponsiveMaxWidth( child: ResponsiveMaxWidth(
child: ListView.separated( child: ListView.separated(
itemBuilder: (context, index) { itemBuilder: (context, index) {
final m = thread.messages[index]; final m = thread.messages[index];
final textPieces = m.text.split('...\n'); return _DirectMessageListItem(
final text = m, profile, widget.parentThreadId);
textPieces.length == 1 ? textPieces[0] : textPieces[1];
final imageUrl = m.senderId == yourId
? yourAvatarUrl
: participants[m.senderId]?.avatarUrl ?? '';
return ListTile(
onTap: m.seen ? null : () => service.markMessageRead(m),
onLongPress: () async {
await copyToClipboard(context: context, text: m.text);
},
leading: ImageControl(
imageUrl: imageUrl,
iconOverride: const Icon(Icons.person),
width: 32.0,
onTap: null,
),
title: Text(
text,
style: m.seen
? null
: const TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text(
DateTime.fromMillisecondsSinceEpoch(m.createdAt * 1000)
.toString()),
);
}, },
separatorBuilder: (_, __) => const Divider(), separatorBuilder: (_, __) => const Divider(),
itemCount: thread.messages.length), itemCount: thread.messages.length),
@ -119,13 +117,16 @@ class _MessageThreadScreenState extends ConsumerState<MessageThreadScreen> {
return; return;
} }
final othersMessages = final othersMessages =
thread.messages.where((m) => m.senderId != yourId); thread.messages.where((m) => m.senderId != profile.userId);
if (othersMessages.isEmpty) { if (othersMessages.isEmpty) {
buildSnackbar( buildSnackbar(
context, "Have to wait for a response before sending"); context, "Have to wait for a response before sending");
return; return;
} }
await service await ref
.read(directMessageThreadServiceProvider(
profile, thread.parentUri)
.notifier)
.newReplyMessage( .newReplyMessage(
othersMessages.last, othersMessages.last,
textController.text, textController.text,
@ -147,3 +148,46 @@ class _MessageThreadScreenState extends ConsumerState<MessageThreadScreen> {
)); ));
} }
} }
class _DirectMessageListItem extends ConsumerWidget {
final DirectMessage message;
final Profile profile;
final String threadId;
const _DirectMessageListItem(this.message, this.profile, this.threadId);
@override
Widget build(BuildContext context, WidgetRef ref) {
final senderAvatar = ref
.read(connectionByIdProvider(profile, message.senderId))
.fold(onSuccess: (c) => c.avatarUrl, onError: (_) => '');
final m = message;
final textPieces = m.text.split('...\n');
final text = textPieces.length == 1 ? textPieces[0] : textPieces[1];
final imageUrl =
m.senderId == profile.userId ? profile.avatar : senderAvatar;
return ListTile(
onTap: m.seen
? null
: () => ref
.read(DirectMessageThreadServiceProvider(profile, threadId)
.notifier)
.markMessageRead(m),
onLongPress: () async {
await copyToClipboard(context: context, text: m.text);
},
leading: ImageControl(
imageUrl: imageUrl,
iconOverride: const Icon(Icons.person),
width: 32.0,
onTap: null,
),
title: Text(
text,
style: m.seen ? null : const TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text(
DateTime.fromMillisecondsSinceEpoch(m.createdAt * 1000).toString()),
);
}
}

Wyświetl plik

@ -8,6 +8,7 @@ import '../controls/standard_appbar.dart';
import '../controls/status_and_refresh_button.dart'; import '../controls/status_and_refresh_button.dart';
import '../models/auth/profile.dart'; import '../models/auth/profile.dart';
import '../riverpod_controllers/account_services.dart'; import '../riverpod_controllers/account_services.dart';
import '../riverpod_controllers/connection_manager_services.dart';
import '../riverpod_controllers/direct_message_services.dart'; import '../riverpod_controllers/direct_message_services.dart';
import '../riverpod_controllers/networking/network_status_services.dart'; import '../riverpod_controllers/networking/network_status_services.dart';
import '../routes.dart'; import '../routes.dart';
@ -19,13 +20,16 @@ class MessagesScreen extends ConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final profile = ref.watch(activeProfileProvider); final profile = ref.watch(activeProfileProvider);
final service = ref.watch(directMessageThreadIdsProvider(profile).notifier); ref.watch(directMessageThreadIdsProvider(profile));
final loading = ref.watch(directMessageLoadingProvider(profile));
return Scaffold( return Scaffold(
appBar: StandardAppBar.build(context, 'Direct Message Threads', actions: [ appBar: StandardAppBar.build(context, 'Direct Message Threads', actions: [
StatusAndRefreshButton2( StatusAndRefreshButton3(
watcher: (ref2) => ref2.watch(directMessageLoadingProvider(profile)), executing: loading,
refreshFunction: () async => await service.update(), refreshFunction: () async => await ref
.read(directMessageThreadIdsProvider(profile).notifier)
.update(),
busyColor: Theme.of(context).colorScheme.surface, busyColor: Theme.of(context).colorScheme.surface,
), ),
IconButton( IconButton(
@ -37,34 +41,59 @@ class MessagesScreen extends ConsumerWidget {
]), ]),
body: RefreshIndicator( body: RefreshIndicator(
onRefresh: () async { onRefresh: () async {
await service.update(); await ref
.read(directMessageThreadIdsProvider(profile).notifier)
.update();
}, },
child: Center(child: buildBody(profile, ref)), child: Center(
child: Column(
children: [
if (loading) const LinearProgressIndicator(),
Expanded(child: buildBody(profile, ref))
],
)),
), ),
); );
} }
Widget buildBody(Profile profile, WidgetRef ref) { Widget buildBody(Profile profile, WidgetRef ref) {
final threadIds = ref.watch(directMessageThreadIdsProvider(profile)); final threadIds = ref.watch(directMessageThreadIdsProvider(profile));
return threadIds.isEmpty return threadIds.isEmpty
? const Text('No Direct Message Threads') ? const Text('No Direct Message Threads')
: ResponsiveMaxWidth( : ResponsiveMaxWidth(
child: ListView.separated( child: ListView.separated(
itemCount: threadIds.length, itemCount: threadIds.length,
itemBuilder: (context, index) { itemBuilder: (context, i) {
final index = i;
final threadId = threadIds[index]; final threadId = threadIds[index];
final thread = ref.watch( final thread = ref.watch(
directMessageThreadServiceProvider(profile, threadId)); directMessageThreadServiceProvider(profile, threadId));
final style = thread.allSeen final style = thread.allSeen
? null ? null
: const TextStyle(fontWeight: FontWeight.bold); : const TextStyle(fontWeight: FontWeight.bold);
final participantsAvatars = <String>[];
final participantNames = <String>[];
for (final pid in thread.participantIds) {
if (pid == profile.userId) {
continue;
}
ref.watch(connectionByIdProvider(profile, pid)).match(
onSuccess: (c) {
participantsAvatars.add(c.avatarUrl);
participantNames.add('${c.name}(${c.handle})');
},
onError: (_) => participantNames.add('Person 1'));
}
return ListTile( return ListTile(
onTap: () => context.pushNamed( onTap: () => context.pushNamed(
ScreenPaths.thread, ScreenPaths.thread,
queryParameters: {'uri': thread.parentUri}, queryParameters: {'uri': thread.parentUri},
), ),
leading: ImageControl( leading: thread.participantIds.isEmpty
imageUrl: thread.participants.first.avatarUrl.toString(), ? null
: ImageControl(
imageUrl: participantsAvatars.first,
iconOverride: const Icon(Icons.person), iconOverride: const Icon(Icons.person),
width: 32.0, width: 32.0,
onTap: null, onTap: null,
@ -72,9 +101,8 @@ class MessagesScreen extends ConsumerWidget {
title: Text( title: Text(
[ [
'You', 'You',
...thread.participants ...participantNames,
.map((p) => '${p.name}(${p.handle})') ].join(participantNames.length == 1 ? ' & ' : ', '),
].join(thread.participants.length == 1 ? ' & ' : ', '),
softWrap: true, softWrap: true,
style: style, style: style,
), ),
@ -83,7 +111,9 @@ class MessagesScreen extends ConsumerWidget {
style: style, style: style,
), ),
trailing: Text( trailing: Text(
ElapsedDateUtils.elapsedTimeStringFromEpochSeconds( thread.messages.isEmpty
? ''
: ElapsedDateUtils.elapsedTimeStringFromEpochSeconds(
thread.messages.last.createdAt), thread.messages.last.createdAt),
style: style, style: style,
), ),

Wyświetl plik

@ -40,21 +40,6 @@ extension DirectMessageFriendicaExtension on DirectMessage {
final String parentUri = json['friendica_parent_uri']; final String parentUri = json['friendica_parent_uri'];
// TODO Add back when RP conversion is complete
// getIt<ActiveProfileSelector<ConnectionsManager>>()
// .activeEntry
// .andThenSuccess((cm) {
// if (getIt<AccountsService>().currentProfile.userId != senderId) {
// final s = ConnectionFriendicaExtensions.fromJson(json['sender']);
// cm.upsertConnection(s);
// }
//
// if (getIt<AccountsService>().currentProfile.userId != recipientId) {
// final r = ConnectionFriendicaExtensions.fromJson(json['recipient']);
// cm.upsertConnection(r);
// }
// });
return DirectMessage( return DirectMessage(
id: id, id: id,
senderId: senderId, senderId: senderId,