Merge branch 'reshare-post-fix' into 'main'

Reshare post fix

See merge request mysocialportal/relatica!35
codemagic-setup
HankG 2023-04-27 13:30:49 +00:00
commit c59b427133
10 zmienionych plików z 149 dodań i 27 usunięć

Wyświetl plik

@ -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.

Wyświetl plik

@ -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 {

Wyświetl plik

@ -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';
}
}

Wyświetl plik

@ -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 ^

Wyświetl plik

@ -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,

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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) {

Wyświetl plik

@ -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,
),
};
}

Wyświetl plik

@ -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);
}
}

Wyświetl plik

@ -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([]);
}