mirror of
https://gitlab.com/mysocialportal/fediverse-archiving-tools.git
synced 2024-10-18 08:53:31 +00:00
Implement treed post/comment entries
This commit is contained in:
parent
eb9ecd39e5
commit
aaa41c9131
9 changed files with 264 additions and 174 deletions
|
@ -66,7 +66,9 @@ class FriendicaArchiveBrowser extends StatelessWidget {
|
|||
create: (context) => friendicaArchiveService),
|
||||
Provider(create: (context) => pathMappingService),
|
||||
],
|
||||
child: Home(settingsController: settingsController),
|
||||
child: Home(
|
||||
settingsController: settingsController,
|
||||
archiveService: friendicaArchiveService),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/friendica_timeline_entry.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/friendica_entry_tree_item.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/location_data.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/services/path_mapping_service.dart';
|
||||
import 'package:friendica_archive_browser/src/settings/settings_controller.dart';
|
||||
|
@ -14,11 +14,14 @@ import 'package:url_launcher/url_launcher.dart';
|
|||
import 'link_elements_component.dart';
|
||||
import 'media_timeline_component.dart';
|
||||
|
||||
class PostCard extends StatelessWidget {
|
||||
static final _logger = Logger("$PostCard");
|
||||
final FriendicaTimelineEntry post;
|
||||
class TreeEntryCard extends StatelessWidget {
|
||||
static final _logger = Logger("$TreeEntryCard");
|
||||
final FriendicaEntryTreeItem treeEntry;
|
||||
final bool isTopLevel;
|
||||
|
||||
const PostCard({Key? key, required this.post}) : super(key: key);
|
||||
const TreeEntryCard(
|
||||
{Key? key, required this.treeEntry, this.isTopLevel = true})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -31,10 +34,16 @@ class PostCard extends StatelessWidget {
|
|||
Provider.of<SettingsController>(context).dateTimeFormatter;
|
||||
final mapper = Provider.of<PathMappingService>(context);
|
||||
|
||||
final title = post.title.isEmpty ? 'Post' : post.title;
|
||||
final entry = treeEntry.entry;
|
||||
|
||||
final title = entry.title.isNotEmpty
|
||||
? entry.title
|
||||
: entry.parentId.isEmpty
|
||||
? 'Post'
|
||||
: 'Comment on post by ${entry.parentAuthor}';
|
||||
final dateStamp = ' At ' +
|
||||
formatter.format(
|
||||
DateTime.fromMillisecondsSinceEpoch(post.creationTimestamp * 1000)
|
||||
DateTime.fromMillisecondsSinceEpoch(entry.creationTimestamp * 1000)
|
||||
.toLocal());
|
||||
|
||||
return Padding(
|
||||
|
@ -62,15 +71,15 @@ class PostCard extends StatelessWidget {
|
|||
child: IconButton(
|
||||
onPressed: () async => await copyToClipboard(
|
||||
context: context,
|
||||
text: post.toHumanString(mapper, formatter),
|
||||
text: entry.toHumanString(mapper, formatter),
|
||||
snackbarMessage: 'Copied Post to clipboard'),
|
||||
icon: const Icon(Icons.copy)),
|
||||
),
|
||||
]),
|
||||
if (post.post.isNotEmpty) ...[
|
||||
if (entry.post.isNotEmpty) ...[
|
||||
const SizedBox(height: spacingHeight),
|
||||
HtmlWidget(
|
||||
post.post,
|
||||
entry.post,
|
||||
onTapUrl: (url) async {
|
||||
bool canLaunchResult = await canLaunch(url);
|
||||
if (!canLaunchResult) {
|
||||
|
@ -87,16 +96,22 @@ class PostCard extends StatelessWidget {
|
|||
},
|
||||
)
|
||||
],
|
||||
if (post.locationData.hasData())
|
||||
post.locationData.toWidget(spacingHeight),
|
||||
if (post.links.isNotEmpty) ...[
|
||||
if (entry.locationData.hasData())
|
||||
entry.locationData.toWidget(spacingHeight),
|
||||
if (entry.links.isNotEmpty) ...[
|
||||
const SizedBox(height: spacingHeight),
|
||||
LinkElementsComponent(links: post.links)
|
||||
LinkElementsComponent(links: entry.links)
|
||||
],
|
||||
if (post.mediaAttachments.isNotEmpty) ...[
|
||||
if (entry.mediaAttachments.isNotEmpty) ...[
|
||||
const SizedBox(height: spacingHeight),
|
||||
MediaTimelineComponent(mediaAttachments: post.mediaAttachments)
|
||||
]
|
||||
MediaTimelineComponent(mediaAttachments: entry.mediaAttachments)
|
||||
],
|
||||
if (treeEntry.children.isNotEmpty)
|
||||
Column(
|
||||
children: treeEntry.children
|
||||
.map((e) => TreeEntryCard(treeEntry: e))
|
||||
.toList(),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
|
@ -0,0 +1,19 @@
|
|||
import 'package:friendica_archive_browser/src/friendica/models/friendica_timeline_entry.dart';
|
||||
|
||||
class FriendicaEntryTreeItem {
|
||||
final FriendicaTimelineEntry entry;
|
||||
final bool isOrphaned;
|
||||
|
||||
final _children = <String, FriendicaEntryTreeItem>{};
|
||||
|
||||
FriendicaEntryTreeItem(this.entry, this.isOrphaned);
|
||||
|
||||
String get id => entry.id;
|
||||
|
||||
void addChild(FriendicaEntryTreeItem child) {
|
||||
_children[child.id] = child;
|
||||
}
|
||||
|
||||
List<FriendicaEntryTreeItem> get children =>
|
||||
List.unmodifiable(_children.values);
|
||||
}
|
|
@ -11,6 +11,12 @@ import 'timeline_type.dart';
|
|||
class FriendicaTimelineEntry {
|
||||
static final _logger = Logger('$FriendicaTimelineEntry');
|
||||
|
||||
final String id;
|
||||
|
||||
final String parentId;
|
||||
|
||||
final String parentAuthor;
|
||||
|
||||
final int creationTimestamp;
|
||||
|
||||
final int backdatedTimestamp;
|
||||
|
@ -32,12 +38,15 @@ class FriendicaTimelineEntry {
|
|||
final TimelineType timelineType;
|
||||
|
||||
FriendicaTimelineEntry(
|
||||
{this.creationTimestamp = 0,
|
||||
{this.id = '',
|
||||
this.parentId = '',
|
||||
this.creationTimestamp = 0,
|
||||
this.backdatedTimestamp = 0,
|
||||
this.modificationTimestamp = 0,
|
||||
this.post = '',
|
||||
this.title = '',
|
||||
this.author = '',
|
||||
this.parentAuthor = '',
|
||||
this.locationData = const LocationData(),
|
||||
required this.timelineType,
|
||||
List<FriendicaMediaAttachment>? mediaAttachments,
|
||||
|
@ -49,9 +58,12 @@ class FriendicaTimelineEntry {
|
|||
: creationTimestamp = DateTime.now().millisecondsSinceEpoch,
|
||||
backdatedTimestamp = DateTime.now().millisecondsSinceEpoch,
|
||||
modificationTimestamp = DateTime.now().millisecondsSinceEpoch,
|
||||
id = randomId(),
|
||||
parentId = randomId(),
|
||||
post = 'Random post text ${randomId()}',
|
||||
title = 'Random title ${randomId()}',
|
||||
author = 'Random author ${randomId()}',
|
||||
parentAuthor = 'Random parent author ${randomId()}',
|
||||
locationData = LocationData.randomBuilt(),
|
||||
timelineType = TimelineType.active,
|
||||
links = [
|
||||
|
@ -67,9 +79,12 @@ class FriendicaTimelineEntry {
|
|||
{int? creationTimestamp,
|
||||
int? backdatedTimestamp,
|
||||
int? modificationTimestamp,
|
||||
String? id,
|
||||
String? parentId,
|
||||
String? post,
|
||||
String? title,
|
||||
String? author,
|
||||
String? parentAuthor,
|
||||
LocationData? locationData,
|
||||
List<FriendicaMediaAttachment>? mediaAttachments,
|
||||
TimelineType? timelineType,
|
||||
|
@ -79,9 +94,12 @@ class FriendicaTimelineEntry {
|
|||
backdatedTimestamp: backdatedTimestamp ?? this.backdatedTimestamp,
|
||||
modificationTimestamp:
|
||||
modificationTimestamp ?? this.modificationTimestamp,
|
||||
id: id ?? this.id,
|
||||
parentId: parentId ?? this.parentId,
|
||||
post: post ?? this.post,
|
||||
title: title ?? this.title,
|
||||
author: author ?? this.author,
|
||||
parentAuthor: parentAuthor ?? this.parentAuthor,
|
||||
locationData: locationData ?? this.locationData,
|
||||
mediaAttachments: mediaAttachments ?? this.mediaAttachments,
|
||||
timelineType: timelineType ?? this.timelineType,
|
||||
|
@ -90,7 +108,7 @@ class FriendicaTimelineEntry {
|
|||
|
||||
@override
|
||||
String toString() {
|
||||
return 'FacebookPost{creationTimestamp: $creationTimestamp, modificationTimestamp: $modificationTimestamp, backdatedTimeStamp: $backdatedTimestamp, timelineType: $timelineType, post: $post, title: $title, author: $author, mediaAttachments: $mediaAttachments, links: $links}';
|
||||
return 'FacebookPost{id: $id, parentId: $parentId, creationTimestamp: $creationTimestamp, modificationTimestamp: $modificationTimestamp, backdatedTimeStamp: $backdatedTimestamp, timelineType: $timelineType, post: $post, title: $title, author: $author, parentAuthor: $parentAuthor mediaAttachments: $mediaAttachments, links: $links}';
|
||||
}
|
||||
|
||||
String toHumanString(PathMappingService mapper, DateFormat formatter) {
|
||||
|
@ -104,6 +122,8 @@ class FriendicaTimelineEntry {
|
|||
'Author: $author',
|
||||
post,
|
||||
'',
|
||||
if (parentId.isEmpty)
|
||||
"Comment on post/comment by ${parentAuthor.isNotEmpty ? parentAuthor : 'unknown author'}",
|
||||
if (links.isNotEmpty) 'Links:',
|
||||
...links.map((e) => e.toString()),
|
||||
'',
|
||||
|
@ -135,6 +155,9 @@ class FriendicaTimelineEntry {
|
|||
return 0;
|
||||
})
|
||||
: 0;
|
||||
final id = json['id_str'] ?? '';
|
||||
final parentId = json['in_reply_to_status_id_str'] ?? '';
|
||||
final parentAuthor = json['in_reply_to_screen_name'] ?? '';
|
||||
final post = json['friendica_html'] ?? '';
|
||||
final author = json['user']['name'];
|
||||
final title = json['friendica_title'] ?? '';
|
||||
|
@ -152,7 +175,10 @@ class FriendicaTimelineEntry {
|
|||
backdatedTimestamp: backdatedTimestamp,
|
||||
locationData: actualLocationData,
|
||||
post: post,
|
||||
id: id,
|
||||
parentId: parentId,
|
||||
author: author,
|
||||
parentAuthor: parentAuthor,
|
||||
title: title,
|
||||
links: links,
|
||||
mediaAttachments: mediaAttachments,
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/components/filter_control_component.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/components/tree_entry_card.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/friendica_entry_tree_item.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/model_utils.dart';
|
||||
import 'package:friendica_archive_browser/src/screens/error_screen.dart';
|
||||
import 'package:friendica_archive_browser/src/utils/exec_error.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:result_monad/result_monad.dart';
|
||||
|
||||
import '../../screens/loading_status_screen.dart';
|
||||
import '../../screens/standin_status_screen.dart';
|
||||
|
||||
class EntriesScreen extends StatelessWidget {
|
||||
static final _logger = Logger('$EntriesScreen');
|
||||
final FutureResult<List<FriendicaEntryTreeItem>, ExecError> Function()
|
||||
populator;
|
||||
|
||||
const EntriesScreen({Key? key, required this.populator}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_logger.info('Build FriendicaEntriesScreen');
|
||||
|
||||
return FutureBuilder<Result<List<FriendicaEntryTreeItem>, ExecError>>(
|
||||
future: populator(),
|
||||
builder: (context, snapshot) {
|
||||
_logger.info('FriendicaEntriesScreen Future builder called');
|
||||
|
||||
if (!snapshot.hasData ||
|
||||
snapshot.connectionState != ConnectionState.done) {
|
||||
return const LoadingStatusScreen(title: 'Loading entries');
|
||||
}
|
||||
|
||||
final postsResult = snapshot.requireData;
|
||||
|
||||
if (postsResult.isFailure) {
|
||||
return ErrorScreen(
|
||||
title: 'Error getting entries', error: postsResult.error);
|
||||
}
|
||||
|
||||
final allPosts = postsResult.value;
|
||||
final posts = allPosts;
|
||||
|
||||
if (posts.isEmpty) {
|
||||
return const StandInStatusScreen(title: 'No entries were found');
|
||||
}
|
||||
|
||||
_logger.fine('Build Entries ListView');
|
||||
return _FriendicaEntriesScreenWidget(posts: posts);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class _FriendicaEntriesScreenWidget extends StatelessWidget {
|
||||
static final _logger = Logger('$_FriendicaEntriesScreenWidget');
|
||||
|
||||
final List<FriendicaEntryTreeItem> posts;
|
||||
|
||||
const _FriendicaEntriesScreenWidget({Key? key, required this.posts})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_logger.fine('Redrawing');
|
||||
return FilterControl<FriendicaEntryTreeItem, dynamic>(
|
||||
allItems: posts,
|
||||
imagesOnlyFilterFunction: (post) => post.entry.hasImages(),
|
||||
videosOnlyFilterFunction: (post) => post.entry.hasVideos(),
|
||||
textSearchFilterFunction: (post, text) =>
|
||||
post.entry.title.contains(text) || post.entry.post.contains(text),
|
||||
itemToDateTimeFunction: (post) => DateTime.fromMillisecondsSinceEpoch(
|
||||
post.entry.creationTimestamp * 1000),
|
||||
dateRangeFilterFunction: (post, start, stop) =>
|
||||
timestampInRange(post.entry.creationTimestamp * 1000, start, stop),
|
||||
builder: (context, items) {
|
||||
if (items.isEmpty) {
|
||||
return const StandInStatusScreen(
|
||||
title: 'No posts meet filter criteria');
|
||||
}
|
||||
|
||||
return ScrollConfiguration(
|
||||
behavior:
|
||||
ScrollConfiguration.of(context).copyWith(scrollbars: false),
|
||||
child: ListView.separated(
|
||||
primary: false,
|
||||
physics: const RangeMaintainingScrollPhysics(),
|
||||
restorationId: 'friendicaEntriesListView',
|
||||
itemCount: items.length,
|
||||
itemBuilder: (context, index) {
|
||||
_logger.finer('Rendering Friendica List Item');
|
||||
return TreeEntryCard(treeEntry: items[index]);
|
||||
},
|
||||
separatorBuilder: (context, index) {
|
||||
return const Divider(
|
||||
color: Colors.black,
|
||||
thickness: 0.2,
|
||||
);
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -5,7 +5,8 @@ import 'package:flutter/material.dart';
|
|||
import 'package:friendica_archive_browser/src/friendica/components/geo/geo_extensions.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/components/geo/map_bounds.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/components/geo/marker_data.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/components/post_card.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/components/tree_entry_card.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/friendica_entry_tree_item.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/friendica_timeline_entry.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/model_utils.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/services/facebook_archive_service.dart';
|
||||
|
@ -237,7 +238,8 @@ class _GeospatialViewState extends State<GeospatialView> {
|
|||
return ScrollConfiguration(
|
||||
behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false),
|
||||
child: ListView.separated(
|
||||
itemBuilder: (context, index) => PostCard(post: postsInList[index]),
|
||||
itemBuilder: (context, index) => TreeEntryCard(
|
||||
treeEntry: FriendicaEntryTreeItem(postsInList[index], false)),
|
||||
separatorBuilder: (context, index) => const Divider(height: 1),
|
||||
itemCount: postsInList.length),
|
||||
);
|
||||
|
|
|
@ -1,131 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/components/filter_control_component.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/components/post_card.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/friendica_timeline_entry.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/model_utils.dart';
|
||||
import 'package:friendica_archive_browser/src/screens/error_screen.dart';
|
||||
import 'package:friendica_archive_browser/src/services/friendica_archive_service.dart';
|
||||
import 'package:friendica_archive_browser/src/settings/settings_controller.dart';
|
||||
import 'package:friendica_archive_browser/src/utils/exec_error.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:result_monad/result_monad.dart';
|
||||
|
||||
import '../../screens/loading_status_screen.dart';
|
||||
import '../../screens/standin_status_screen.dart';
|
||||
|
||||
class PostsScreen extends StatelessWidget {
|
||||
static final _logger = Logger('$PostsScreen');
|
||||
|
||||
const PostsScreen({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_logger.info('Build FriendicaPostListView');
|
||||
final service = Provider.of<FriendicaArchiveService>(context);
|
||||
final username = Provider.of<SettingsController>(context).facebookName;
|
||||
|
||||
return FutureBuilder<Result<List<FriendicaTimelineEntry>, ExecError>>(
|
||||
future: service.getPosts(),
|
||||
builder: (context, snapshot) {
|
||||
_logger.info('FriendicaPostListView Future builder called');
|
||||
|
||||
if (!snapshot.hasData ||
|
||||
snapshot.connectionState != ConnectionState.done) {
|
||||
return const LoadingStatusScreen(title: 'Loading posts');
|
||||
}
|
||||
|
||||
final postsResult = snapshot.requireData;
|
||||
|
||||
if (postsResult.isFailure) {
|
||||
return ErrorScreen(
|
||||
title: 'Error getting posts', error: postsResult.error);
|
||||
}
|
||||
|
||||
final allPosts = postsResult.value;
|
||||
final posts = allPosts;
|
||||
// final filteredPosts = username.isEmpty
|
||||
// ? allPosts
|
||||
// : allPosts.where((p) =>
|
||||
// p.title != username ||
|
||||
// p.post.isNotEmpty ||
|
||||
// p.mediaAttachments.isNotEmpty ||
|
||||
// p.links.isNotEmpty);
|
||||
//
|
||||
// final posts = username.isEmpty
|
||||
// ? filteredPosts.toList()
|
||||
// : filteredPosts.map((p) {
|
||||
// var newTitle = p.title;
|
||||
// if (p.title == username) {
|
||||
// newTitle = 'You posted';
|
||||
// } else {
|
||||
// newTitle = p.title
|
||||
// .replaceAll(username, 'You')
|
||||
// .replaceAll(wholeWordRegEx('his'), 'your')
|
||||
// .replaceAll(wholeWordRegEx('her'), 'your');
|
||||
// }
|
||||
// if (newTitle == p.title) {
|
||||
// return p;
|
||||
// } else {
|
||||
// return p.copy(title: newTitle);
|
||||
// }
|
||||
// }).toList();
|
||||
if (posts.isEmpty) {
|
||||
return const StandInStatusScreen(title: 'No posts were found');
|
||||
}
|
||||
|
||||
_logger.fine('Build Posts ListView');
|
||||
return _FriendicaPostsScreenWidget(posts: posts);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class _FriendicaPostsScreenWidget extends StatelessWidget {
|
||||
static final _logger = Logger('$_FriendicaPostsScreenWidget');
|
||||
|
||||
final List<FriendicaTimelineEntry> posts;
|
||||
|
||||
const _FriendicaPostsScreenWidget({Key? key, required this.posts})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_logger.fine('Redrawing');
|
||||
return FilterControl<FriendicaTimelineEntry, dynamic>(
|
||||
allItems: posts,
|
||||
imagesOnlyFilterFunction: (post) => post.hasImages(),
|
||||
videosOnlyFilterFunction: (post) => post.hasVideos(),
|
||||
textSearchFilterFunction: (post, text) =>
|
||||
post.title.contains(text) || post.post.contains(text),
|
||||
itemToDateTimeFunction: (post) =>
|
||||
DateTime.fromMillisecondsSinceEpoch(post.creationTimestamp * 1000),
|
||||
dateRangeFilterFunction: (post, start, stop) =>
|
||||
timestampInRange(post.creationTimestamp * 1000, start, stop),
|
||||
builder: (context, items) {
|
||||
if (items.isEmpty) {
|
||||
return const StandInStatusScreen(
|
||||
title: 'No posts meet filter criteria');
|
||||
}
|
||||
|
||||
return ScrollConfiguration(
|
||||
behavior:
|
||||
ScrollConfiguration.of(context).copyWith(scrollbars: false),
|
||||
child: ListView.separated(
|
||||
primary: false,
|
||||
physics: const RangeMaintainingScrollPhysics(),
|
||||
restorationId: 'friendicaPostsListView',
|
||||
itemCount: items.length,
|
||||
itemBuilder: (context, index) {
|
||||
_logger.finer('Rendering Friendica List Item');
|
||||
return PostCard(post: items[index]);
|
||||
},
|
||||
separatorBuilder: (context, index) {
|
||||
return const Divider(
|
||||
color: Colors.black,
|
||||
thickness: 0.2,
|
||||
);
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:friendica_archive_browser/src/services/friendica_archive_service.dart';
|
||||
|
||||
import 'friendica/screens/photo_album_browser_screen.dart';
|
||||
import 'friendica/screens/posts_screen.dart';
|
||||
import 'friendica/screens/entries_screen.dart';
|
||||
import 'friendica/screens/stats_screen.dart';
|
||||
import 'friendica/services/facebook_archive_reader.dart';
|
||||
import 'settings/settings_controller.dart';
|
||||
|
@ -9,8 +9,13 @@ import 'settings/settings_view.dart';
|
|||
|
||||
class Home extends StatefulWidget {
|
||||
final SettingsController settingsController;
|
||||
final FriendicaArchiveService archiveService;
|
||||
|
||||
const Home({Key? key, required this.settingsController}) : super(key: key);
|
||||
const Home(
|
||||
{Key? key,
|
||||
required this.settingsController,
|
||||
required this.archiveService})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
_HomeState createState() => _HomeState();
|
||||
|
@ -25,9 +30,18 @@ class _HomeState extends State<Home> {
|
|||
@override
|
||||
void initState() {
|
||||
_pageData.addAll([
|
||||
AppPageData('Posts', Icons.home, () => const PostsScreen()),
|
||||
AppPageData('Photos', Icons.photo_library,
|
||||
() => const PhotoAlbumsBrowserScreen()),
|
||||
AppPageData(
|
||||
'Posts',
|
||||
Icons.home,
|
||||
() => EntriesScreen(
|
||||
populator: widget.archiveService.getPosts,
|
||||
)),
|
||||
AppPageData(
|
||||
'Orphan\nComments',
|
||||
Icons.comment,
|
||||
() => EntriesScreen(
|
||||
populator: widget.archiveService.getOrphanedComments,
|
||||
)),
|
||||
AppPageData('Stats', Icons.bar_chart, () => const StatsScreen()),
|
||||
AppPageData('Settings', Icons.settings, () => _buildSettingsView()),
|
||||
]);
|
||||
|
@ -114,6 +128,10 @@ class AppPageData {
|
|||
|
||||
AppPageData(this.label, this.icon, widgetBuilder)
|
||||
: _widgetBuilder = widgetBuilder,
|
||||
navRailDestination =
|
||||
NavigationRailDestination(icon: Icon(icon), label: Text(label));
|
||||
navRailDestination = NavigationRailDestination(
|
||||
icon: Icon(icon),
|
||||
label: Text(
|
||||
label,
|
||||
textAlign: TextAlign.center,
|
||||
));
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:convert';
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/friendica_entry_tree_item.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/friendica_timeline_entry.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/timeline_type.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/services/path_mapping_service.dart';
|
||||
|
@ -13,21 +14,32 @@ import 'package:result_monad/result_monad.dart';
|
|||
class FriendicaArchiveService extends ChangeNotifier {
|
||||
final PathMappingService pathMappingService;
|
||||
final Map<String, ImageEntry> _imagesByRequestUrl = {};
|
||||
final List<FriendicaTimelineEntry> _posts = [];
|
||||
final List<FriendicaEntryTreeItem> _postEntries = [];
|
||||
final List<FriendicaEntryTreeItem> _orphanedCommentEntries = [];
|
||||
|
||||
FriendicaArchiveService({required this.pathMappingService});
|
||||
|
||||
void clearCaches() {
|
||||
_imagesByRequestUrl.clear();
|
||||
_posts.clear();
|
||||
_orphanedCommentEntries.clear();
|
||||
_postEntries.clear();
|
||||
}
|
||||
|
||||
FutureResult<List<FriendicaTimelineEntry>, ExecError> getPosts() async {
|
||||
if (_posts.isEmpty) {
|
||||
_loadPosts();
|
||||
FutureResult<List<FriendicaEntryTreeItem>, ExecError> getPosts() async {
|
||||
if (_postEntries.isEmpty && _orphanedCommentEntries.isEmpty) {
|
||||
_loadEntries();
|
||||
}
|
||||
|
||||
return Result.ok(_posts);
|
||||
return Result.ok(_postEntries);
|
||||
}
|
||||
|
||||
FutureResult<List<FriendicaEntryTreeItem>, ExecError>
|
||||
getOrphanedComments() async {
|
||||
if (_postEntries.isEmpty && _orphanedCommentEntries.isEmpty) {
|
||||
_loadEntries();
|
||||
}
|
||||
|
||||
return Result.ok(_orphanedCommentEntries);
|
||||
}
|
||||
|
||||
Result<ImageEntry, ExecError> getImageByUrl(String url) {
|
||||
|
@ -43,19 +55,43 @@ class FriendicaArchiveService extends ChangeNotifier {
|
|||
|
||||
String get _baseArchiveFolder => pathMappingService.rootFolder;
|
||||
|
||||
void _loadPosts() {
|
||||
void _loadEntries() {
|
||||
final entriesJsonPath = p.join(_baseArchiveFolder, 'postsAndComments.json');
|
||||
final jsonFile = File(entriesJsonPath);
|
||||
if (jsonFile.existsSync()) {
|
||||
final json = jsonDecode(jsonFile.readAsStringSync()) as List<dynamic>;
|
||||
final postEntries = json
|
||||
final entries = json
|
||||
.map((j) => FriendicaTimelineEntry.fromJson(j, TimelineType.active));
|
||||
_posts.clear();
|
||||
_posts.addAll(postEntries);
|
||||
}
|
||||
final topLevelEntries =
|
||||
entries.where((element) => element.parentId.isEmpty);
|
||||
final ids = entries.map((e) => e.id).toSet();
|
||||
final commentEntries =
|
||||
entries.where((element) => element.parentId.isNotEmpty).toList();
|
||||
final entryTrees = <String, FriendicaEntryTreeItem>{};
|
||||
for (final entry in topLevelEntries) {
|
||||
entryTrees[entry.id] = FriendicaEntryTreeItem(entry, false);
|
||||
}
|
||||
|
||||
final uniqueUsers = _posts.map((e) => e.author).toSet();
|
||||
print(uniqueUsers);
|
||||
final commentsWithParents = commentEntries
|
||||
.where((element) => ids.contains(element.parentId))
|
||||
.toList();
|
||||
print(commentsWithParents.length);
|
||||
commentEntries.sort(
|
||||
(c1, c2) => c1.creationTimestamp.compareTo(c2.creationTimestamp));
|
||||
for (final entry in commentEntries) {
|
||||
final parent = entryTrees[entry.parentId];
|
||||
final treeEntry = FriendicaEntryTreeItem(entry, parent == null);
|
||||
parent?.addChild(treeEntry);
|
||||
entryTrees[entry.id] = treeEntry;
|
||||
}
|
||||
|
||||
_postEntries.clear();
|
||||
_postEntries
|
||||
.addAll(entryTrees.values.where((element) => !element.isOrphaned));
|
||||
_orphanedCommentEntries.clear();
|
||||
_orphanedCommentEntries
|
||||
.addAll(entryTrees.values.where((element) => element.isOrphaned));
|
||||
}
|
||||
}
|
||||
|
||||
void _loadImages() {
|
||||
|
|
Loading…
Reference in a new issue