kopia lustrzana https://gitlab.com/mysocialportal/relatica
Merge branch 'reshare-post-fix' into 'main'
Reshare post fix See merge request mysocialportal/relatica!35merge-requests/67/merge
commit
c59b427133
|
@ -14,6 +14,7 @@
|
|||
to make the zooming in of images more predictable.
|
||||
* Fixes
|
||||
* Seemingly disappearing contacts on Contacts screen have been corrected.
|
||||
* Workaround for a regresssion in Friendica 2023.04 for reshared posts
|
||||
* New Features
|
||||
* Responsive design for allowing the image and video attachments to scale up for larger videos
|
||||
but limit timeline/list width on very large screens.
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:go_router/go_router.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:relatica/models/exec_error.dart';
|
||||
import 'package:relatica/services/auth_service.dart';
|
||||
import 'package:result_monad/result_monad.dart';
|
||||
|
||||
import '../../globals.dart';
|
||||
|
@ -111,7 +112,21 @@ class _InteractionsBarControlState extends State<InteractionsBarControl> {
|
|||
}
|
||||
|
||||
Future<void> addComment() async {
|
||||
context.push('/comment/new?parent_id=${widget.entry.id}');
|
||||
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) {
|
||||
context.push('/comment/new?parent_id=${widget.entry.id}');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> unResharePost() async {
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
import 'friendica_version.dart';
|
||||
|
||||
final FriendicaVersionRequirement unknownRequirement =
|
||||
FriendicaVersionRequirement(unknown);
|
||||
|
||||
class FriendicaVersionRequirement {
|
||||
final FriendicaVersion minimumVersion;
|
||||
final FriendicaVersion? maxVersion;
|
||||
|
||||
const FriendicaVersionRequirement(this.minimumVersion, {this.maxVersion});
|
||||
|
||||
bool versionMeetsRequirement(FriendicaVersion version) {
|
||||
if (version < minimumVersion) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (maxVersion == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return version <= maxVersion!;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
if (maxVersion == null) {
|
||||
return 'requires at least Friendica $minimumVersion';
|
||||
}
|
||||
|
||||
return 'works only on Friendica $minimumVersion to $maxVersion';
|
||||
}
|
||||
}
|
|
@ -16,6 +16,8 @@ class TimelineEntry {
|
|||
|
||||
final String parentAuthorId;
|
||||
|
||||
final String reshareOriginalPostId;
|
||||
|
||||
final String reshareAuthor;
|
||||
|
||||
final String reshareAuthorId;
|
||||
|
@ -61,6 +63,7 @@ class TimelineEntry {
|
|||
TimelineEntry(
|
||||
{this.id = '',
|
||||
this.parentId = '',
|
||||
this.reshareOriginalPostId = '',
|
||||
this.creationTimestamp = 0,
|
||||
this.backdatedTimestamp = 0,
|
||||
this.modificationTimestamp = 0,
|
||||
|
@ -91,6 +94,7 @@ class TimelineEntry {
|
|||
backdatedTimestamp = DateTime.now().millisecondsSinceEpoch,
|
||||
modificationTimestamp = DateTime.now().millisecondsSinceEpoch,
|
||||
id = randomId(),
|
||||
reshareOriginalPostId = '',
|
||||
youReshared = DateTime.now().second ~/ 2 == 0 ? true : false,
|
||||
visibility = DateTime.now().second ~/ 2 == 0
|
||||
? Visibility.public()
|
||||
|
@ -122,6 +126,7 @@ class TimelineEntry {
|
|||
bool? isReshare,
|
||||
Visibility? visibility,
|
||||
String? id,
|
||||
String? reshareOriginalPostId,
|
||||
String? parentId,
|
||||
String? externalLink,
|
||||
String? body,
|
||||
|
@ -148,6 +153,8 @@ class TimelineEntry {
|
|||
modificationTimestamp:
|
||||
modificationTimestamp ?? this.modificationTimestamp,
|
||||
id: id ?? this.id,
|
||||
reshareOriginalPostId:
|
||||
reshareOriginalPostId ?? this.reshareOriginalPostId,
|
||||
youReshared: isReshare ?? this.youReshared,
|
||||
visibility: visibility ?? this.visibility,
|
||||
parentId: parentId ?? this.parentId,
|
||||
|
@ -187,6 +194,7 @@ class TimelineEntry {
|
|||
other is TimelineEntry &&
|
||||
runtimeType == other.runtimeType &&
|
||||
id == other.id &&
|
||||
reshareOriginalPostId == other.reshareOriginalPostId &&
|
||||
parentId == other.parentId &&
|
||||
parentAuthor == other.parentAuthor &&
|
||||
parentAuthorId == other.parentAuthorId &&
|
||||
|
@ -214,6 +222,7 @@ class TimelineEntry {
|
|||
@override
|
||||
int get hashCode =>
|
||||
id.hashCode ^
|
||||
reshareOriginalPostId.hashCode ^
|
||||
parentId.hashCode ^
|
||||
parentAuthor.hashCode ^
|
||||
parentAuthorId.hashCode ^
|
||||
|
|
|
@ -76,15 +76,18 @@ extension TimelineEntryMastodonExtensions on TimelineEntry {
|
|||
|
||||
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>? tags = json['tags'];
|
||||
|
@ -106,6 +109,7 @@ extension TimelineEntryMastodonExtensions on TimelineEntry {
|
|||
youReshared: youReshared,
|
||||
visibility: visibility,
|
||||
id: id,
|
||||
reshareOriginalPostId: reshareOriginalPostId,
|
||||
parentId: parentId,
|
||||
parentAuthorId: parentAuthorId,
|
||||
reshareAuthor: reshareAuthor,
|
||||
|
|
|
@ -19,6 +19,7 @@ class ConnectionsManager extends ChangeNotifier {
|
|||
static final _logger = Logger('$ConnectionsManager');
|
||||
late final IConnectionsRepo conRepo;
|
||||
late final IGroupsRepo groupsRepo;
|
||||
var groupsNotInitialized = true;
|
||||
|
||||
ConnectionsManager(this.conRepo, this.groupsRepo);
|
||||
|
||||
|
@ -200,7 +201,8 @@ class ConnectionsManager extends ChangeNotifier {
|
|||
|
||||
List<GroupData> getMyGroups() {
|
||||
final myGroups = groupsRepo.getMyGroups();
|
||||
if (myGroups.isEmpty) {
|
||||
if (groupsNotInitialized) {
|
||||
groupsNotInitialized = true;
|
||||
_updateMyGroups(true);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import '../models/media_attachment_uploads/new_entry_media_items.dart';
|
|||
import '../models/timeline_entry.dart';
|
||||
import '../models/visibility.dart';
|
||||
import 'auth_service.dart';
|
||||
import 'feature_version_checker.dart';
|
||||
import 'media_upload_attachment_helper.dart';
|
||||
|
||||
class EntryManagerService extends ChangeNotifier {
|
||||
|
@ -44,8 +45,9 @@ class EntryManagerService extends ChangeNotifier {
|
|||
|
||||
Result<EntryTreeItem, ExecError> getPostTreeEntryBy(String id) {
|
||||
_logger.finest('Getting post: $id');
|
||||
final idForCall = mapInteractionId(id);
|
||||
final currentId = getIt<AccountsService>().currentProfile.userId;
|
||||
final postNode = _getPostRootNode(id);
|
||||
final postNode = _getPostRootNode(idForCall);
|
||||
if (postNode == null) {
|
||||
return Result.error(ExecError(
|
||||
type: ErrorType.notFound,
|
||||
|
@ -179,6 +181,7 @@ class EntryManagerService extends ChangeNotifier {
|
|||
required Visibility newMediaItemVisibility,
|
||||
}) async {
|
||||
_logger.finest('Editing post: $text');
|
||||
final idForCall = mapInteractionId(id);
|
||||
final mediaIds = existingMediaItems
|
||||
.map((m) => m.scales.isEmpty ? m.id : m.scales.first.id)
|
||||
.toList();
|
||||
|
@ -225,7 +228,10 @@ class EntryManagerService extends ChangeNotifier {
|
|||
|
||||
final result = await StatusesClient(getIt<AccountsService>().currentProfile)
|
||||
.editStatus(
|
||||
id: id, text: text, spoilerText: spoilerText, mediaIds: mediaIds)
|
||||
id: idForCall,
|
||||
text: text,
|
||||
spoilerText: spoilerText,
|
||||
mediaIds: mediaIds)
|
||||
.andThenSuccessAsync((item) async {
|
||||
await processNewItems(
|
||||
[item], getIt<AccountsService>().currentProfile.username, null);
|
||||
|
@ -394,13 +400,29 @@ class EntryManagerService extends ChangeNotifier {
|
|||
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 {
|
||||
_logger.finest('Refreshing post: $id');
|
||||
final client = StatusesClient(getIt<AccountsService>().currentProfile);
|
||||
final idForCall = mapInteractionId(id);
|
||||
final result = await client
|
||||
.getPostOrComment(id, fullContext: false)
|
||||
.getPostOrComment(idForCall, fullContext: false)
|
||||
.andThenAsync((rootItems) async => await client
|
||||
.getPostOrComment(id, fullContext: true)
|
||||
.getPostOrComment(idForCall, fullContext: true)
|
||||
.andThenSuccessAsync(
|
||||
(contextItems) async => [...rootItems, ...contextItems]))
|
||||
.andThenSuccessAsync((items) async {
|
||||
|
@ -424,8 +446,9 @@ class EntryManagerService extends ChangeNotifier {
|
|||
FutureResult<EntryTreeItem, ExecError> resharePost(String id) async {
|
||||
_logger.finest('Resharing post: $id');
|
||||
final client = StatusesClient(getIt<AccountsService>().currentProfile);
|
||||
final idForCall = mapInteractionId(id);
|
||||
final result =
|
||||
await client.resharePost(id).andThenSuccessAsync((item) async {
|
||||
await client.resharePost(idForCall).andThenSuccessAsync((item) async {
|
||||
await processNewItems([item], client.profile.username, null);
|
||||
});
|
||||
|
||||
|
@ -446,8 +469,9 @@ class EntryManagerService extends ChangeNotifier {
|
|||
FutureResult<bool, ExecError> unResharePost(String id) async {
|
||||
_logger.finest('Unresharing post: $id');
|
||||
final client = StatusesClient(getIt<AccountsService>().currentProfile);
|
||||
final idForCall = mapInteractionId(id);
|
||||
final result =
|
||||
await client.unResharePost(id).andThenSuccessAsync((item) async {
|
||||
await client.unResharePost(idForCall).andThenSuccessAsync((item) async {
|
||||
await processNewItems([item], client.profile.username, null);
|
||||
});
|
||||
|
||||
|
@ -463,20 +487,29 @@ class EntryManagerService extends ChangeNotifier {
|
|||
|
||||
FutureResult<EntryTreeItem, ExecError> toggleFavorited(
|
||||
String id, bool newStatus) async {
|
||||
final client = InteractionsClient(getIt<AccountsService>().currentProfile);
|
||||
final result = await client.changeFavoriteStatus(id, newStatus);
|
||||
final profile = getIt<AccountsService>().currentProfile;
|
||||
final interactionClient = InteractionsClient(profile);
|
||||
final postsClient = StatusesClient(profile);
|
||||
final idForCall = mapInteractionId(id);
|
||||
final result =
|
||||
await interactionClient.changeFavoriteStatus(idForCall, newStatus);
|
||||
if (result.isFailure) {
|
||||
return result.errorCast();
|
||||
}
|
||||
|
||||
final update = result.value;
|
||||
final updateResult =
|
||||
await postsClient.getPostOrComment(id, fullContext: false);
|
||||
if (updateResult.isFailure) {
|
||||
return updateResult.errorCast();
|
||||
}
|
||||
final update = updateResult.value.first;
|
||||
_entries[update.id] = update;
|
||||
final node = update.parentId.isEmpty
|
||||
? _postNodes[update.id]!
|
||||
: _postNodes[_parentPostIds[update.id]]!;
|
||||
|
||||
notifyListeners();
|
||||
return Result.ok(_nodeToTreeItem(node, client.profile.userId));
|
||||
return Result.ok(_nodeToTreeItem(node, interactionClient.profile.userId));
|
||||
}
|
||||
|
||||
EntryTreeItem _nodeToTreeItem(_Node node, String currentId) {
|
||||
|
|
|
@ -6,11 +6,13 @@ import 'package:result_monad/result_monad.dart';
|
|||
import '../globals.dart';
|
||||
import '../models/exec_error.dart';
|
||||
import '../models/friendica_version.dart';
|
||||
import '../models/friendica_version_requirement.dart';
|
||||
|
||||
enum RelaticaFeatures {
|
||||
diasporaReshare('Resharing Diaspora Posts'),
|
||||
directMessageCreation('Direct message creation with OAuth login'),
|
||||
postSpoilerText('Spoiler Text on Posts'),
|
||||
reshareIdFix('Reshare ID fix'),
|
||||
statusEditing('Post/Comment Editing'),
|
||||
usingActualFollowRequests(
|
||||
'Using Follow Request System not Friend Request Notifications'),
|
||||
|
@ -29,8 +31,8 @@ class FriendicaVersionChecker {
|
|||
const FriendicaVersionChecker();
|
||||
|
||||
bool canUseFeature(RelaticaFeatures feature) {
|
||||
final neededVersion = featureVersionRequirement[feature];
|
||||
if (neededVersion == null) {
|
||||
final requirement = featureVersionRequirement[feature];
|
||||
if (requirement == null) {
|
||||
_logger.severe(
|
||||
'Return false since no minimum version data in table for: $feature',
|
||||
);
|
||||
|
@ -39,7 +41,9 @@ class FriendicaVersionChecker {
|
|||
|
||||
return getIt<ActiveProfileSelector<InstanceInfo>>()
|
||||
.activeEntry
|
||||
.andThenSuccess((info) => info.friendicaVersion >= neededVersion)
|
||||
.andThenSuccess(
|
||||
(info) => requirement.versionMeetsRequirement(info.friendicaVersion),
|
||||
)
|
||||
.fold(
|
||||
onSuccess: (versionMet) => versionMet,
|
||||
onError: (error) {
|
||||
|
@ -61,17 +65,26 @@ class FriendicaVersionChecker {
|
|||
);
|
||||
}
|
||||
|
||||
FriendicaVersion getVersionRequirement(RelaticaFeatures feature) =>
|
||||
featureVersionRequirement[feature] ?? unknown;
|
||||
FriendicaVersionRequirement getVersionRequirement(RelaticaFeatures feature) =>
|
||||
featureVersionRequirement[feature] ?? unknownRequirement;
|
||||
|
||||
String versionErrorString(RelaticaFeatures feature) =>
|
||||
"${feature.label} requires at least Friendica ${getVersionRequirement(feature).toVersionString()}";
|
||||
"${feature.label} ${getVersionRequirement(feature)}";
|
||||
|
||||
static final featureVersionRequirement = <RelaticaFeatures, FriendicaVersion>{
|
||||
RelaticaFeatures.diasporaReshare: v2023_04,
|
||||
RelaticaFeatures.directMessageCreation: v2023_04,
|
||||
RelaticaFeatures.postSpoilerText: v2023_04,
|
||||
RelaticaFeatures.statusEditing: v2023_04,
|
||||
RelaticaFeatures.usingActualFollowRequests: v2023_04,
|
||||
static final featureVersionRequirement =
|
||||
<RelaticaFeatures, FriendicaVersionRequirement>{
|
||||
RelaticaFeatures.diasporaReshare: FriendicaVersionRequirement(v2023_04),
|
||||
RelaticaFeatures.directMessageCreation: FriendicaVersionRequirement(
|
||||
v2023_04,
|
||||
),
|
||||
RelaticaFeatures.postSpoilerText: FriendicaVersionRequirement(v2023_04),
|
||||
RelaticaFeatures.reshareIdFix: FriendicaVersionRequirement(
|
||||
v2023_04,
|
||||
maxVersion: v2023_04,
|
||||
),
|
||||
RelaticaFeatures.statusEditing: FriendicaVersionRequirement(v2023_04),
|
||||
RelaticaFeatures.usingActualFollowRequests: FriendicaVersionRequirement(
|
||||
v2023_04,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -5,7 +5,9 @@ import '../friendica_client/friendica_client.dart';
|
|||
import '../globals.dart';
|
||||
import '../models/connection.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 {
|
||||
final _likesByStatusId = <String, List<Connection>>{};
|
||||
|
@ -31,9 +33,10 @@ class InteractionsManager extends ChangeNotifier {
|
|||
|
||||
FutureResult<List<Connection>, ExecError> updateLikesForStatus(
|
||||
String statusId) async {
|
||||
final idForCall = _mapStatusId(statusId);
|
||||
final likesResult =
|
||||
await InteractionsClient(getIt<AccountsService>().currentProfile)
|
||||
.getLikes(statusId);
|
||||
.getLikes(idForCall);
|
||||
if (likesResult.isSuccess) {
|
||||
_likesByStatusId[statusId] = likesResult.value;
|
||||
notifyListeners();
|
||||
|
@ -43,13 +46,21 @@ class InteractionsManager extends ChangeNotifier {
|
|||
|
||||
FutureResult<List<Connection>, ExecError> updateResharesForStatus(
|
||||
String statusId) async {
|
||||
final idForCall = _mapStatusId(statusId);
|
||||
final resharesResult =
|
||||
await InteractionsClient(getIt<AccountsService>().currentProfile)
|
||||
.getReshares(statusId);
|
||||
.getReshares(idForCall);
|
||||
if (resharesResult.isSuccess) {
|
||||
_resharesByStatusId[statusId] = resharesResult.value;
|
||||
notifyListeners();
|
||||
}
|
||||
return resharesResult;
|
||||
}
|
||||
|
||||
String _mapStatusId(String statusId) {
|
||||
return getIt<ActiveProfileSelector<EntryManagerService>>()
|
||||
.activeEntry
|
||||
.transform((m) => m.mapInteractionId(statusId))
|
||||
.getValueOrElse(() => statusId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ class TimelineManager extends ChangeNotifier {
|
|||
|
||||
final IGroupsRepo groupsRepo;
|
||||
final EntryManagerService entryManagerService;
|
||||
var groupsNotInitialized = true;
|
||||
|
||||
final cachedTimelines = <TimelineIdentifiers, Timeline>{};
|
||||
|
||||
|
@ -41,8 +42,9 @@ class TimelineManager extends ChangeNotifier {
|
|||
}
|
||||
|
||||
Result<List<GroupData>, ExecError> getGroups() {
|
||||
if (groupsRepo.getMyGroups().isEmpty) {
|
||||
if (groupsNotInitialized) {
|
||||
_refreshGroupData();
|
||||
groupsNotInitialized = false;
|
||||
return Result.ok([]);
|
||||
}
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue