kopia lustrzana https://gitlab.com/mysocialportal/relatica
rodzic
8c6aa6fee7
commit
40eed4a5d5
|
@ -38,7 +38,9 @@
|
|||
* Ability to turn off Spoiler Alert/CWs at the application
|
||||
level. Defaults to on. ([Feature #42](https://gitlab.com/mysocialportal/relatica/-/issues/42))
|
||||
* Throws a confirm dialog box up if adding a comment to a post/comment over 30 days
|
||||
old. ([Feature #58](https://gitlab.com/mysocialportal/relatica/-/issues/58))
|
||||
old. ([Feature #58](https://gitlab.com/mysocialportal/relatica/-/issues/58))
|
||||
* Autocomplete now lists hashtags and accounts that are used in a post or post above the rest of the
|
||||
results. ([Feature #28](https://gitlab.com/mysocialportal/relatica/-/issues/28))
|
||||
|
||||
## Version 0.10.1 (beta)
|
||||
|
||||
|
|
|
@ -1,21 +1,34 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../globals.dart';
|
||||
import '../../services/entry_manager_service.dart';
|
||||
import '../../services/hashtag_service.dart';
|
||||
import '../../utils/active_profile_selector.dart';
|
||||
|
||||
class HashtagAutocompleteOptions extends StatelessWidget {
|
||||
const HashtagAutocompleteOptions({
|
||||
super.key,
|
||||
required this.id,
|
||||
required this.query,
|
||||
required this.onHashtagTap,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final String query;
|
||||
final ValueSetter<String> onHashtagTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final hashtags = getIt<HashtagService>().getMatchingHashTags(query);
|
||||
final manager = context
|
||||
.read<ActiveProfileSelector<EntryManagerService>>()
|
||||
.activeEntry
|
||||
.value;
|
||||
final postTreeHashtags =
|
||||
manager.getPostTreeHashtags(id).getValueOrElse(() => [])..sort();
|
||||
final hashtagsFromService =
|
||||
getIt<HashtagService>().getMatchingHashTags(query);
|
||||
final hashtags = [...postTreeHashtags, ...hashtagsFromService];
|
||||
|
||||
if (hashtags.isEmpty) return const SizedBox.shrink();
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../globals.dart';
|
||||
import '../../models/connection.dart';
|
||||
import '../../services/connections_manager.dart';
|
||||
import '../../services/entry_manager_service.dart';
|
||||
import '../../utils/active_profile_selector.dart';
|
||||
import '../image_control.dart';
|
||||
|
||||
|
@ -12,24 +13,39 @@ class MentionAutocompleteOptions extends StatelessWidget {
|
|||
|
||||
const MentionAutocompleteOptions({
|
||||
super.key,
|
||||
required this.id,
|
||||
required this.query,
|
||||
required this.onMentionUserTap,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final String query;
|
||||
final ValueSetter<Connection> onMentionUserTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final users = getIt<ActiveProfileSelector<ConnectionsManager>>()
|
||||
final entryManager = context
|
||||
.read<ActiveProfileSelector<EntryManagerService>>()
|
||||
.activeEntry
|
||||
.andThenSuccess((manager) => manager.getKnownUsersByName(query))
|
||||
.fold(
|
||||
onSuccess: (users) => users,
|
||||
onError: (error) {
|
||||
_logger.severe('Error getting users list: $error');
|
||||
return [];
|
||||
});
|
||||
.value;
|
||||
|
||||
final connectionManager = context
|
||||
.read<ActiveProfileSelector<ConnectionsManager>>()
|
||||
.activeEntry
|
||||
.value;
|
||||
|
||||
final postTreeUsers = entryManager
|
||||
.getPostTreeConnectionIds(id)
|
||||
.getValueOrElse(() => [])
|
||||
.map((id) => connectionManager.getById(id))
|
||||
.where((result) => result.isSuccess)
|
||||
.map((result) => result.value)
|
||||
.toList()
|
||||
..sort((u1, u2) => u1.name.compareTo(u2.name));
|
||||
|
||||
final knownUsers = connectionManager.getKnownUsersByName(query);
|
||||
|
||||
final users = [...postTreeUsers, ...knownUsers];
|
||||
|
||||
if (users.isEmpty) return const SizedBox.shrink();
|
||||
|
||||
|
|
|
@ -333,6 +333,7 @@ class _EditorScreenState extends State<EditorScreen> {
|
|||
trigger: '@',
|
||||
optionsViewBuilder: (context, autocompleteQuery, controller) {
|
||||
return MentionAutocompleteOptions(
|
||||
id: parentEntry?.id ?? '',
|
||||
query: autocompleteQuery.query,
|
||||
onMentionUserTap: (user) {
|
||||
final autocomplete = MultiTriggerAutocomplete.of(context);
|
||||
|
@ -345,6 +346,7 @@ class _EditorScreenState extends State<EditorScreen> {
|
|||
trigger: '#',
|
||||
optionsViewBuilder: (context, autocompleteQuery, controller) {
|
||||
return HashtagAutocompleteOptions(
|
||||
id: parentEntry?.id ?? '',
|
||||
query: autocompleteQuery.query,
|
||||
onHashtagTap: (hashtag) {
|
||||
final autocomplete = MultiTriggerAutocomplete.of(context);
|
||||
|
|
|
@ -407,6 +407,7 @@ class _FilterEditorScreenState extends State<FilterEditorScreen> {
|
|||
optionsViewBuilder:
|
||||
(ovbContext, autocompleteQuery, controller) {
|
||||
return MentionAutocompleteOptions(
|
||||
id: '',
|
||||
query: autocompleteQuery.query,
|
||||
onMentionUserTap: (user) {
|
||||
final autocomplete =
|
||||
|
@ -480,6 +481,7 @@ class _FilterEditorScreenState extends State<FilterEditorScreen> {
|
|||
optionsViewBuilder:
|
||||
(ovbContext, autocompleteQuery, controller) {
|
||||
return HashtagAutocompleteOptions(
|
||||
id: '',
|
||||
query: autocompleteQuery.query,
|
||||
onHashtagTap: (hashtag) {
|
||||
final autocomplete =
|
||||
|
|
|
@ -49,6 +49,7 @@ class MessagesNewThread extends StatelessWidget {
|
|||
trigger: '@',
|
||||
optionsViewBuilder: (context, autocompleteQuery, controller) {
|
||||
return MentionAutocompleteOptions(
|
||||
id: '',
|
||||
query: autocompleteQuery.query,
|
||||
onMentionUserTap: (user) {
|
||||
final autocomplete =
|
||||
|
|
|
@ -23,6 +23,8 @@ class EntryManagerService extends ChangeNotifier {
|
|||
final _entries = <String, TimelineEntry>{};
|
||||
final _parentPostIds = <String, String>{};
|
||||
final _postNodes = <String, _Node>{};
|
||||
final _postThreadHashtags = <String, Set<String>>{};
|
||||
final _postTreeConnections = <String, Set<String>>{};
|
||||
final Profile profile;
|
||||
|
||||
EntryManagerService(this.profile);
|
||||
|
@ -61,6 +63,32 @@ class EntryManagerService extends ChangeNotifier {
|
|||
return Result.ok(_nodeToTreeItem(postNode, profile.userId));
|
||||
}
|
||||
|
||||
Result<List<String>, ExecError> getPostTreeHashtags(String id) {
|
||||
final postId = _getPostRootNode(id)?.id ?? '';
|
||||
if (postId.isEmpty) {
|
||||
return buildErrorResult(
|
||||
type: ErrorType.notFound,
|
||||
message: 'Root Post ID not found for $id',
|
||||
);
|
||||
}
|
||||
final hashtags = _postThreadHashtags[postId]?.toList() ?? [];
|
||||
|
||||
return Result.ok(hashtags);
|
||||
}
|
||||
|
||||
Result<List<String>, ExecError> getPostTreeConnectionIds(String id) {
|
||||
final postId = _getPostRootNode(id)?.id ?? '';
|
||||
if (postId.isEmpty) {
|
||||
return buildErrorResult(
|
||||
type: ErrorType.notFound,
|
||||
message: 'Root Post ID not found for $id',
|
||||
);
|
||||
}
|
||||
final hashtags = _postTreeConnections[postId]?.toList() ?? [];
|
||||
|
||||
return Result.ok(hashtags);
|
||||
}
|
||||
|
||||
Result<TimelineEntry, ExecError> getEntryById(String id) {
|
||||
if (_entries.containsKey(id)) {
|
||||
return Result.ok(_entries[id]!);
|
||||
|
@ -353,6 +381,11 @@ class EntryManagerService extends ChangeNotifier {
|
|||
if (item.parentId.isEmpty) {
|
||||
final postNode =
|
||||
_postNodes.putIfAbsent(item.id, () => _Node(item.id));
|
||||
final pth = _postThreadHashtags.putIfAbsent(item.id, () => {});
|
||||
final ptc = _postTreeConnections.putIfAbsent(item.id, () => {});
|
||||
pth.addAll(item.tags);
|
||||
ptc.add(item.authorId);
|
||||
ptc.add(item.parentAuthorId);
|
||||
postNodesToReturn.add(postNode);
|
||||
allSeenItems.remove(item);
|
||||
} else {
|
||||
|
@ -364,6 +397,14 @@ class EntryManagerService extends ChangeNotifier {
|
|||
'Error finding parent ${item.parentId} for entry ${item.id}');
|
||||
continue;
|
||||
}
|
||||
final pth =
|
||||
_postThreadHashtags.putIfAbsent(parentParentPostId!, () => {});
|
||||
final ptc =
|
||||
_postTreeConnections.putIfAbsent(parentParentPostId, () => {});
|
||||
pth.addAll(item.tags);
|
||||
ptc.add(item.authorId);
|
||||
ptc.add(item.parentAuthorId);
|
||||
|
||||
final parentPostNode = _postNodes[parentParentPostId]!;
|
||||
postNodesToReturn.add(parentPostNode);
|
||||
_parentPostIds[item.id] = parentPostNode.id;
|
||||
|
|
Ładowanie…
Reference in New Issue