kopia lustrzana https://gitlab.com/mysocialportal/relatica
Add real hashtag processing and storage
rodzic
2a3c2296a1
commit
fa1609bdc3
10
README.md
10
README.md
|
@ -2,7 +2,15 @@
|
||||||
|
|
||||||
A Flutter application for interfacing with the Friendica social network.
|
A Flutter application for interfacing with the Friendica social network.
|
||||||
|
|
||||||
For Linux development be sure that libsecret-1-dev and libjsoncpp-dev are installed on the machine. For running only make sure the non-dev versions are...
|
For Linux development be sure that libsecret-1-dev and libjsoncpp-dev are installed on the machine. For running only
|
||||||
|
make sure the non-dev versions are...
|
||||||
|
|
||||||
|
## Development Notes
|
||||||
|
|
||||||
|
Whenever a model is changed that is stored in ObjectBox it is necessary to execute the command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
flutter pub run build_runner build
|
||||||
|
```
|
||||||
|
|
||||||
Licensed with the Mozilla Public License 2.0 copyleft license.
|
Licensed with the Mozilla Public License 2.0 copyleft license.
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
import '../../models/hashtag.dart';
|
||||||
|
|
||||||
|
class IHashtagRepo {
|
||||||
|
void add(Hashtag tag) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> getMatchingHashTags(String text) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
import '../../globals.dart';
|
||||||
|
import '../../models/hashtag.dart';
|
||||||
|
import '../../objectbox.g.dart';
|
||||||
|
import '../interfaces/hashtag_repo_intf.dart';
|
||||||
|
import 'objectbox_cache.dart';
|
||||||
|
|
||||||
|
class ObjectBoxHashtagRepo implements IHashtagRepo {
|
||||||
|
late final Box<Hashtag> box;
|
||||||
|
|
||||||
|
ObjectBoxHashtagRepo() {
|
||||||
|
box = getIt<ObjectBoxCache>().store.box<Hashtag>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void add(Hashtag tag) {
|
||||||
|
box.putAsync(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<String> getMatchingHashTags(String text) {
|
||||||
|
return (box
|
||||||
|
.query(
|
||||||
|
text.length <= 2
|
||||||
|
? Hashtag_.tag.startsWith(text, caseSensitive: false)
|
||||||
|
: Hashtag_.tag.contains(text, caseSensitive: false),
|
||||||
|
)
|
||||||
|
.order(Hashtag_.tag)
|
||||||
|
.build()
|
||||||
|
..limit = 100)
|
||||||
|
.find()
|
||||||
|
.map((h) => h.tag)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,9 +7,11 @@ import 'package:result_monad/result_monad.dart';
|
||||||
|
|
||||||
import 'data/interfaces/connections_repo_intf.dart';
|
import 'data/interfaces/connections_repo_intf.dart';
|
||||||
import 'data/interfaces/groups_repo.intf.dart';
|
import 'data/interfaces/groups_repo.intf.dart';
|
||||||
|
import 'data/interfaces/hashtag_repo_intf.dart';
|
||||||
import 'data/memory/memory_groups_repo.dart';
|
import 'data/memory/memory_groups_repo.dart';
|
||||||
import 'data/objectbox/objectbox_cache.dart';
|
import 'data/objectbox/objectbox_cache.dart';
|
||||||
import 'data/objectbox/objectbox_connections_repo.dart';
|
import 'data/objectbox/objectbox_connections_repo.dart';
|
||||||
|
import 'data/objectbox/objectbox_hashtag_repo.dart';
|
||||||
import 'globals.dart';
|
import 'globals.dart';
|
||||||
import 'models/TimelineIdentifiers.dart';
|
import 'models/TimelineIdentifiers.dart';
|
||||||
import 'routes.dart';
|
import 'routes.dart';
|
||||||
|
@ -54,6 +56,7 @@ void main() async {
|
||||||
final objectBoxCache = await ObjectBoxCache.create();
|
final objectBoxCache = await ObjectBoxCache.create();
|
||||||
getIt.registerSingleton<ObjectBoxCache>(objectBoxCache);
|
getIt.registerSingleton<ObjectBoxCache>(objectBoxCache);
|
||||||
getIt.registerSingleton<IConnectionsRepo>(ObjectBoxConnectionsRepo());
|
getIt.registerSingleton<IConnectionsRepo>(ObjectBoxConnectionsRepo());
|
||||||
|
getIt.registerSingleton<IHashtagRepo>(ObjectBoxHashtagRepo());
|
||||||
getIt.registerSingleton<IGroupsRepo>(MemoryGroupsRepo());
|
getIt.registerSingleton<IGroupsRepo>(MemoryGroupsRepo());
|
||||||
getIt.registerLazySingleton<ConnectionsManager>(() => ConnectionsManager());
|
getIt.registerLazySingleton<ConnectionsManager>(() => ConnectionsManager());
|
||||||
getIt.registerLazySingleton<HashtagService>(() => HashtagService());
|
getIt.registerLazySingleton<HashtagService>(() => HashtagService());
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
import 'package:objectbox/objectbox.dart';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
class Hashtag {
|
||||||
|
@Id()
|
||||||
|
int id;
|
||||||
|
|
||||||
|
@Unique(onConflict: ConflictStrategy.replace)
|
||||||
|
String tag;
|
||||||
|
|
||||||
|
String url;
|
||||||
|
|
||||||
|
@Property(type: PropertyType.date)
|
||||||
|
DateTime lastUpdateTime;
|
||||||
|
|
||||||
|
Hashtag({
|
||||||
|
this.id = 0,
|
||||||
|
required this.tag,
|
||||||
|
required this.url,
|
||||||
|
DateTime? updateTime,
|
||||||
|
}) : lastUpdateTime = updateTime ?? DateTime.now();
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
other is Hashtag && runtimeType == other.runtimeType && tag == other.tag;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => tag.hashCode;
|
||||||
|
}
|
|
@ -58,10 +58,41 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"relations": []
|
"relations": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2:8060242331335522964",
|
||||||
|
"lastPropertyId": "4:985152873657204249",
|
||||||
|
"name": "Hashtag",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "1:3633001791521338712",
|
||||||
|
"name": "id",
|
||||||
|
"type": 6,
|
||||||
|
"flags": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2:3468373950035339457",
|
||||||
|
"name": "tag",
|
||||||
|
"type": 9,
|
||||||
|
"flags": 34848,
|
||||||
|
"indexId": "2:6156017341759176249"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3:5102584273729210526",
|
||||||
|
"name": "url",
|
||||||
|
"type": 9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4:985152873657204249",
|
||||||
|
"name": "lastUpdateTime",
|
||||||
|
"type": 10
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"relations": []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"lastEntityId": "1:1213035855270739890",
|
"lastEntityId": "2:8060242331335522964",
|
||||||
"lastIndexId": "1:8342366639839511243",
|
"lastIndexId": "2:6156017341759176249",
|
||||||
"lastRelationId": "0:0",
|
"lastRelationId": "0:0",
|
||||||
"lastSequenceId": "0:0",
|
"lastSequenceId": "0:0",
|
||||||
"modelVersion": 5,
|
"modelVersion": 5,
|
||||||
|
|
|
@ -15,6 +15,7 @@ import 'package:objectbox/objectbox.dart';
|
||||||
import 'package:objectbox_flutter_libs/objectbox_flutter_libs.dart';
|
import 'package:objectbox_flutter_libs/objectbox_flutter_libs.dart';
|
||||||
|
|
||||||
import 'models/connection.dart';
|
import 'models/connection.dart';
|
||||||
|
import 'models/hashtag.dart';
|
||||||
|
|
||||||
export 'package:objectbox/objectbox.dart'; // so that callers only have to import this file
|
export 'package:objectbox/objectbox.dart'; // so that callers only have to import this file
|
||||||
|
|
||||||
|
@ -73,6 +74,36 @@ final _entities = <ModelEntity>[
|
||||||
flags: 0)
|
flags: 0)
|
||||||
],
|
],
|
||||||
relations: <ModelRelation>[],
|
relations: <ModelRelation>[],
|
||||||
|
backlinks: <ModelBacklink>[]),
|
||||||
|
ModelEntity(
|
||||||
|
id: const IdUid(2, 8060242331335522964),
|
||||||
|
name: 'Hashtag',
|
||||||
|
lastPropertyId: const IdUid(4, 985152873657204249),
|
||||||
|
flags: 0,
|
||||||
|
properties: <ModelProperty>[
|
||||||
|
ModelProperty(
|
||||||
|
id: const IdUid(1, 3633001791521338712),
|
||||||
|
name: 'id',
|
||||||
|
type: 6,
|
||||||
|
flags: 1),
|
||||||
|
ModelProperty(
|
||||||
|
id: const IdUid(2, 3468373950035339457),
|
||||||
|
name: 'tag',
|
||||||
|
type: 9,
|
||||||
|
flags: 34848,
|
||||||
|
indexId: const IdUid(2, 6156017341759176249)),
|
||||||
|
ModelProperty(
|
||||||
|
id: const IdUid(3, 5102584273729210526),
|
||||||
|
name: 'url',
|
||||||
|
type: 9,
|
||||||
|
flags: 0),
|
||||||
|
ModelProperty(
|
||||||
|
id: const IdUid(4, 985152873657204249),
|
||||||
|
name: 'lastUpdateTime',
|
||||||
|
type: 10,
|
||||||
|
flags: 0)
|
||||||
|
],
|
||||||
|
relations: <ModelRelation>[],
|
||||||
backlinks: <ModelBacklink>[])
|
backlinks: <ModelBacklink>[])
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -96,8 +127,8 @@ Future<Store> openStore(
|
||||||
ModelDefinition getObjectBoxModel() {
|
ModelDefinition getObjectBoxModel() {
|
||||||
final model = ModelInfo(
|
final model = ModelInfo(
|
||||||
entities: _entities,
|
entities: _entities,
|
||||||
lastEntityId: const IdUid(1, 1213035855270739890),
|
lastEntityId: const IdUid(2, 8060242331335522964),
|
||||||
lastIndexId: const IdUid(1, 8342366639839511243),
|
lastIndexId: const IdUid(2, 6156017341759176249),
|
||||||
lastRelationId: const IdUid(0, 0),
|
lastRelationId: const IdUid(0, 0),
|
||||||
lastSequenceId: const IdUid(0, 0),
|
lastSequenceId: const IdUid(0, 0),
|
||||||
retiredEntityUids: const [],
|
retiredEntityUids: const [],
|
||||||
|
@ -160,6 +191,40 @@ ModelDefinition getObjectBoxModel() {
|
||||||
..dbStatus =
|
..dbStatus =
|
||||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 18, 0);
|
const fb.Int64Reader().vTableGet(buffer, rootOffset, 18, 0);
|
||||||
|
|
||||||
|
return object;
|
||||||
|
}),
|
||||||
|
Hashtag: EntityDefinition<Hashtag>(
|
||||||
|
model: _entities[1],
|
||||||
|
toOneRelations: (Hashtag object) => [],
|
||||||
|
toManyRelations: (Hashtag object) => {},
|
||||||
|
getId: (Hashtag object) => object.id,
|
||||||
|
setId: (Hashtag object, int id) {
|
||||||
|
object.id = id;
|
||||||
|
},
|
||||||
|
objectToFB: (Hashtag object, fb.Builder fbb) {
|
||||||
|
final tagOffset = fbb.writeString(object.tag);
|
||||||
|
final urlOffset = fbb.writeString(object.url);
|
||||||
|
fbb.startTable(5);
|
||||||
|
fbb.addInt64(0, object.id);
|
||||||
|
fbb.addOffset(1, tagOffset);
|
||||||
|
fbb.addOffset(2, urlOffset);
|
||||||
|
fbb.addInt64(3, object.lastUpdateTime.millisecondsSinceEpoch);
|
||||||
|
fbb.finish(fbb.endTable());
|
||||||
|
return object.id;
|
||||||
|
},
|
||||||
|
objectFromFB: (Store store, ByteData fbData) {
|
||||||
|
final buffer = fb.BufferContext(fbData);
|
||||||
|
final rootOffset = buffer.derefObject(0);
|
||||||
|
|
||||||
|
final object = Hashtag(
|
||||||
|
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
|
||||||
|
tag: const fb.StringReader(asciiOptimization: true)
|
||||||
|
.vTableGet(buffer, rootOffset, 6, ''),
|
||||||
|
url: const fb.StringReader(asciiOptimization: true)
|
||||||
|
.vTableGet(buffer, rootOffset, 8, ''))
|
||||||
|
..lastUpdateTime = DateTime.fromMillisecondsSinceEpoch(
|
||||||
|
const fb.Int64Reader().vTableGet(buffer, rootOffset, 10, 0));
|
||||||
|
|
||||||
return object;
|
return object;
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
@ -204,3 +269,19 @@ class Connection_ {
|
||||||
static final lastUpdateTime =
|
static final lastUpdateTime =
|
||||||
QueryIntegerProperty<Connection>(_entities[0].properties[8]);
|
QueryIntegerProperty<Connection>(_entities[0].properties[8]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// [Hashtag] entity fields to define ObjectBox queries.
|
||||||
|
class Hashtag_ {
|
||||||
|
/// see [Hashtag.id]
|
||||||
|
static final id = QueryIntegerProperty<Hashtag>(_entities[1].properties[0]);
|
||||||
|
|
||||||
|
/// see [Hashtag.tag]
|
||||||
|
static final tag = QueryStringProperty<Hashtag>(_entities[1].properties[1]);
|
||||||
|
|
||||||
|
/// see [Hashtag.url]
|
||||||
|
static final url = QueryStringProperty<Hashtag>(_entities[1].properties[2]);
|
||||||
|
|
||||||
|
/// see [Hashtag.lastUpdateTime]
|
||||||
|
static final lastUpdateTime =
|
||||||
|
QueryIntegerProperty<Hashtag>(_entities[1].properties[3]);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import '../../models/hashtag.dart';
|
||||||
|
|
||||||
|
extension HashtagMastodonExtensions on Hashtag {
|
||||||
|
static Hashtag fromJson(Map<String, dynamic> json) {
|
||||||
|
final tag = json['name'];
|
||||||
|
final url = json['url'];
|
||||||
|
return Hashtag(
|
||||||
|
tag: tag,
|
||||||
|
url: url,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ import '../../services/connections_manager.dart';
|
||||||
import '../../services/hashtag_service.dart';
|
import '../../services/hashtag_service.dart';
|
||||||
import '../../utils/dateutils.dart';
|
import '../../utils/dateutils.dart';
|
||||||
import 'connection_mastodon_extensions.dart';
|
import 'connection_mastodon_extensions.dart';
|
||||||
|
import 'hashtag_mastodon_extensions.dart';
|
||||||
|
|
||||||
final _logger = Logger('TimelineEntryMastodonExtensions');
|
final _logger = Logger('TimelineEntryMastodonExtensions');
|
||||||
|
|
||||||
|
@ -75,9 +76,9 @@ extension TimelineEntryMastodonExtensions on TimelineEntry {
|
||||||
final List<dynamic>? tags = json['tags'];
|
final List<dynamic>? tags = json['tags'];
|
||||||
if (tags?.isNotEmpty ?? false) {
|
if (tags?.isNotEmpty ?? false) {
|
||||||
final tagManager = getIt<HashtagService>();
|
final tagManager = getIt<HashtagService>();
|
||||||
for (final tag in tags!) {
|
for (final tagJson in tags!) {
|
||||||
final tagName = tag['name'];
|
final tag = HashtagMastodonExtensions.fromJson(tagJson);
|
||||||
tagManager.addHashtage(tagName);
|
tagManager.add(tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
class HashtagService extends ChangeNotifier {
|
import '../data/interfaces/hashtag_repo_intf.dart';
|
||||||
final _hashTags = <String>{};
|
import '../globals.dart';
|
||||||
|
import '../models/hashtag.dart';
|
||||||
|
|
||||||
void clear() {
|
class HashtagService extends ChangeNotifier {
|
||||||
_hashTags.clear();
|
late final IHashtagRepo repo;
|
||||||
notifyListeners();
|
|
||||||
|
HashtagService() {
|
||||||
|
repo = getIt<IHashtagRepo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void addHashtage(String hashtag) {
|
void add(Hashtag tag) {
|
||||||
_hashTags.add(hashtag);
|
repo.add(tag);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> getMatchingHashTags(String searchString) {
|
List<String> getMatchingHashTags(String searchString) {
|
||||||
return _hashTags
|
return repo.getMatchingHashTags(searchString);
|
||||||
.where((tag) => tag.toLowerCase().contains(searchString.toLowerCase()))
|
|
||||||
.toList();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Ładowanie…
Reference in New Issue