mirror of
https://gitlab.com/mysocialportal/relatica
synced 2024-10-18 10:03:31 +00:00
Convert DirectMessage System to Riverpod
This commit is contained in:
parent
bb0a6bd36b
commit
f0ab80d312
12 changed files with 729 additions and 371 deletions
|
@ -18,7 +18,6 @@ import 'models/instance_info.dart';
|
|||
import 'services/auth_service.dart';
|
||||
import 'services/blocks_manager.dart';
|
||||
import 'services/connections_manager.dart';
|
||||
import 'services/direct_message_service.dart';
|
||||
import 'services/entry_manager_service.dart';
|
||||
import 'services/feature_version_checker.dart';
|
||||
import 'services/fediverse_server_validator.dart';
|
||||
|
@ -128,9 +127,6 @@ Future<void> dependencyInjectionInitialization() async {
|
|||
getIt.registerSingleton<ActiveProfileSelector<FollowRequestsManager>>(
|
||||
ActiveProfileSelector((p) => FollowRequestsManager(p))
|
||||
..subscribeToProfileSwaps());
|
||||
getIt.registerSingleton<ActiveProfileSelector<DirectMessageService>>(
|
||||
ActiveProfileSelector((p) => DirectMessageService(p))
|
||||
..subscribeToProfileSwaps());
|
||||
getIt.registerSingleton<ActiveProfileSelector<InteractionsManager>>(
|
||||
ActiveProfileSelector((p) => InteractionsManager(p))
|
||||
..subscribeToProfileSwaps());
|
||||
|
@ -167,12 +163,6 @@ void clearCaches() {
|
|||
_logger.severe('Error clearing IConnections Repo: $error'),
|
||||
);
|
||||
|
||||
getIt<ActiveProfileSelector<DirectMessageService>>().activeEntry.match(
|
||||
onSuccess: (service) => service.clear(),
|
||||
onError: (error) =>
|
||||
_logger.severe('Error clearing DirectMessageService Repo: $error'),
|
||||
);
|
||||
|
||||
getIt<ActiveProfileSelector<EntryManagerService>>().activeEntry.match(
|
||||
onSuccess: (service) => service.clear(),
|
||||
onError: (error) =>
|
||||
|
|
|
@ -16,7 +16,6 @@ import 'routes.dart';
|
|||
import 'services/auth_service.dart';
|
||||
import 'services/blocks_manager.dart';
|
||||
import 'services/connections_manager.dart';
|
||||
import 'services/direct_message_service.dart';
|
||||
import 'services/entry_manager_service.dart';
|
||||
import 'services/follow_requests_manager.dart';
|
||||
import 'services/gallery_service.dart';
|
||||
|
@ -147,11 +146,6 @@ class _AppState extends fr.ConsumerState<App> {
|
|||
create: (_) =>
|
||||
getIt<ActiveProfileSelector<FollowRequestsManager>>(),
|
||||
),
|
||||
ChangeNotifierProvider<
|
||||
ActiveProfileSelector<DirectMessageService>>(
|
||||
create: (_) =>
|
||||
getIt<ActiveProfileSelector<DirectMessageService>>(),
|
||||
),
|
||||
ChangeNotifierProvider<
|
||||
ActiveProfileSelector<InteractionsManager>>(
|
||||
create: (_) =>
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'basic_credentials.dart';
|
||||
import 'credentials_intf.dart';
|
||||
|
||||
class Profile {
|
||||
|
@ -30,6 +31,15 @@ class Profile {
|
|||
loggedIn: false,
|
||||
);
|
||||
|
||||
factory Profile.empty() => Profile(
|
||||
credentials: BasicCredentials.empty(),
|
||||
username: '',
|
||||
userId: '',
|
||||
avatar: '',
|
||||
serverName: '',
|
||||
loggedIn: false,
|
||||
);
|
||||
|
||||
factory Profile.fromJson(
|
||||
Map<String, dynamic> json,
|
||||
ICredentials Function(Map<String, dynamic> json) credentialsFromJson,
|
||||
|
|
|
@ -62,8 +62,27 @@ class DirectMessage {
|
|||
identical(this, other) ||
|
||||
other is DirectMessage &&
|
||||
runtimeType == other.runtimeType &&
|
||||
id == other.id;
|
||||
id == other.id &&
|
||||
senderId == other.senderId &&
|
||||
senderScreenName == other.senderScreenName &&
|
||||
recipientId == other.recipientId &&
|
||||
recipientScreenName == other.recipientScreenName &&
|
||||
title == other.title &&
|
||||
text == other.text &&
|
||||
createdAt == other.createdAt &&
|
||||
seen == other.seen &&
|
||||
parentUri == other.parentUri;
|
||||
|
||||
@override
|
||||
int get hashCode => id.hashCode;
|
||||
int get hashCode =>
|
||||
id.hashCode ^
|
||||
senderId.hashCode ^
|
||||
senderScreenName.hashCode ^
|
||||
recipientId.hashCode ^
|
||||
recipientScreenName.hashCode ^
|
||||
title.hashCode ^
|
||||
text.hashCode ^
|
||||
createdAt.hashCode ^
|
||||
seen.hashCode ^
|
||||
parentUri.hashCode;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
import '../globals.dart';
|
||||
|
@ -23,6 +24,13 @@ class DirectMessageThread {
|
|||
required this.parentUri,
|
||||
});
|
||||
|
||||
DirectMessageThread deepCopy() => DirectMessageThread(
|
||||
messages: List.from(messages),
|
||||
participants: List.from(participants),
|
||||
title: title,
|
||||
parentUri: parentUri,
|
||||
);
|
||||
|
||||
get allSeen => messages.isEmpty
|
||||
? false
|
||||
: messages
|
||||
|
@ -83,8 +91,15 @@ class DirectMessageThread {
|
|||
identical(this, other) ||
|
||||
other is DirectMessageThread &&
|
||||
runtimeType == other.runtimeType &&
|
||||
parentUri == other.parentUri;
|
||||
title == other.title &&
|
||||
parentUri == other.parentUri &&
|
||||
listEquals(messages, other.messages) &&
|
||||
listEquals(participants, other.participants);
|
||||
|
||||
@override
|
||||
int get hashCode => parentUri.hashCode;
|
||||
int get hashCode =>
|
||||
title.hashCode ^
|
||||
parentUri.hashCode ^
|
||||
Object.hashAll(messages) ^
|
||||
Object.hashAll(participants);
|
||||
}
|
||||
|
|
163
lib/riverpod_controllers/direct_message_services.dart
Normal file
163
lib/riverpod_controllers/direct_message_services.dart
Normal file
|
@ -0,0 +1,163 @@
|
|||
import 'package:logging/logging.dart';
|
||||
import 'package:relatica/models/connection.dart';
|
||||
import 'package:relatica/models/direct_message_thread.dart';
|
||||
import 'package:relatica/models/exec_error.dart';
|
||||
import 'package:result_monad/result_monad.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
import '../friendica_client/friendica_client.dart';
|
||||
import '../friendica_client/paging_data.dart';
|
||||
import '../globals.dart';
|
||||
import '../models/auth/oauth_credentials.dart';
|
||||
import '../models/auth/profile.dart';
|
||||
import '../models/direct_message.dart';
|
||||
import '../services/feature_version_checker.dart';
|
||||
|
||||
part 'direct_message_services.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class DirectMessageThreadIds extends _$DirectMessageThreadIds {
|
||||
static final _logger = Logger('DirectMessageThreadIdsProvider');
|
||||
late Profile userProfile;
|
||||
|
||||
@override
|
||||
List<String> build(Profile profile) {
|
||||
userProfile = profile;
|
||||
update();
|
||||
return [];
|
||||
}
|
||||
|
||||
Future<void> update() async {
|
||||
final threads = <String>[];
|
||||
await DirectMessagingClient(userProfile)
|
||||
.getDirectMessages(PagingData())
|
||||
.match(
|
||||
onSuccess: (update) {
|
||||
final newThreads = DirectMessageThread.createThreads(update);
|
||||
for (final t in newThreads) {
|
||||
threads.add(t.parentUri);
|
||||
ref
|
||||
.read(directMessageThreadServiceProvider(userProfile, t.parentUri)
|
||||
.notifier)
|
||||
.update(t);
|
||||
}
|
||||
_logger.fine(
|
||||
'Updated ${update.length} direct messages, across ${newThreads.length} threads');
|
||||
},
|
||||
onError: (error) {
|
||||
_logger.severe('Error getting direct messages: $error');
|
||||
},
|
||||
);
|
||||
state = threads;
|
||||
}
|
||||
|
||||
FutureResult<DirectMessage, ExecError> newThread(
|
||||
Connection receiver, String text) async {
|
||||
if (userProfile.credentials is OAuthCredentials) {
|
||||
final result = getIt<FriendicaVersionChecker>()
|
||||
.canUseFeatureResult(RelaticaFeatures.directMessageCreation);
|
||||
if (result.isFailure) {
|
||||
return result.errorCast();
|
||||
}
|
||||
}
|
||||
|
||||
final result = await DirectMessagingClient(userProfile).postDirectMessage(
|
||||
null,
|
||||
receiver.id,
|
||||
text,
|
||||
);
|
||||
result.match(onSuccess: (newMessage) {
|
||||
DirectMessageThread.createThreads([newMessage]).forEach((thread) {
|
||||
state = [...state, thread.parentUri];
|
||||
ref
|
||||
.read(directMessageThreadServiceProvider(
|
||||
userProfile, thread.parentUri)
|
||||
.notifier)
|
||||
.update(thread);
|
||||
});
|
||||
}, onError: (error) {
|
||||
_logger.severe('Error getting direct messages: $error');
|
||||
});
|
||||
|
||||
ref.invalidateSelf();
|
||||
return result.execErrorCast();
|
||||
}
|
||||
}
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class DirectMessageThreadService extends _$DirectMessageThreadService {
|
||||
static final _logger = Logger('DirectMessageThreadServiceProvider');
|
||||
String threadId = '';
|
||||
|
||||
@override
|
||||
DirectMessageThread build(Profile profile, String id) {
|
||||
_logger.severe('build id = $id');
|
||||
threadId = id;
|
||||
state = DirectMessageThread(
|
||||
messages: [],
|
||||
participants: [],
|
||||
title: 'Uninitialized',
|
||||
parentUri: '',
|
||||
);
|
||||
return state;
|
||||
}
|
||||
|
||||
void update(DirectMessageThread thread) {
|
||||
print('OldThread == NewThread? ${state == thread}');
|
||||
state = thread;
|
||||
}
|
||||
|
||||
FutureResult<DirectMessage, ExecError> newReplyMessage(
|
||||
DirectMessage original, String text) async {
|
||||
if (!state.messages.contains(original)) {
|
||||
final error =
|
||||
'Message is not for this thread: ${state.parentUri}, $original';
|
||||
_logger.severe(error);
|
||||
return buildErrorResult(
|
||||
type: ErrorType.notFound,
|
||||
message: error,
|
||||
);
|
||||
}
|
||||
|
||||
if (profile.credentials is OAuthCredentials) {
|
||||
final result = getIt<FriendicaVersionChecker>()
|
||||
.canUseFeatureResult(RelaticaFeatures.directMessageCreation);
|
||||
if (result.isFailure) {
|
||||
return result.errorCast();
|
||||
}
|
||||
}
|
||||
|
||||
final result = await DirectMessagingClient(profile).postDirectMessage(
|
||||
original.id,
|
||||
original.senderId,
|
||||
text,
|
||||
);
|
||||
result.match(onSuccess: (newMessage) {
|
||||
state.messages.add(newMessage);
|
||||
}, onError: (error) {
|
||||
_logger.severe('Error getting direct messages: $error');
|
||||
});
|
||||
|
||||
update(state);
|
||||
return result.execErrorCast();
|
||||
}
|
||||
|
||||
Future<void> markMessageRead(DirectMessage m) async {
|
||||
final oldIndex = state.messages.indexOf(m);
|
||||
if (oldIndex < 0) {
|
||||
_logger.severe('Message is not for this thread: ${state.parentUri}, $m');
|
||||
return;
|
||||
}
|
||||
|
||||
await DirectMessagingClient(profile).markDirectMessageRead(m).match(
|
||||
onSuccess: (updatedItem) {
|
||||
final newState = state.deepCopy();
|
||||
newState.messages[oldIndex] = updatedItem;
|
||||
update(newState);
|
||||
},
|
||||
onError: (error) {
|
||||
_logger.severe('Error getting direct messages: $error');
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
344
lib/riverpod_controllers/direct_message_services.g.dart
Normal file
344
lib/riverpod_controllers/direct_message_services.g.dart
Normal file
|
@ -0,0 +1,344 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'direct_message_services.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$directMessageThreadIdsHash() =>
|
||||
r'30b269250935a6966d4fa47c479d29a4bb562729';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
_SystemHash._();
|
||||
|
||||
static int combine(int hash, int value) {
|
||||
// ignore: parameter_assignments
|
||||
hash = 0x1fffffff & (hash + value);
|
||||
// ignore: parameter_assignments
|
||||
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
|
||||
return hash ^ (hash >> 6);
|
||||
}
|
||||
|
||||
static int finish(int hash) {
|
||||
// ignore: parameter_assignments
|
||||
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
|
||||
// ignore: parameter_assignments
|
||||
hash = hash ^ (hash >> 11);
|
||||
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _$DirectMessageThreadIds
|
||||
extends BuildlessNotifier<List<String>> {
|
||||
late final Profile profile;
|
||||
|
||||
List<String> build(
|
||||
Profile profile,
|
||||
);
|
||||
}
|
||||
|
||||
/// See also [DirectMessageThreadIds].
|
||||
@ProviderFor(DirectMessageThreadIds)
|
||||
const directMessageThreadIdsProvider = DirectMessageThreadIdsFamily();
|
||||
|
||||
/// See also [DirectMessageThreadIds].
|
||||
class DirectMessageThreadIdsFamily extends Family<List<String>> {
|
||||
/// See also [DirectMessageThreadIds].
|
||||
const DirectMessageThreadIdsFamily();
|
||||
|
||||
/// See also [DirectMessageThreadIds].
|
||||
DirectMessageThreadIdsProvider call(
|
||||
Profile profile,
|
||||
) {
|
||||
return DirectMessageThreadIdsProvider(
|
||||
profile,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
DirectMessageThreadIdsProvider getProviderOverride(
|
||||
covariant DirectMessageThreadIdsProvider 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'directMessageThreadIdsProvider';
|
||||
}
|
||||
|
||||
/// See also [DirectMessageThreadIds].
|
||||
class DirectMessageThreadIdsProvider
|
||||
extends NotifierProviderImpl<DirectMessageThreadIds, List<String>> {
|
||||
/// See also [DirectMessageThreadIds].
|
||||
DirectMessageThreadIdsProvider(
|
||||
Profile profile,
|
||||
) : this._internal(
|
||||
() => DirectMessageThreadIds()..profile = profile,
|
||||
from: directMessageThreadIdsProvider,
|
||||
name: r'directMessageThreadIdsProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$directMessageThreadIdsHash,
|
||||
dependencies: DirectMessageThreadIdsFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
DirectMessageThreadIdsFamily._allTransitiveDependencies,
|
||||
profile: profile,
|
||||
);
|
||||
|
||||
DirectMessageThreadIdsProvider._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
|
||||
List<String> runNotifierBuild(
|
||||
covariant DirectMessageThreadIds notifier,
|
||||
) {
|
||||
return notifier.build(
|
||||
profile,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Override overrideWith(DirectMessageThreadIds Function() create) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: DirectMessageThreadIdsProvider._internal(
|
||||
() => create()..profile = profile,
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
profile: profile,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
NotifierProviderElement<DirectMessageThreadIds, List<String>>
|
||||
createElement() {
|
||||
return _DirectMessageThreadIdsProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is DirectMessageThreadIdsProvider && other.profile == profile;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, profile.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
mixin DirectMessageThreadIdsRef on NotifierProviderRef<List<String>> {
|
||||
/// The parameter `profile` of this provider.
|
||||
Profile get profile;
|
||||
}
|
||||
|
||||
class _DirectMessageThreadIdsProviderElement
|
||||
extends NotifierProviderElement<DirectMessageThreadIds, List<String>>
|
||||
with DirectMessageThreadIdsRef {
|
||||
_DirectMessageThreadIdsProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
Profile get profile => (origin as DirectMessageThreadIdsProvider).profile;
|
||||
}
|
||||
|
||||
String _$directMessageThreadServiceHash() =>
|
||||
r'f6a518e07e5e017ef0e2b9f1821408bbca77aef5';
|
||||
|
||||
abstract class _$DirectMessageThreadService
|
||||
extends BuildlessNotifier<DirectMessageThread> {
|
||||
late final Profile profile;
|
||||
late final String id;
|
||||
|
||||
DirectMessageThread build(
|
||||
Profile profile,
|
||||
String id,
|
||||
);
|
||||
}
|
||||
|
||||
/// See also [DirectMessageThreadService].
|
||||
@ProviderFor(DirectMessageThreadService)
|
||||
const directMessageThreadServiceProvider = DirectMessageThreadServiceFamily();
|
||||
|
||||
/// See also [DirectMessageThreadService].
|
||||
class DirectMessageThreadServiceFamily extends Family<DirectMessageThread> {
|
||||
/// See also [DirectMessageThreadService].
|
||||
const DirectMessageThreadServiceFamily();
|
||||
|
||||
/// See also [DirectMessageThreadService].
|
||||
DirectMessageThreadServiceProvider call(
|
||||
Profile profile,
|
||||
String id,
|
||||
) {
|
||||
return DirectMessageThreadServiceProvider(
|
||||
profile,
|
||||
id,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
DirectMessageThreadServiceProvider getProviderOverride(
|
||||
covariant DirectMessageThreadServiceProvider provider,
|
||||
) {
|
||||
return call(
|
||||
provider.profile,
|
||||
provider.id,
|
||||
);
|
||||
}
|
||||
|
||||
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'directMessageThreadServiceProvider';
|
||||
}
|
||||
|
||||
/// See also [DirectMessageThreadService].
|
||||
class DirectMessageThreadServiceProvider extends NotifierProviderImpl<
|
||||
DirectMessageThreadService, DirectMessageThread> {
|
||||
/// See also [DirectMessageThreadService].
|
||||
DirectMessageThreadServiceProvider(
|
||||
Profile profile,
|
||||
String id,
|
||||
) : this._internal(
|
||||
() => DirectMessageThreadService()
|
||||
..profile = profile
|
||||
..id = id,
|
||||
from: directMessageThreadServiceProvider,
|
||||
name: r'directMessageThreadServiceProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$directMessageThreadServiceHash,
|
||||
dependencies: DirectMessageThreadServiceFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
DirectMessageThreadServiceFamily._allTransitiveDependencies,
|
||||
profile: profile,
|
||||
id: id,
|
||||
);
|
||||
|
||||
DirectMessageThreadServiceProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.profile,
|
||||
required this.id,
|
||||
}) : super.internal();
|
||||
|
||||
final Profile profile;
|
||||
final String id;
|
||||
|
||||
@override
|
||||
DirectMessageThread runNotifierBuild(
|
||||
covariant DirectMessageThreadService notifier,
|
||||
) {
|
||||
return notifier.build(
|
||||
profile,
|
||||
id,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Override overrideWith(DirectMessageThreadService Function() create) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: DirectMessageThreadServiceProvider._internal(
|
||||
() => create()
|
||||
..profile = profile
|
||||
..id = id,
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
profile: profile,
|
||||
id: id,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
NotifierProviderElement<DirectMessageThreadService, DirectMessageThread>
|
||||
createElement() {
|
||||
return _DirectMessageThreadServiceProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is DirectMessageThreadServiceProvider &&
|
||||
other.profile == profile &&
|
||||
other.id == id;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, profile.hashCode);
|
||||
hash = _SystemHash.combine(hash, id.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
mixin DirectMessageThreadServiceRef
|
||||
on NotifierProviderRef<DirectMessageThread> {
|
||||
/// The parameter `profile` of this provider.
|
||||
Profile get profile;
|
||||
|
||||
/// The parameter `id` of this provider.
|
||||
String get id;
|
||||
}
|
||||
|
||||
class _DirectMessageThreadServiceProviderElement
|
||||
extends NotifierProviderElement<DirectMessageThreadService,
|
||||
DirectMessageThread> with DirectMessageThreadServiceRef {
|
||||
_DirectMessageThreadServiceProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
Profile get profile => (origin as DirectMessageThreadServiceProvider).profile;
|
||||
@override
|
||||
String get id => (origin as DirectMessageThreadServiceProvider).id;
|
||||
}
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:result_monad/result_monad.dart';
|
||||
|
||||
|
@ -7,15 +8,14 @@ import '../controls/padding.dart';
|
|||
import '../controls/responsive_max_width.dart';
|
||||
import '../controls/standard_appbar.dart';
|
||||
import '../globals.dart';
|
||||
import '../models/auth/profile.dart';
|
||||
import '../models/direct_message_thread.dart';
|
||||
import '../models/exec_error.dart';
|
||||
import '../riverpod_controllers/direct_message_services.dart';
|
||||
import '../services/auth_service.dart';
|
||||
import '../services/direct_message_service.dart';
|
||||
import '../utils/active_profile_selector.dart';
|
||||
import '../utils/clipboard_utils.dart';
|
||||
import '../utils/snackbar_builder.dart';
|
||||
|
||||
class MessageThreadScreen extends StatefulWidget {
|
||||
class MessageThreadScreen extends ConsumerStatefulWidget {
|
||||
final String parentThreadId;
|
||||
|
||||
const MessageThreadScreen({
|
||||
|
@ -24,141 +24,128 @@ class MessageThreadScreen extends StatefulWidget {
|
|||
});
|
||||
|
||||
@override
|
||||
State<MessageThreadScreen> createState() => _MessageThreadScreenState();
|
||||
ConsumerState<MessageThreadScreen> createState() =>
|
||||
_MessageThreadScreenState();
|
||||
}
|
||||
|
||||
class _MessageThreadScreenState extends State<MessageThreadScreen> {
|
||||
class _MessageThreadScreenState extends ConsumerState<MessageThreadScreen> {
|
||||
final textController = TextEditingController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final service = context
|
||||
.watch<ActiveProfileSelector<DirectMessageService>>()
|
||||
.activeEntry
|
||||
.value;
|
||||
final result = service.getThreadByParentUri(widget.parentThreadId);
|
||||
final title = result.fold(
|
||||
onSuccess: (t) => t.title.isEmpty ? 'Thread' : t.title,
|
||||
onError: (_) => 'Thread');
|
||||
final profile = context.watch<AccountsService>().currentProfile;
|
||||
final t = ref.watch(
|
||||
directMessageThreadServiceProvider(profile, widget.parentThreadId));
|
||||
final title = t.title.isEmpty ? 'Thread' : t.title;
|
||||
return Scaffold(
|
||||
appBar: StandardAppBar.build(context, title),
|
||||
body: buildBody(result, service),
|
||||
body: buildBody(profile, t),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildBody(
|
||||
Result<DirectMessageThread, ExecError> result,
|
||||
DirectMessageService service,
|
||||
Profile profile,
|
||||
DirectMessageThread thread,
|
||||
) {
|
||||
return result.fold(
|
||||
onSuccess: (thread) {
|
||||
final yourId = getIt<AccountsService>().currentProfile.userId;
|
||||
final yourAvatarUrl = getIt<AccountsService>().currentProfile.avatar;
|
||||
final participants =
|
||||
Map.fromEntries(thread.participants.map((p) => MapEntry(p.id, p)));
|
||||
return Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ResponsiveMaxWidth(
|
||||
child: ListView.separated(
|
||||
itemBuilder: (context, index) {
|
||||
final m = thread.messages[index];
|
||||
final textPieces = m.text.split('...\n');
|
||||
final text = 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(
|
||||
widget.parentThreadId, 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()),
|
||||
);
|
||||
final service = ref.read(
|
||||
directMessageThreadServiceProvider(profile, thread.parentUri).notifier);
|
||||
final yourId = getIt<AccountsService>().currentProfile.userId;
|
||||
final yourAvatarUrl = getIt<AccountsService>().currentProfile.avatar;
|
||||
final participants =
|
||||
Map.fromEntries(thread.participants.map((p) => MapEntry(p.id, p)));
|
||||
return Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ResponsiveMaxWidth(
|
||||
child: ListView.separated(
|
||||
itemBuilder: (context, index) {
|
||||
final m = thread.messages[index];
|
||||
final textPieces = m.text.split('...\n');
|
||||
final text =
|
||||
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);
|
||||
},
|
||||
separatorBuilder: (_, __) => const Divider(),
|
||||
itemCount: thread.messages.length),
|
||||
),
|
||||
),
|
||||
const VerticalDivider(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ResponsiveMaxWidth(
|
||||
child: TextFormField(
|
||||
controller: textController,
|
||||
textCapitalization: TextCapitalization.sentences,
|
||||
spellCheckConfiguration: const SpellCheckConfiguration(),
|
||||
maxLines: 4,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Reply Text',
|
||||
border: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(5.0),
|
||||
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(),
|
||||
itemCount: thread.messages.length),
|
||||
),
|
||||
),
|
||||
const VerticalDivider(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ResponsiveMaxWidth(
|
||||
child: TextFormField(
|
||||
controller: textController,
|
||||
textCapitalization: TextCapitalization.sentences,
|
||||
spellCheckConfiguration: const SpellCheckConfiguration(),
|
||||
maxLines: 4,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Reply Text',
|
||||
border: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(5.0),
|
||||
),
|
||||
),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
if (textController.text.isEmpty) {
|
||||
buildSnackbar(context, "Can't submit an empty reply");
|
||||
return;
|
||||
}
|
||||
final othersMessages =
|
||||
thread.messages.where((m) => m.senderId != yourId);
|
||||
if (othersMessages.isEmpty) {
|
||||
buildSnackbar(
|
||||
context, "Have to wait for a response before sending");
|
||||
return;
|
||||
}
|
||||
await service
|
||||
.newReplyMessage(
|
||||
thread.parentUri,
|
||||
othersMessages.last,
|
||||
textController.text,
|
||||
)
|
||||
.match(onSuccess: (_) {
|
||||
setState(() {
|
||||
textController.clear();
|
||||
});
|
||||
}, onError: (error) {
|
||||
if (mounted) {
|
||||
buildSnackbar(context, error.message);
|
||||
}
|
||||
});
|
||||
},
|
||||
child: const Text('Submit'),
|
||||
),
|
||||
const VerticalPadding(),
|
||||
],
|
||||
));
|
||||
},
|
||||
onError: (error) => Center(
|
||||
child: Text('Error getting thread: $error'),
|
||||
),
|
||||
);
|
||||
),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
if (textController.text.isEmpty) {
|
||||
buildSnackbar(context, "Can't submit an empty reply");
|
||||
return;
|
||||
}
|
||||
final othersMessages =
|
||||
thread.messages.where((m) => m.senderId != yourId);
|
||||
if (othersMessages.isEmpty) {
|
||||
buildSnackbar(
|
||||
context, "Have to wait for a response before sending");
|
||||
return;
|
||||
}
|
||||
await service
|
||||
.newReplyMessage(
|
||||
othersMessages.last,
|
||||
textController.text,
|
||||
)
|
||||
.match(onSuccess: (_) {
|
||||
setState(() {
|
||||
textController.clear();
|
||||
});
|
||||
}, onError: (error) {
|
||||
if (mounted) {
|
||||
buildSnackbar(context, error.message);
|
||||
}
|
||||
});
|
||||
},
|
||||
child: const Text('Submit'),
|
||||
),
|
||||
const VerticalPadding(),
|
||||
],
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
|
@ -7,27 +8,26 @@ import '../controls/responsive_max_width.dart';
|
|||
import '../controls/standard_appbar.dart';
|
||||
import '../controls/status_and_refresh_button.dart';
|
||||
import '../globals.dart';
|
||||
import '../models/auth/profile.dart';
|
||||
import '../riverpod_controllers/direct_message_services.dart';
|
||||
import '../routes.dart';
|
||||
import '../services/direct_message_service.dart';
|
||||
import '../services/auth_service.dart';
|
||||
import '../services/network_status_service.dart';
|
||||
import '../utils/active_profile_selector.dart';
|
||||
import '../utils/dateutils.dart';
|
||||
|
||||
class MessagesScreen extends StatelessWidget {
|
||||
class MessagesScreen extends ConsumerWidget {
|
||||
const MessagesScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final service = context
|
||||
.watch<ActiveProfileSelector<DirectMessageService>>()
|
||||
.activeEntry
|
||||
.value;
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final profile = context.watch<AccountsService>().currentProfile;
|
||||
final service = ref.watch(directMessageThreadIdsProvider(profile).notifier);
|
||||
final nss = getIt<NetworkStatusService>();
|
||||
return Scaffold(
|
||||
appBar: StandardAppBar.build(context, 'Direct Message Threads', actions: [
|
||||
StatusAndRefreshButton(
|
||||
valueListenable: nss.directMessageUpdateStatus,
|
||||
refreshFunction: () async => await service.updateThreads(),
|
||||
refreshFunction: () async => await service.update(),
|
||||
busyColor: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
IconButton(
|
||||
|
@ -39,24 +39,24 @@ class MessagesScreen extends StatelessWidget {
|
|||
]),
|
||||
body: RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
service.updateThreads();
|
||||
await service.update();
|
||||
},
|
||||
child: Center(child: buildBody(context, service)),
|
||||
child: Center(child: buildBody(profile, ref)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildBody(BuildContext context, DirectMessageService service) {
|
||||
final threads = service.getThreads();
|
||||
threads.sort((t1, t2) =>
|
||||
t2.messages.last.createdAt.compareTo(t1.messages.last.createdAt));
|
||||
return threads.isEmpty
|
||||
Widget buildBody(Profile profile, WidgetRef ref) {
|
||||
final threadIds = ref.watch(directMessageThreadIdsProvider(profile));
|
||||
return threadIds.isEmpty
|
||||
? const Text('No Direct Message Threads')
|
||||
: ResponsiveMaxWidth(
|
||||
child: ListView.separated(
|
||||
itemCount: threads.length,
|
||||
itemCount: threadIds.length,
|
||||
itemBuilder: (context, index) {
|
||||
final thread = threads[index];
|
||||
final threadId = threadIds[index];
|
||||
final thread = ref.watch(
|
||||
directMessageThreadServiceProvider(profile, threadId));
|
||||
final style = thread.allSeen
|
||||
? null
|
||||
: const TextStyle(fontWeight: FontWeight.bold);
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:multi_trigger_autocomplete/multi_trigger_autocomplete.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../controls/autocomplete/mention_autocomplete_options.dart';
|
||||
import '../controls/padding.dart';
|
||||
import '../controls/standard_appbar.dart';
|
||||
import '../globals.dart';
|
||||
import '../riverpod_controllers/direct_message_services.dart';
|
||||
import '../services/auth_service.dart';
|
||||
import '../services/connections_manager.dart';
|
||||
import '../services/direct_message_service.dart';
|
||||
import '../utils/active_profile_selector.dart';
|
||||
import '../utils/snackbar_builder.dart';
|
||||
|
||||
class MessagesNewThread extends StatelessWidget {
|
||||
class MessagesNewThread extends ConsumerWidget {
|
||||
final receiverController = TextEditingController();
|
||||
final replyController = TextEditingController();
|
||||
final focusNode = FocusNode();
|
||||
|
@ -19,14 +22,15 @@ class MessagesNewThread extends StatelessWidget {
|
|||
MessagesNewThread({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return Scaffold(
|
||||
appBar: StandardAppBar.build(context, 'New Thread'),
|
||||
body: buildBody(context),
|
||||
body: buildBody(context, ref),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildBody(BuildContext context) {
|
||||
Widget buildBody(BuildContext context, WidgetRef ref) {
|
||||
final profile = context.watch<AccountsService>().currentProfile;
|
||||
final border = OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
|
@ -88,16 +92,14 @@ class MessagesNewThread extends StatelessWidget {
|
|||
const VerticalPadding(),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
final result =
|
||||
await getIt<ActiveProfileSelector<ConnectionsManager>>()
|
||||
.activeEntry
|
||||
.andThen((manager) => manager.getByHandle(
|
||||
receiverController.text.trim().substring(1)))
|
||||
.andThenAsync((connection) async =>
|
||||
getIt<ActiveProfileSelector<DirectMessageService>>()
|
||||
.activeEntry
|
||||
.andThenAsync((dms) async => dms.newThread(
|
||||
connection, replyController.text)));
|
||||
final result = await getIt<
|
||||
ActiveProfileSelector<ConnectionsManager>>()
|
||||
.activeEntry
|
||||
.andThen((manager) => manager.getByHandle(
|
||||
receiverController.text.trim().substring(1)))
|
||||
.andThenAsync((connection) async => await ref
|
||||
.read(directMessageThreadIdsProvider(profile).notifier)
|
||||
.newThread(connection, replyController.text));
|
||||
result.match(onSuccess: (_) {
|
||||
if (context.canPop()) {
|
||||
context.pop();
|
||||
|
|
|
@ -1,167 +0,0 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:result_monad/result_monad.dart';
|
||||
|
||||
import '../friendica_client/friendica_client.dart';
|
||||
import '../friendica_client/paging_data.dart';
|
||||
import '../globals.dart';
|
||||
import '../models/auth/oauth_credentials.dart';
|
||||
import '../models/auth/profile.dart';
|
||||
import '../models/connection.dart';
|
||||
import '../models/direct_message.dart';
|
||||
import '../models/direct_message_thread.dart';
|
||||
import '../models/exec_error.dart';
|
||||
import 'feature_version_checker.dart';
|
||||
|
||||
class DirectMessageService extends ChangeNotifier {
|
||||
static final _logger = Logger('$DirectMessageService');
|
||||
final _threads = <String, DirectMessageThread>{};
|
||||
final Profile profile;
|
||||
var _firstLoading = true;
|
||||
|
||||
DirectMessageService(this.profile);
|
||||
|
||||
void clear() {
|
||||
_threads.clear();
|
||||
_firstLoading = true;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
List<DirectMessageThread> getThreads({bool unreadyOnly = false}) {
|
||||
if (_threads.isEmpty && _firstLoading) {
|
||||
updateThreads();
|
||||
_firstLoading = false;
|
||||
}
|
||||
|
||||
if (unreadyOnly) {
|
||||
return _threads.values.where((t) => !t.allSeen).toList();
|
||||
}
|
||||
|
||||
return _threads.values.toList();
|
||||
}
|
||||
|
||||
Result<DirectMessageThread, ExecError> getThreadByParentUri(String uri) {
|
||||
if (_threads.containsKey(uri)) {
|
||||
return Result.ok(_threads[uri]!);
|
||||
}
|
||||
|
||||
return buildErrorResult(
|
||||
type: ErrorType.notFound, message: 'Thread ID not found: $uri');
|
||||
}
|
||||
|
||||
Future<void> updateThreads() async {
|
||||
await DirectMessagingClient(profile).getDirectMessages(PagingData()).match(
|
||||
onSuccess: (update) {
|
||||
final newThreads = DirectMessageThread.createThreads(update);
|
||||
_threads.clear();
|
||||
for (final t in newThreads) {
|
||||
//TODO do merge operation
|
||||
_threads[t.parentUri] = t;
|
||||
}
|
||||
_logger.fine(
|
||||
'Updated ${update.length} direct messages, across ${newThreads.length} threads');
|
||||
notifyListeners();
|
||||
},
|
||||
onError: (error) {
|
||||
_logger.severe('Error getting direct messages: $error');
|
||||
},
|
||||
);
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
FutureResult<DirectMessage, ExecError> newThread(
|
||||
Connection receiver, String text) async {
|
||||
if (profile.credentials is OAuthCredentials) {
|
||||
final result = getIt<FriendicaVersionChecker>()
|
||||
.canUseFeatureResult(RelaticaFeatures.directMessageCreation);
|
||||
if (result.isFailure) {
|
||||
return result.errorCast();
|
||||
}
|
||||
}
|
||||
|
||||
final result = await DirectMessagingClient(profile).postDirectMessage(
|
||||
null,
|
||||
receiver.id,
|
||||
text,
|
||||
);
|
||||
result.match(onSuccess: (newMessage) {
|
||||
DirectMessageThread.createThreads([newMessage]).forEach((thread) {
|
||||
_threads[thread.parentUri] = thread;
|
||||
});
|
||||
notifyListeners();
|
||||
}, onError: (error) {
|
||||
_logger.severe('Error getting direct messages: $error');
|
||||
});
|
||||
|
||||
return result.execErrorCast();
|
||||
}
|
||||
|
||||
FutureResult<DirectMessage, ExecError> newReplyMessage(
|
||||
String threadId, DirectMessage original, String text) async {
|
||||
final thread = _threads[threadId];
|
||||
if (thread == null) {
|
||||
final error = 'Message is not for this thread: $threadId, $original';
|
||||
_logger.severe(error);
|
||||
return buildErrorResult(
|
||||
type: ErrorType.notFound,
|
||||
message: error,
|
||||
);
|
||||
}
|
||||
|
||||
if (!thread.messages.contains(original)) {
|
||||
final error = 'Message is not for this thread: $threadId, $original';
|
||||
_logger.severe(error);
|
||||
return buildErrorResult(
|
||||
type: ErrorType.notFound,
|
||||
message: error,
|
||||
);
|
||||
}
|
||||
|
||||
if (profile.credentials is OAuthCredentials) {
|
||||
final result = getIt<FriendicaVersionChecker>()
|
||||
.canUseFeatureResult(RelaticaFeatures.directMessageCreation);
|
||||
if (result.isFailure) {
|
||||
return result.errorCast();
|
||||
}
|
||||
}
|
||||
|
||||
final result = await DirectMessagingClient(profile).postDirectMessage(
|
||||
original.id,
|
||||
original.senderId,
|
||||
text,
|
||||
);
|
||||
result.match(onSuccess: (newMessage) {
|
||||
thread.messages.add(newMessage);
|
||||
notifyListeners();
|
||||
}, onError: (error) {
|
||||
_logger.severe('Error getting direct messages: $error');
|
||||
});
|
||||
|
||||
return result.execErrorCast();
|
||||
}
|
||||
|
||||
Future<void> markMessageRead(String threadId, DirectMessage m) async {
|
||||
final thread = _threads[threadId];
|
||||
if (thread == null) {
|
||||
_logger.severe('Message is not for this thread: $threadId, $m');
|
||||
return;
|
||||
}
|
||||
final oldIndex = thread.messages.indexOf(m);
|
||||
if (oldIndex < 0) {
|
||||
_logger.severe('Message is not for this thread: $threadId, $m');
|
||||
return;
|
||||
}
|
||||
|
||||
await DirectMessagingClient(profile).markDirectMessageRead(m).match(
|
||||
onSuccess: (update) {
|
||||
thread.messages.removeAt(oldIndex);
|
||||
thread.messages.insert(oldIndex, update);
|
||||
notifyListeners();
|
||||
},
|
||||
onError: (error) {
|
||||
_logger.severe('Error getting direct messages: $error');
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -14,7 +14,6 @@ import '../models/user_notification.dart';
|
|||
import '../serializers/mastodon/follow_request_mastodon_extensions.dart';
|
||||
import '../utils/active_profile_selector.dart';
|
||||
import 'auth_service.dart';
|
||||
import 'direct_message_service.dart';
|
||||
import 'feature_version_checker.dart';
|
||||
import 'follow_requests_manager.dart';
|
||||
import 'network_status_service.dart';
|
||||
|
@ -184,40 +183,42 @@ class NotificationsManager extends ChangeNotifier {
|
|||
|
||||
List<UserNotification> buildUnreadMessageNotifications(
|
||||
bool useActualRequests) {
|
||||
final myId = profile.userId;
|
||||
final dmsResult = getIt<ActiveProfileSelector<DirectMessageService>>()
|
||||
.getForProfile(profile)
|
||||
.transform((d) => d.getThreads(unreadyOnly: true).map((t) {
|
||||
final fromAccount =
|
||||
t.participants.firstWhere((p) => p.id != myId);
|
||||
final latestMessage = t.messages
|
||||
.reduce((s, m) => s.createdAt > m.createdAt ? s : m);
|
||||
return UserNotification(
|
||||
id: (fromAccount.hashCode ^
|
||||
t.parentUri.hashCode ^
|
||||
t.title.hashCode)
|
||||
.toString(),
|
||||
type: NotificationType.direct_message,
|
||||
fromId: fromAccount.id,
|
||||
fromName: fromAccount.name,
|
||||
fromUrl: fromAccount.profileUrl,
|
||||
timestamp: latestMessage.createdAt,
|
||||
iid: t.parentUri,
|
||||
dismissed: false,
|
||||
content: '${fromAccount.name} sent you a direct message',
|
||||
link: '');
|
||||
}).toList())
|
||||
.getValueOrElse(() => []);
|
||||
// TODO Re-wire into DMS once this is converted to Riverpod Controller
|
||||
// final myId = profile.userId;
|
||||
// final dmsResult = getIt<ActiveProfileSelector<DirectMessageService>>()
|
||||
// .getForProfile(profile)
|
||||
// .transform((d) => d.getThreads(unreadyOnly: true).map((t) {
|
||||
// final fromAccount =
|
||||
// t.participants.firstWhere((p) => p.id != myId);
|
||||
// final latestMessage = t.messages
|
||||
// .reduce((s, m) => s.createdAt > m.createdAt ? s : m);
|
||||
// return UserNotification(
|
||||
// id: (fromAccount.hashCode ^
|
||||
// t.parentUri.hashCode ^
|
||||
// t.title.hashCode)
|
||||
// .toString(),
|
||||
// type: NotificationType.direct_message,
|
||||
// fromId: fromAccount.id,
|
||||
// fromName: fromAccount.name,
|
||||
// fromUrl: fromAccount.profileUrl,
|
||||
// timestamp: latestMessage.createdAt,
|
||||
// iid: t.parentUri,
|
||||
// dismissed: false,
|
||||
// content: '${fromAccount.name} sent you a direct message',
|
||||
// link: '');
|
||||
// }).toList())
|
||||
// .getValueOrElse(() => []);
|
||||
|
||||
final followRequestResult = !useActualRequests
|
||||
? []
|
||||
? <UserNotification>[]
|
||||
: getIt<ActiveProfileSelector<FollowRequestsManager>>()
|
||||
.getForProfile(profile)
|
||||
.transform(
|
||||
(fm) => fm.requests.map((r) => r.toUserNotification()).toList())
|
||||
.getValueOrElse(() => []);
|
||||
|
||||
return [...dmsResult, ...followRequestResult];
|
||||
// return [...dmsResult, ...followRequestResult];
|
||||
return followRequestResult;
|
||||
}
|
||||
|
||||
void updateNotification(UserNotification notification) {}
|
||||
|
@ -229,9 +230,9 @@ class NotificationsManager extends ChangeNotifier {
|
|||
getIt<NetworkStatusService>().startNotificationUpdate();
|
||||
if (DateTime.now().difference(lastDmsUpdate) >
|
||||
minimumDmsAndCrsUpdateDuration) {
|
||||
await getIt<ActiveProfileSelector<DirectMessageService>>()
|
||||
.getForProfile(profile)
|
||||
.transformAsync((dms) async => await dms.updateThreads());
|
||||
// await getIt<ActiveProfileSelector<DirectMessageService>>()
|
||||
// .getForProfile(profile)
|
||||
// .transformAsync((dms) async => await dms.updateThreads());
|
||||
lastDmsUpdate = DateTime.now();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue