kopia lustrzana https://gitlab.com/mysocialportal/relatica
Refactor how reshares are handled, interactions with posts to take into account that, and refreshing it
rodzic
dda42c899a
commit
df8ec9009f
|
@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:relatica/models/exec_error.dart';
|
import 'package:relatica/models/exec_error.dart';
|
||||||
import 'package:relatica/services/auth_service.dart';
|
|
||||||
import 'package:result_monad/result_monad.dart';
|
import 'package:result_monad/result_monad.dart';
|
||||||
|
|
||||||
import '../../globals.dart';
|
import '../../globals.dart';
|
||||||
|
@ -112,18 +111,6 @@ class _InteractionsBarControlState extends State<InteractionsBarControl> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> addComment() async {
|
Future<void> addComment() async {
|
||||||
final needingReshareIdFix = getIt<FriendicaVersionChecker>()
|
|
||||||
.canUseFeature(RelaticaFeatures.reshareIdFix);
|
|
||||||
final myId = getIt<AccountsService>().currentProfile.userId;
|
|
||||||
|
|
||||||
if (needingReshareIdFix &&
|
|
||||||
widget.entry.reshareOriginalPostId.isNotEmpty &&
|
|
||||||
widget.entry.reshareAuthorId != myId) {
|
|
||||||
await showConfirmDialog(context,
|
|
||||||
'Unable to comment on reshared posts with your current version of Friendica server.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
context.push('/comment/new?parent_id=${widget.entry.id}');
|
context.push('/comment/new?parent_id=${widget.entry.id}');
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,9 @@ import '../../models/connection.dart';
|
||||||
import '../../models/timeline_entry.dart';
|
import '../../models/timeline_entry.dart';
|
||||||
import '../../models/visibility.dart';
|
import '../../models/visibility.dart';
|
||||||
import '../../routes.dart';
|
import '../../routes.dart';
|
||||||
|
import '../../services/auth_service.dart';
|
||||||
import '../../services/connections_manager.dart';
|
import '../../services/connections_manager.dart';
|
||||||
|
import '../../services/reshared_via_service.dart';
|
||||||
import '../../utils/active_profile_selector.dart';
|
import '../../utils/active_profile_selector.dart';
|
||||||
import '../../utils/dateutils.dart';
|
import '../../utils/dateutils.dart';
|
||||||
import '../image_control.dart';
|
import '../image_control.dart';
|
||||||
|
@ -32,14 +34,21 @@ class StatusHeaderControl extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
late final Connection author;
|
late final Connection author;
|
||||||
late final Connection reshareAuthor;
|
late final Connection reshareAuthor;
|
||||||
|
final activeProfile = getIt<AccountsService>().currentProfile;
|
||||||
|
final reshareId = getIt<ActiveProfileSelector<ReshareViaService>>()
|
||||||
|
.getForProfile(activeProfile)
|
||||||
|
.transform((s) => s.getForPost(entry.id)?.resharers.firstOrNull)
|
||||||
|
.getValueOrElse(() => null);
|
||||||
|
|
||||||
getIt<ActiveProfileSelector<ConnectionsManager>>().activeEntry.match(
|
getIt<ActiveProfileSelector<ConnectionsManager>>()
|
||||||
|
.getForProfile(activeProfile)
|
||||||
|
.match(
|
||||||
onSuccess: (manager) {
|
onSuccess: (manager) {
|
||||||
author =
|
author =
|
||||||
manager.getById(entry.authorId).getValueOrElse(() => Connection());
|
manager.getById(entry.authorId).getValueOrElse(() => Connection());
|
||||||
reshareAuthor = manager
|
reshareAuthor = reshareId == null
|
||||||
.getById(entry.reshareAuthorId)
|
? Connection()
|
||||||
.getValueOrElse(() => Connection());
|
: manager.getById(reshareId).getValueOrElse(() => Connection());
|
||||||
},
|
},
|
||||||
onError: (error) {
|
onError: (error) {
|
||||||
_logger.severe('Error getting connections manageR: $error');
|
_logger.severe('Error getting connections manageR: $error');
|
||||||
|
@ -75,9 +84,10 @@ class StatusHeaderControl extends StatelessWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (reshareAuthor.isNotEmpty) ...[
|
if (reshareAuthor.isNotEmpty &&
|
||||||
|
author.id != activeProfile.userId) ...[
|
||||||
const HorizontalPadding(width: 3.0),
|
const HorizontalPadding(width: 3.0),
|
||||||
const Text('reshared post by: '),
|
const Text('reshared by '),
|
||||||
const HorizontalPadding(width: 3.0),
|
const HorizontalPadding(width: 3.0),
|
||||||
Row(
|
Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
|
|
@ -29,6 +29,7 @@ import 'services/interactions_manager.dart';
|
||||||
import 'services/network_status_service.dart';
|
import 'services/network_status_service.dart';
|
||||||
import 'services/notifications_manager.dart';
|
import 'services/notifications_manager.dart';
|
||||||
import 'services/persistent_info_service.dart';
|
import 'services/persistent_info_service.dart';
|
||||||
|
import 'services/reshared_via_service.dart';
|
||||||
import 'services/secrets_service.dart';
|
import 'services/secrets_service.dart';
|
||||||
import 'services/setting_service.dart';
|
import 'services/setting_service.dart';
|
||||||
import 'services/timeline_entry_filter_service.dart';
|
import 'services/timeline_entry_filter_service.dart';
|
||||||
|
@ -91,6 +92,9 @@ Future<void> dependencyInjectionInitialization() async {
|
||||||
getIt.registerSingleton<AccountsService>(accountsService);
|
getIt.registerSingleton<AccountsService>(accountsService);
|
||||||
getIt<ActiveProfileSelector<IConnectionsRepo>>().subscribeToProfileSwaps();
|
getIt<ActiveProfileSelector<IConnectionsRepo>>().subscribeToProfileSwaps();
|
||||||
getIt<ActiveProfileSelector<InstanceInfo>>().subscribeToProfileSwaps();
|
getIt<ActiveProfileSelector<InstanceInfo>>().subscribeToProfileSwaps();
|
||||||
|
getIt.registerSingleton<ActiveProfileSelector<ReshareViaService>>(
|
||||||
|
ActiveProfileSelector((p) => ReshareViaService())
|
||||||
|
..subscribeToProfileSwaps());
|
||||||
|
|
||||||
getIt.registerSingleton<ActiveProfileSelector<IGroupsRepo>>(
|
getIt.registerSingleton<ActiveProfileSelector<IGroupsRepo>>(
|
||||||
ActiveProfileSelector((p) => MemoryGroupsRepo())
|
ActiveProfileSelector((p) => MemoryGroupsRepo())
|
||||||
|
|
|
@ -154,6 +154,8 @@ final FriendicaVersion v2023_04_01 = FriendicaVersion(
|
||||||
DateTime(2023, 04),
|
DateTime(2023, 04),
|
||||||
extra: '1',
|
extra: '1',
|
||||||
);
|
);
|
||||||
|
final FriendicaVersion v2023_05 = FriendicaVersion(DateTime(2023, 05));
|
||||||
|
final FriendicaVersion v2023_09 = FriendicaVersion(DateTime(2023, 09));
|
||||||
|
|
||||||
final knownFriendicaVersions = [
|
final knownFriendicaVersions = [
|
||||||
// 2018 Versions
|
// 2018 Versions
|
||||||
|
@ -192,6 +194,8 @@ final knownFriendicaVersions = [
|
||||||
v2023_01,
|
v2023_01,
|
||||||
v2023_04,
|
v2023_04,
|
||||||
v2023_04_01,
|
v2023_04_01,
|
||||||
|
v2023_05,
|
||||||
|
v2023_09,
|
||||||
];
|
];
|
||||||
|
|
||||||
FriendicaVersion latestVersion() => knownFriendicaVersions.last;
|
FriendicaVersion latestVersion() => knownFriendicaVersions.last;
|
||||||
|
|
|
@ -16,12 +16,6 @@ class TimelineEntry {
|
||||||
|
|
||||||
final String parentAuthorId;
|
final String parentAuthorId;
|
||||||
|
|
||||||
final String reshareOriginalPostId;
|
|
||||||
|
|
||||||
final String reshareAuthor;
|
|
||||||
|
|
||||||
final String reshareAuthorId;
|
|
||||||
|
|
||||||
final int creationTimestamp;
|
final int creationTimestamp;
|
||||||
|
|
||||||
final int backdatedTimestamp;
|
final int backdatedTimestamp;
|
||||||
|
@ -65,7 +59,6 @@ class TimelineEntry {
|
||||||
TimelineEntry(
|
TimelineEntry(
|
||||||
{this.id = '',
|
{this.id = '',
|
||||||
this.parentId = '',
|
this.parentId = '',
|
||||||
this.reshareOriginalPostId = '',
|
|
||||||
this.creationTimestamp = 0,
|
this.creationTimestamp = 0,
|
||||||
this.backdatedTimestamp = 0,
|
this.backdatedTimestamp = 0,
|
||||||
this.modificationTimestamp = 0,
|
this.modificationTimestamp = 0,
|
||||||
|
@ -78,8 +71,6 @@ class TimelineEntry {
|
||||||
this.authorId = '',
|
this.authorId = '',
|
||||||
this.parentAuthor = '',
|
this.parentAuthor = '',
|
||||||
this.parentAuthorId = '',
|
this.parentAuthorId = '',
|
||||||
this.reshareAuthor = '',
|
|
||||||
this.reshareAuthorId = '',
|
|
||||||
this.externalLink = '',
|
this.externalLink = '',
|
||||||
this.locationData = const LocationData(),
|
this.locationData = const LocationData(),
|
||||||
this.isFavorited = false,
|
this.isFavorited = false,
|
||||||
|
@ -97,7 +88,6 @@ class TimelineEntry {
|
||||||
backdatedTimestamp = DateTime.now().millisecondsSinceEpoch,
|
backdatedTimestamp = DateTime.now().millisecondsSinceEpoch,
|
||||||
modificationTimestamp = DateTime.now().millisecondsSinceEpoch,
|
modificationTimestamp = DateTime.now().millisecondsSinceEpoch,
|
||||||
id = randomId(),
|
id = randomId(),
|
||||||
reshareOriginalPostId = '',
|
|
||||||
youReshared = DateTime.now().second ~/ 2 == 0 ? true : false,
|
youReshared = DateTime.now().second ~/ 2 == 0 ? true : false,
|
||||||
visibility = DateTime.now().second ~/ 2 == 0
|
visibility = DateTime.now().second ~/ 2 == 0
|
||||||
? Visibility.public()
|
? Visibility.public()
|
||||||
|
@ -111,8 +101,6 @@ class TimelineEntry {
|
||||||
authorId = 'Random authorId ${randomId()}',
|
authorId = 'Random authorId ${randomId()}',
|
||||||
parentAuthor = 'Random parent author ${randomId()}',
|
parentAuthor = 'Random parent author ${randomId()}',
|
||||||
parentAuthorId = 'Random parent author id ${randomId()}',
|
parentAuthorId = 'Random parent author id ${randomId()}',
|
||||||
reshareAuthor = 'Random parent author ${randomId()}',
|
|
||||||
reshareAuthorId = 'Random parent author id ${randomId()}',
|
|
||||||
locationData = LocationData.randomBuilt(),
|
locationData = LocationData.randomBuilt(),
|
||||||
isFavorited = DateTime.now().second ~/ 2 == 0 ? true : false,
|
isFavorited = DateTime.now().second ~/ 2 == 0 ? true : false,
|
||||||
tags = [],
|
tags = [],
|
||||||
|
@ -158,8 +146,6 @@ class TimelineEntry {
|
||||||
modificationTimestamp:
|
modificationTimestamp:
|
||||||
modificationTimestamp ?? this.modificationTimestamp,
|
modificationTimestamp ?? this.modificationTimestamp,
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
reshareOriginalPostId:
|
|
||||||
reshareOriginalPostId ?? this.reshareOriginalPostId,
|
|
||||||
youReshared: isReshare ?? this.youReshared,
|
youReshared: isReshare ?? this.youReshared,
|
||||||
visibility: visibility ?? this.visibility,
|
visibility: visibility ?? this.visibility,
|
||||||
parentId: parentId ?? this.parentId,
|
parentId: parentId ?? this.parentId,
|
||||||
|
@ -171,8 +157,6 @@ class TimelineEntry {
|
||||||
authorId: authorId ?? this.authorId,
|
authorId: authorId ?? this.authorId,
|
||||||
parentAuthor: parentAuthor ?? this.parentAuthor,
|
parentAuthor: parentAuthor ?? this.parentAuthor,
|
||||||
parentAuthorId: parentAuthorId ?? this.parentAuthorId,
|
parentAuthorId: parentAuthorId ?? this.parentAuthorId,
|
||||||
reshareAuthor: parentAuthor ?? this.reshareAuthor,
|
|
||||||
reshareAuthorId: parentAuthorId ?? this.reshareAuthorId,
|
|
||||||
locationData: locationData ?? this.locationData,
|
locationData: locationData ?? this.locationData,
|
||||||
isFavorited: isFavorited ?? this.isFavorited,
|
isFavorited: isFavorited ?? this.isFavorited,
|
||||||
tags: tags ?? this.tags,
|
tags: tags ?? this.tags,
|
||||||
|
@ -200,12 +184,9 @@ class TimelineEntry {
|
||||||
other is TimelineEntry &&
|
other is TimelineEntry &&
|
||||||
runtimeType == other.runtimeType &&
|
runtimeType == other.runtimeType &&
|
||||||
id == other.id &&
|
id == other.id &&
|
||||||
reshareOriginalPostId == other.reshareOriginalPostId &&
|
|
||||||
parentId == other.parentId &&
|
parentId == other.parentId &&
|
||||||
parentAuthor == other.parentAuthor &&
|
parentAuthor == other.parentAuthor &&
|
||||||
parentAuthorId == other.parentAuthorId &&
|
parentAuthorId == other.parentAuthorId &&
|
||||||
reshareAuthor == other.reshareAuthor &&
|
|
||||||
reshareAuthorId == other.reshareAuthorId &&
|
|
||||||
creationTimestamp == other.creationTimestamp &&
|
creationTimestamp == other.creationTimestamp &&
|
||||||
backdatedTimestamp == other.backdatedTimestamp &&
|
backdatedTimestamp == other.backdatedTimestamp &&
|
||||||
modificationTimestamp == other.modificationTimestamp &&
|
modificationTimestamp == other.modificationTimestamp &&
|
||||||
|
@ -229,12 +210,9 @@ class TimelineEntry {
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode =>
|
||||||
id.hashCode ^
|
id.hashCode ^
|
||||||
reshareOriginalPostId.hashCode ^
|
|
||||||
parentId.hashCode ^
|
parentId.hashCode ^
|
||||||
parentAuthor.hashCode ^
|
parentAuthor.hashCode ^
|
||||||
parentAuthorId.hashCode ^
|
parentAuthorId.hashCode ^
|
||||||
reshareAuthor.hashCode ^
|
|
||||||
reshareAuthorId.hashCode ^
|
|
||||||
creationTimestamp.hashCode ^
|
creationTimestamp.hashCode ^
|
||||||
backdatedTimestamp.hashCode ^
|
backdatedTimestamp.hashCode ^
|
||||||
modificationTimestamp.hashCode ^
|
modificationTimestamp.hashCode ^
|
||||||
|
|
|
@ -42,10 +42,10 @@ extension NotificationMastodonExtension on UserNotification {
|
||||||
case NotificationType.unknown:
|
case NotificationType.unknown:
|
||||||
content = '${from.name} has unknown interaction notification';
|
content = '${from.name} has unknown interaction notification';
|
||||||
break;
|
break;
|
||||||
case NotificationType.favourite:
|
|
||||||
case NotificationType.mention:
|
|
||||||
case NotificationType.reshare:
|
case NotificationType.reshare:
|
||||||
case NotificationType.reblog:
|
case NotificationType.reblog:
|
||||||
|
case NotificationType.favourite:
|
||||||
|
case NotificationType.mention:
|
||||||
case NotificationType.status:
|
case NotificationType.status:
|
||||||
final status = TimelineEntryMastodonExtensions.fromJson(json['status']);
|
final status = TimelineEntryMastodonExtensions.fromJson(json['status']);
|
||||||
statusId = status.id;
|
statusId = status.id;
|
||||||
|
@ -58,11 +58,8 @@ extension NotificationMastodonExtension on UserNotification {
|
||||||
final baseContent = type == NotificationType.mention
|
final baseContent = type == NotificationType.mention
|
||||||
? "${from.name} ${type.toVerb()}"
|
? "${from.name} ${type.toVerb()}"
|
||||||
: "${from.name} ${type.toVerb()} ${status.author}'s";
|
: "${from.name} ${type.toVerb()} ${status.author}'s";
|
||||||
final shareInfo = status.reshareAuthorId.isNotEmpty
|
|
||||||
? "reshare of ${status.reshareAuthor}'s"
|
|
||||||
: '';
|
|
||||||
final bodyText = htmlToSimpleText(status.body).truncate(length: 100);
|
final bodyText = htmlToSimpleText(status.body).truncate(length: 100);
|
||||||
content = "$baseContent $shareInfo $referenceType: $bodyText";
|
content = "$baseContent $referenceType: $bodyText";
|
||||||
break;
|
break;
|
||||||
case NotificationType.direct_message:
|
case NotificationType.direct_message:
|
||||||
// this is a Relatica internal type so nothing to do here
|
// this is a Relatica internal type so nothing to do here
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:relatica/services/auth_service.dart';
|
||||||
|
import 'package:relatica/services/reshared_via_service.dart';
|
||||||
|
|
||||||
import '../../globals.dart';
|
import '../../globals.dart';
|
||||||
import '../../models/engagement_summary.dart';
|
import '../../models/engagement_summary.dart';
|
||||||
|
@ -19,6 +21,45 @@ final _logger = Logger('TimelineEntryMastodonExtensions');
|
||||||
|
|
||||||
extension TimelineEntryMastodonExtensions on TimelineEntry {
|
extension TimelineEntryMastodonExtensions on TimelineEntry {
|
||||||
static TimelineEntry fromJson(Map<String, dynamic> json) {
|
static TimelineEntry fromJson(Map<String, dynamic> json) {
|
||||||
|
final activeProfile = getIt<AccountsService>().currentProfile;
|
||||||
|
final resharedViaService = getIt<ActiveProfileSelector<ReshareViaService>>()
|
||||||
|
.getForProfile(activeProfile)
|
||||||
|
.fold(
|
||||||
|
onSuccess: (s) => s,
|
||||||
|
onError: (error) {
|
||||||
|
_logger.severe('Error getting reshared via service: $error');
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
final connectionManager = getIt<ActiveProfileSelector<ConnectionsManager>>()
|
||||||
|
.getForProfile(activeProfile)
|
||||||
|
.fold(
|
||||||
|
onSuccess: (m) => m,
|
||||||
|
onError: (error) {
|
||||||
|
_logger.severe('Error getting connection manager: $error');
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
final id = json['id'] ?? '';
|
||||||
|
final parentId = json['in_reply_to_id'] ?? '';
|
||||||
|
final parentAuthor = json['in_reply_to_account_id'] ?? '';
|
||||||
|
final parentAuthorId = json['in_reply_to_account_id'] ?? '';
|
||||||
|
final author = json['account']['display_name'];
|
||||||
|
final authorId = json['account']['id'];
|
||||||
|
final resharePostData = json['reblog'];
|
||||||
|
|
||||||
|
if (resharePostData != null) {
|
||||||
|
final rebloggedUser =
|
||||||
|
ConnectionMastodonExtensions.fromJson(resharePostData['account']);
|
||||||
|
connectionManager?.upsertConnection(rebloggedUser);
|
||||||
|
final resharedPost = fromJson(resharePostData);
|
||||||
|
resharedViaService?.upsertResharedVia(
|
||||||
|
postId: resharedPost.id,
|
||||||
|
resharerId: authorId,
|
||||||
|
);
|
||||||
|
return resharedPost;
|
||||||
|
}
|
||||||
|
|
||||||
final int timestamp = json.containsKey('created_at')
|
final int timestamp = json.containsKey('created_at')
|
||||||
? OffsetDateTimeUtils.epochSecTimeFromTimeZoneString(json['created_at'])
|
? OffsetDateTimeUtils.epochSecTimeFromTimeZoneString(json['created_at'])
|
||||||
.fold(
|
.fold(
|
||||||
|
@ -28,21 +69,18 @@ extension TimelineEntryMastodonExtensions on TimelineEntry {
|
||||||
return 0;
|
return 0;
|
||||||
})
|
})
|
||||||
: 0;
|
: 0;
|
||||||
final id = json['id'] ?? '';
|
|
||||||
final youReshared = json['reblogged'] ?? false;
|
final youReshared = json['reblogged'] ?? false;
|
||||||
final visibility = ['public', 'unlisted'].contains(json['visibility'])
|
final visibility = ['public', 'unlisted'].contains(json['visibility'])
|
||||||
? Visibility.public()
|
? Visibility.public()
|
||||||
: Visibility.private();
|
: Visibility.private();
|
||||||
final parentId = json['in_reply_to_id'] ?? '';
|
|
||||||
final parentAuthor = json['in_reply_to_account_id'] ?? '';
|
|
||||||
final parentAuthorId = json['in_reply_to_account_id'] ?? '';
|
|
||||||
final body = json['content'] ?? '';
|
|
||||||
final author = json['account']['display_name'];
|
|
||||||
final authorId = json['account']['id'];
|
|
||||||
const title = '';
|
const title = '';
|
||||||
|
final body = json['content'] ?? '';
|
||||||
final spoilerText = json['spoiler_text'] ?? '';
|
final spoilerText = json['spoiler_text'] ?? '';
|
||||||
final externalLink = json['uri'] ?? '';
|
final externalLink = json['uri'] ?? '';
|
||||||
final actualLocationData = LocationData();
|
const actualLocationData = LocationData();
|
||||||
|
|
||||||
final modificationTimestamp = timestamp;
|
final modificationTimestamp = timestamp;
|
||||||
final backdatedTimestamp = timestamp;
|
final backdatedTimestamp = timestamp;
|
||||||
final isFavorited = json['favourited'] ?? false;
|
final isFavorited = json['favourited'] ?? false;
|
||||||
|
@ -61,35 +99,9 @@ extension TimelineEntryMastodonExtensions on TimelineEntry {
|
||||||
rebloggedCount: rebloggedCount,
|
rebloggedCount: rebloggedCount,
|
||||||
repliesCount: repliesCount,
|
repliesCount: repliesCount,
|
||||||
);
|
);
|
||||||
final linkPreviewData =
|
final linkPreviewData = LinkPreviewMastodonExtensions.fromJson(
|
||||||
LinkPreviewMastodonExtensions.fromJson(json['card']);
|
json['card'],
|
||||||
|
);
|
||||||
final connectionManager =
|
|
||||||
getIt<ActiveProfileSelector<ConnectionsManager>>().activeEntry.fold(
|
|
||||||
onSuccess: (m) => m,
|
|
||||||
onError: (error) {
|
|
||||||
_logger.severe('Error getting connection manager: $error');
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
final connection = ConnectionMastodonExtensions.fromJson(json['account']);
|
|
||||||
connectionManager?.upsertConnection(connection);
|
|
||||||
|
|
||||||
late final String reshareAuthor;
|
|
||||||
late final String reshareAuthorId;
|
|
||||||
late final String reshareOriginalPostId;
|
|
||||||
if (json['reblog'] != null) {
|
|
||||||
final rebloggedUser =
|
|
||||||
ConnectionMastodonExtensions.fromJson(json['reblog']['account']);
|
|
||||||
connectionManager?.upsertConnection(rebloggedUser);
|
|
||||||
reshareAuthor = rebloggedUser.name;
|
|
||||||
reshareAuthorId = rebloggedUser.id;
|
|
||||||
reshareOriginalPostId = json['reblog']['id'] ?? id;
|
|
||||||
} else {
|
|
||||||
reshareAuthorId = '';
|
|
||||||
reshareAuthor = '';
|
|
||||||
reshareOriginalPostId = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
final List<dynamic>? tagsJson = json['tags'];
|
final List<dynamic>? tagsJson = json['tags'];
|
||||||
final tags = <String>[];
|
final tags = <String>[];
|
||||||
if (tagsJson?.isNotEmpty ?? false) {
|
if (tagsJson?.isNotEmpty ?? false) {
|
||||||
|
@ -101,6 +113,9 @@ extension TimelineEntryMastodonExtensions on TimelineEntry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final connection = ConnectionMastodonExtensions.fromJson(json['account']);
|
||||||
|
connectionManager?.upsertConnection(connection);
|
||||||
|
|
||||||
return TimelineEntry(
|
return TimelineEntry(
|
||||||
creationTimestamp: timestamp,
|
creationTimestamp: timestamp,
|
||||||
modificationTimestamp: modificationTimestamp,
|
modificationTimestamp: modificationTimestamp,
|
||||||
|
@ -111,11 +126,8 @@ extension TimelineEntryMastodonExtensions on TimelineEntry {
|
||||||
youReshared: youReshared,
|
youReshared: youReshared,
|
||||||
visibility: visibility,
|
visibility: visibility,
|
||||||
id: id,
|
id: id,
|
||||||
reshareOriginalPostId: reshareOriginalPostId,
|
|
||||||
parentId: parentId,
|
parentId: parentId,
|
||||||
parentAuthorId: parentAuthorId,
|
parentAuthorId: parentAuthorId,
|
||||||
reshareAuthor: reshareAuthor,
|
|
||||||
reshareAuthorId: reshareAuthorId,
|
|
||||||
isFavorited: isFavorited,
|
isFavorited: isFavorited,
|
||||||
externalLink: externalLink,
|
externalLink: externalLink,
|
||||||
author: author,
|
author: author,
|
||||||
|
|
|
@ -5,7 +5,6 @@ import 'package:result_monad/result_monad.dart';
|
||||||
|
|
||||||
import '../friendica_client/friendica_client.dart';
|
import '../friendica_client/friendica_client.dart';
|
||||||
import '../friendica_client/paging_data.dart';
|
import '../friendica_client/paging_data.dart';
|
||||||
import '../globals.dart';
|
|
||||||
import '../models/TimelineIdentifiers.dart';
|
import '../models/TimelineIdentifiers.dart';
|
||||||
import '../models/auth/profile.dart';
|
import '../models/auth/profile.dart';
|
||||||
import '../models/entry_tree_item.dart';
|
import '../models/entry_tree_item.dart';
|
||||||
|
@ -14,7 +13,6 @@ import '../models/image_entry.dart';
|
||||||
import '../models/media_attachment_uploads/new_entry_media_items.dart';
|
import '../models/media_attachment_uploads/new_entry_media_items.dart';
|
||||||
import '../models/timeline_entry.dart';
|
import '../models/timeline_entry.dart';
|
||||||
import '../models/visibility.dart';
|
import '../models/visibility.dart';
|
||||||
import 'feature_version_checker.dart';
|
|
||||||
import 'media_upload_attachment_helper.dart';
|
import 'media_upload_attachment_helper.dart';
|
||||||
|
|
||||||
class EntryManagerService extends ChangeNotifier {
|
class EntryManagerService extends ChangeNotifier {
|
||||||
|
@ -48,7 +46,7 @@ class EntryManagerService extends ChangeNotifier {
|
||||||
|
|
||||||
Result<EntryTreeItem, ExecError> getPostTreeEntryBy(String id) {
|
Result<EntryTreeItem, ExecError> getPostTreeEntryBy(String id) {
|
||||||
_logger.finest('Getting post: $id');
|
_logger.finest('Getting post: $id');
|
||||||
final idForCall = mapInteractionId(id);
|
final idForCall = id;
|
||||||
final postNode = _getPostRootNode(idForCall);
|
final postNode = _getPostRootNode(idForCall);
|
||||||
if (postNode == null) {
|
if (postNode == null) {
|
||||||
return Result.error(ExecError(
|
return Result.error(ExecError(
|
||||||
|
@ -180,7 +178,7 @@ class EntryManagerService extends ChangeNotifier {
|
||||||
required Visibility newMediaItemVisibility,
|
required Visibility newMediaItemVisibility,
|
||||||
}) async {
|
}) async {
|
||||||
_logger.finest('Editing post: $text');
|
_logger.finest('Editing post: $text');
|
||||||
final idForCall = mapInteractionId(id);
|
final idForCall = id;
|
||||||
final mediaIds = existingMediaItems
|
final mediaIds = existingMediaItems
|
||||||
.map((m) => m.scales.isEmpty ? m.id : m.scales.first.id)
|
.map((m) => m.scales.isEmpty ? m.id : m.scales.first.id)
|
||||||
.toList();
|
.toList();
|
||||||
|
@ -397,54 +395,20 @@ class EntryManagerService extends ChangeNotifier {
|
||||||
return updatedPosts;
|
return updatedPosts;
|
||||||
}
|
}
|
||||||
|
|
||||||
String mapInteractionId(String id) {
|
|
||||||
return getEntryById(id).transform((e) {
|
|
||||||
if (e.reshareOriginalPostId.isEmpty) {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
final fvc = getIt<FriendicaVersionChecker>();
|
|
||||||
if (fvc.canUseFeature(RelaticaFeatures.reshareIdFix)) {
|
|
||||||
return e.reshareOriginalPostId;
|
|
||||||
}
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}).getValueOrElse(() => id);
|
|
||||||
}
|
|
||||||
|
|
||||||
FutureResult<EntryTreeItem, ExecError> refreshStatusChain(String id) async {
|
FutureResult<EntryTreeItem, ExecError> refreshStatusChain(String id) async {
|
||||||
_logger.finest('Refreshing post: $id');
|
_logger.finest('Refreshing post: $id');
|
||||||
final client = StatusesClient(profile);
|
final client = StatusesClient(profile);
|
||||||
final idForCall = mapInteractionId(id);
|
|
||||||
var parentId = '';
|
|
||||||
final result = await client
|
final result = await client
|
||||||
.getPostOrComment(idForCall, fullContext: false)
|
.getPostOrComment(id, fullContext: false)
|
||||||
.withResult((entries) =>
|
|
||||||
parentId = entries.isEmpty ? '' : entries.first.parentId)
|
|
||||||
.andThenAsync((rootItems) async => await client
|
.andThenAsync((rootItems) async => await client
|
||||||
.getPostOrComment(idForCall, fullContext: true)
|
.getPostOrComment(id, fullContext: true)
|
||||||
.andThenSuccessAsync(
|
.andThenSuccessAsync(
|
||||||
(contextItems) async => [...rootItems, ...contextItems]))
|
(contextItems) async => [...rootItems, ...contextItems]))
|
||||||
.withResult((items) async {
|
.withResult((items) async {
|
||||||
|
_cleanupEntriesForId(id);
|
||||||
await processNewItems(items, client.profile.username, null);
|
await processNewItems(items, client.profile.username, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (parentId.isNotEmpty &&
|
|
||||||
getIt<FriendicaVersionChecker>()
|
|
||||||
.canUseFeature(RelaticaFeatures.reshareIdFix)) {
|
|
||||||
final parentIdForCall = mapInteractionId(parentId);
|
|
||||||
await client
|
|
||||||
.getPostOrComment(parentIdForCall, fullContext: false)
|
|
||||||
.withResult((entries) =>
|
|
||||||
parentId = entries.isEmpty ? '' : entries.first.parentId)
|
|
||||||
.andThenAsync((rootItems) async => await client
|
|
||||||
.getPostOrComment(idForCall, fullContext: true)
|
|
||||||
.transformAsync(
|
|
||||||
(contextItems) async => [...rootItems, ...contextItems]))
|
|
||||||
.withResult((items) async {
|
|
||||||
await processNewItems(items, client.profile.username, null);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return result.mapValue((_) {
|
return result.mapValue((_) {
|
||||||
_logger.finest('$id post updated');
|
_logger.finest('$id post updated');
|
||||||
return _nodeToTreeItem(_getPostRootNode(id)!, client.profile.userId);
|
return _nodeToTreeItem(_getPostRootNode(id)!, client.profile.userId);
|
||||||
|
@ -462,7 +426,7 @@ class EntryManagerService extends ChangeNotifier {
|
||||||
FutureResult<EntryTreeItem, ExecError> resharePost(String id) async {
|
FutureResult<EntryTreeItem, ExecError> resharePost(String id) async {
|
||||||
_logger.finest('Resharing post: $id');
|
_logger.finest('Resharing post: $id');
|
||||||
final client = StatusesClient(profile);
|
final client = StatusesClient(profile);
|
||||||
final idForCall = mapInteractionId(id);
|
final idForCall = id;
|
||||||
final result =
|
final result =
|
||||||
await client.resharePost(idForCall).andThenSuccessAsync((item) async {
|
await client.resharePost(idForCall).andThenSuccessAsync((item) async {
|
||||||
await processNewItems([item], client.profile.username, null);
|
await processNewItems([item], client.profile.username, null);
|
||||||
|
@ -485,7 +449,7 @@ class EntryManagerService extends ChangeNotifier {
|
||||||
FutureResult<bool, ExecError> unResharePost(String id) async {
|
FutureResult<bool, ExecError> unResharePost(String id) async {
|
||||||
_logger.finest('Unresharing post: $id');
|
_logger.finest('Unresharing post: $id');
|
||||||
final client = StatusesClient(profile);
|
final client = StatusesClient(profile);
|
||||||
final idForCall = mapInteractionId(id);
|
final idForCall = id;
|
||||||
final result =
|
final result =
|
||||||
await client.unResharePost(idForCall).andThenSuccessAsync((item) async {
|
await client.unResharePost(idForCall).andThenSuccessAsync((item) async {
|
||||||
await processNewItems([item], client.profile.username, null);
|
await processNewItems([item], client.profile.username, null);
|
||||||
|
@ -505,7 +469,7 @@ class EntryManagerService extends ChangeNotifier {
|
||||||
String id, bool newStatus) async {
|
String id, bool newStatus) async {
|
||||||
final interactionClient = InteractionsClient(profile);
|
final interactionClient = InteractionsClient(profile);
|
||||||
final postsClient = StatusesClient(profile);
|
final postsClient = StatusesClient(profile);
|
||||||
final idForCall = mapInteractionId(id);
|
final idForCall = id;
|
||||||
final result =
|
final result =
|
||||||
await interactionClient.changeFavoriteStatus(idForCall, newStatus);
|
await interactionClient.changeFavoriteStatus(idForCall, newStatus);
|
||||||
if (result.isFailure) {
|
if (result.isFailure) {
|
||||||
|
@ -533,8 +497,7 @@ class EntryManagerService extends ChangeNotifier {
|
||||||
childenEntries[c.id] = _nodeToTreeItem(c, currentId);
|
childenEntries[c.id] = _nodeToTreeItem(c, currentId);
|
||||||
}
|
}
|
||||||
final entry = _entries[node.id]!;
|
final entry = _entries[node.id]!;
|
||||||
final isMine =
|
final isMine = entry.authorId == currentId;
|
||||||
entry.authorId == currentId || entry.reshareAuthorId == currentId;
|
|
||||||
return EntryTreeItem(
|
return EntryTreeItem(
|
||||||
_entries[node.id]!,
|
_entries[node.id]!,
|
||||||
isMine: isMine,
|
isMine: isMine,
|
||||||
|
@ -544,7 +507,9 @@ class EntryManagerService extends ChangeNotifier {
|
||||||
|
|
||||||
void _cleanupEntriesForId(String id) {
|
void _cleanupEntriesForId(String id) {
|
||||||
if (_parentPostIds.containsKey(id)) {
|
if (_parentPostIds.containsKey(id)) {
|
||||||
_parentPostIds.remove(id);
|
final parentPostId = _parentPostIds.remove(id);
|
||||||
|
final parentPostNode = _postNodes[parentPostId];
|
||||||
|
parentPostNode?.removeChildById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_entries.containsKey(id)) {
|
if (_entries.containsKey(id)) {
|
||||||
|
@ -573,7 +538,7 @@ class _Node {
|
||||||
_children[node.id] = node;
|
_children[node.id] = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
_Node? removeChildById(String id) {
|
void removeChildById(String id) {
|
||||||
if (_children.containsKey(id)) {
|
if (_children.containsKey(id)) {
|
||||||
_children.remove(id);
|
_children.remove(id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,9 +78,6 @@ class FriendicaVersionChecker {
|
||||||
v2023_04,
|
v2023_04,
|
||||||
),
|
),
|
||||||
RelaticaFeatures.postSpoilerText: FriendicaVersionRequirement(v2023_04),
|
RelaticaFeatures.postSpoilerText: FriendicaVersionRequirement(v2023_04),
|
||||||
RelaticaFeatures.reshareIdFix: FriendicaVersionRequirement(
|
|
||||||
v2023_04,
|
|
||||||
),
|
|
||||||
RelaticaFeatures.statusEditing: FriendicaVersionRequirement(v2023_04),
|
RelaticaFeatures.statusEditing: FriendicaVersionRequirement(v2023_04),
|
||||||
RelaticaFeatures.usingActualFollowRequests: FriendicaVersionRequirement(
|
RelaticaFeatures.usingActualFollowRequests: FriendicaVersionRequirement(
|
||||||
v2023_04,
|
v2023_04,
|
||||||
|
|
|
@ -6,8 +6,7 @@ import '../globals.dart';
|
||||||
import '../models/auth/profile.dart';
|
import '../models/auth/profile.dart';
|
||||||
import '../models/connection.dart';
|
import '../models/connection.dart';
|
||||||
import '../models/exec_error.dart';
|
import '../models/exec_error.dart';
|
||||||
import '../utils/active_profile_selector.dart';
|
import 'auth_service.dart';
|
||||||
import 'entry_manager_service.dart';
|
|
||||||
|
|
||||||
class InteractionsManager extends ChangeNotifier {
|
class InteractionsManager extends ChangeNotifier {
|
||||||
final _likesByStatusId = <String, List<Connection>>{};
|
final _likesByStatusId = <String, List<Connection>>{};
|
||||||
|
@ -43,8 +42,9 @@ class InteractionsManager extends ChangeNotifier {
|
||||||
|
|
||||||
FutureResult<List<Connection>, ExecError> updateLikesForStatus(
|
FutureResult<List<Connection>, ExecError> updateLikesForStatus(
|
||||||
String statusId) async {
|
String statusId) async {
|
||||||
final idForCall = _mapStatusId(statusId);
|
final likesResult =
|
||||||
final likesResult = await InteractionsClient(profile).getLikes(idForCall);
|
await InteractionsClient(getIt<AccountsService>().currentProfile)
|
||||||
|
.getLikes(statusId);
|
||||||
if (likesResult.isSuccess) {
|
if (likesResult.isSuccess) {
|
||||||
_likesByStatusId[statusId] = likesResult.value;
|
_likesByStatusId[statusId] = likesResult.value;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
@ -54,20 +54,13 @@ class InteractionsManager extends ChangeNotifier {
|
||||||
|
|
||||||
FutureResult<List<Connection>, ExecError> updateResharesForStatus(
|
FutureResult<List<Connection>, ExecError> updateResharesForStatus(
|
||||||
String statusId) async {
|
String statusId) async {
|
||||||
final idForCall = _mapStatusId(statusId);
|
|
||||||
final resharesResult =
|
final resharesResult =
|
||||||
await InteractionsClient(profile).getReshares(idForCall);
|
await InteractionsClient(getIt<AccountsService>().currentProfile)
|
||||||
|
.getReshares(statusId);
|
||||||
if (resharesResult.isSuccess) {
|
if (resharesResult.isSuccess) {
|
||||||
_resharesByStatusId[statusId] = resharesResult.value;
|
_resharesByStatusId[statusId] = resharesResult.value;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
return resharesResult;
|
return resharesResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
String _mapStatusId(String statusId) {
|
|
||||||
return getIt<ActiveProfileSelector<EntryManagerService>>()
|
|
||||||
.getForProfile(profile)
|
|
||||||
.transform((m) => m.mapInteractionId(statusId))
|
|
||||||
.getValueOrElse(() => statusId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
class ReshareViaService {
|
||||||
|
final _postsVia = <String, ResharedViaData>{};
|
||||||
|
|
||||||
|
ResharedViaData? getForPost(String postId) => _postsVia[postId];
|
||||||
|
|
||||||
|
void upsertResharedVia({required String postId, required String resharerId}) {
|
||||||
|
final resharedData = _postsVia.putIfAbsent(
|
||||||
|
postId,
|
||||||
|
() => ResharedViaData(postId: postId),
|
||||||
|
);
|
||||||
|
|
||||||
|
_postsVia[postId] = resharedData.withUpsertedResharer(resharerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ResharedViaData {
|
||||||
|
final String postId;
|
||||||
|
final Set<String> resharers;
|
||||||
|
|
||||||
|
bool get hasResharedVia => resharers.isNotEmpty;
|
||||||
|
|
||||||
|
const ResharedViaData({
|
||||||
|
required this.postId,
|
||||||
|
this.resharers = const {},
|
||||||
|
});
|
||||||
|
|
||||||
|
ResharedViaData withUpsertedResharer(String resharerId) => ResharedViaData(
|
||||||
|
postId: postId,
|
||||||
|
resharers: {resharerId, ...resharers},
|
||||||
|
);
|
||||||
|
}
|
|
@ -95,7 +95,6 @@ extension TimelineEntryFilterOps on TimelineEntryFilter {
|
||||||
var authorFiltered = authorFilters.isEmpty ? true : false;
|
var authorFiltered = authorFilters.isEmpty ? true : false;
|
||||||
for (final filter in authorFilters) {
|
for (final filter in authorFilters) {
|
||||||
if (filter.isFiltered(entry.authorId) ||
|
if (filter.isFiltered(entry.authorId) ||
|
||||||
filter.isFiltered(entry.reshareAuthorId) ||
|
|
||||||
filter.isFiltered(entry.parentAuthorId)) {
|
filter.isFiltered(entry.parentAuthorId)) {
|
||||||
authorFiltered = true;
|
authorFiltered = true;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1452,5 +1452,5 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.1"
|
version: "3.1.1"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.0.0-0 <4.0.0"
|
dart: ">=3.0.0 <4.0.0"
|
||||||
flutter: ">=3.7.0"
|
flutter: ">=3.7.0"
|
||||||
|
|
|
@ -5,7 +5,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||||
version: 0.6.0+1
|
version: 0.6.0+1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=2.18.2 <3.0.0'
|
sdk: '>=3.0.0 <4.0.0'
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
|
|
Ładowanie…
Reference in New Issue