mirror of
https://gitlab.com/mysocialportal/fediverse-archiving-tools.git
synced 2024-10-18 08:53:31 +00:00
Remove unneeded screens and types
This commit is contained in:
parent
9bf45e42ba
commit
3547537619
13 changed files with 3 additions and 1262 deletions
|
@ -1,86 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/facebook_messenger_message.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/services/path_mapping_service.dart';
|
||||
import 'package:friendica_archive_browser/src/settings/settings_controller.dart';
|
||||
import 'package:friendica_archive_browser/src/utils/clipboard_helper.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'facebook_link_elements_component.dart';
|
||||
import 'facebook_media_timeline_component.dart';
|
||||
import 'facebook_media_wrapper_component.dart';
|
||||
|
||||
class ConversationMessageCard extends StatelessWidget {
|
||||
final FacebookMessengerMessage message;
|
||||
|
||||
const ConversationMessageCard({Key? key, required this.message})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (Scrollable.recommendDeferredLoadingForContext(context)) {
|
||||
return const SizedBox();
|
||||
}
|
||||
|
||||
const double spacingHeight = 5.0;
|
||||
const double stickerSize = 64.0;
|
||||
final settings = Provider.of<SettingsController>(context);
|
||||
final formatter = settings.dateTimeFormatter;
|
||||
final mapper = Provider.of<PathMappingService>(context);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Tooltip(
|
||||
message: formatter
|
||||
.format(DateTime.fromMillisecondsSinceEpoch(message.timestampMS)),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Tooltip(
|
||||
message: 'Copy text version of line to clipboard',
|
||||
child: IconButton(
|
||||
onPressed: () async => await copyToClipboard(
|
||||
context: context,
|
||||
text: message.toHumanString(mapper, formatter),
|
||||
snackbarMessage:
|
||||
'Copied Messenger line to clipboard'),
|
||||
icon: const Icon(Icons.copy)),
|
||||
),
|
||||
Text('${message.from}: ',
|
||||
style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||
Expanded(
|
||||
child: Text(
|
||||
message.message,
|
||||
)),
|
||||
]),
|
||||
if (message.media.isNotEmpty) ...[
|
||||
const SizedBox(height: spacingHeight),
|
||||
FacebookMediaTimelineComponent(mediaAttachments: message.media)
|
||||
],
|
||||
if (message.stickers.isNotEmpty) ...[
|
||||
const SizedBox(height: spacingHeight),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: message.stickers
|
||||
.map((s) => FacebookMediaWrapperComponent(
|
||||
mediaAttachment: s,
|
||||
preferredWidth: stickerSize,
|
||||
preferredHeight: stickerSize,
|
||||
))
|
||||
.toList(),
|
||||
)
|
||||
],
|
||||
if (message.links.isNotEmpty) ...[
|
||||
const SizedBox(height: spacingHeight),
|
||||
FacebookLinkElementsComponent(links: message.links)
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/facebook_event.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/facebook_location_data.dart';
|
||||
import 'package:friendica_archive_browser/src/settings/settings_controller.dart';
|
||||
import 'package:friendica_archive_browser/src/utils/clipboard_helper.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class EventCard extends StatelessWidget {
|
||||
final FacebookEvent event;
|
||||
|
||||
const EventCard({Key? key, required this.event}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const double spacingHeight = 5.0;
|
||||
final formatter =
|
||||
Provider.of<SettingsController>(context).dateTimeFormatter;
|
||||
final copyButton = Tooltip(
|
||||
message: 'Copy text version of event to clipboard',
|
||||
child: IconButton(
|
||||
onPressed: () async => await copyToClipboard(
|
||||
context: context,
|
||||
text: event.toHumanString(formatter),
|
||||
snackbarMessage: 'Copied Event to clipboard'),
|
||||
icon: const Icon(Icons.copy)));
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Wrap(
|
||||
direction: Axis.horizontal,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
event.name,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
copyButton,
|
||||
],
|
||||
),
|
||||
if (event.description.isNotEmpty) ...[
|
||||
const SizedBox(height: spacingHeight),
|
||||
Text(event.description)
|
||||
],
|
||||
_buildStatusLine('You are:', _eventStatusToString(event.eventStatus)),
|
||||
const SizedBox(height: spacingHeight),
|
||||
_buildStatusLine(
|
||||
'Starts: ',
|
||||
formatter.format(DateTime.fromMillisecondsSinceEpoch(
|
||||
event.startTimestamp * 1000))),
|
||||
if (event.endTimestamp != 0) ...[
|
||||
const SizedBox(height: spacingHeight),
|
||||
_buildStatusLine(
|
||||
'Stops: ',
|
||||
formatter.format(DateTime.fromMillisecondsSinceEpoch(
|
||||
event.endTimestamp * 1000))),
|
||||
],
|
||||
const SizedBox(height: spacingHeight),
|
||||
if (event.location.hasData()) event.location.toWidget(spacingHeight),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStatusLine(String label, String status) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(width: 2),
|
||||
Text(status),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
String _eventStatusToString(FacebookEventStatus status) {
|
||||
switch (status) {
|
||||
case FacebookEventStatus.declined:
|
||||
return 'Declined';
|
||||
case FacebookEventStatus.interested:
|
||||
return 'Interested';
|
||||
case FacebookEventStatus.invited:
|
||||
return 'Invited';
|
||||
case FacebookEventStatus.joined:
|
||||
return 'Joined';
|
||||
case FacebookEventStatus.owner:
|
||||
return 'Owner';
|
||||
case FacebookEventStatus.unknown:
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/facebook_messenger_conversation.dart';
|
||||
import 'package:friendica_archive_browser/src/screens/standin_status_screen.dart';
|
||||
|
||||
class FacebookConversationHistoryComponent extends StatefulWidget {
|
||||
static final FacebookMessengerConversation noConversationSelected =
|
||||
FacebookMessengerConversation.empty();
|
||||
|
||||
final FacebookMessengerConversation conversation;
|
||||
|
||||
const FacebookConversationHistoryComponent(
|
||||
{Key? key, required this.conversation})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<FacebookConversationHistoryComponent> createState() =>
|
||||
_FacebookConversationHistoryComponentState();
|
||||
}
|
||||
|
||||
class _FacebookConversationHistoryComponentState
|
||||
extends State<FacebookConversationHistoryComponent> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (widget.conversation ==
|
||||
FacebookConversationHistoryComponent.noConversationSelected) {
|
||||
return const StandInStatusScreen(
|
||||
title: 'No conversation selected',
|
||||
subTitle: 'Select a conversation to display here',
|
||||
);
|
||||
}
|
||||
|
||||
return ListView.separated(
|
||||
primary: false,
|
||||
restorationId: 'facebookConversationPane',
|
||||
itemCount: widget.conversation.messages.length,
|
||||
itemBuilder: (context, index) {
|
||||
final message = widget.conversation.messages[index];
|
||||
return Text(
|
||||
'${message.from}: ${message.message}',
|
||||
softWrap: true,
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) {
|
||||
return const Divider(
|
||||
color: Colors.black,
|
||||
thickness: 0.2,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
import 'package:intl/intl.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
import 'facebook_location_data.dart';
|
||||
import 'model_utils.dart';
|
||||
|
||||
enum FacebookEventStatus {
|
||||
declined,
|
||||
interested,
|
||||
invited,
|
||||
joined,
|
||||
owner,
|
||||
unknown,
|
||||
}
|
||||
|
||||
class FacebookEvent {
|
||||
static final _logger = Logger('$FacebookEvent');
|
||||
|
||||
final String name;
|
||||
final String description;
|
||||
final int creationTimestamp;
|
||||
final int startTimestamp;
|
||||
final int endTimestamp;
|
||||
final FacebookLocationData location;
|
||||
final FacebookEventStatus eventStatus;
|
||||
|
||||
FacebookEvent(
|
||||
{required this.name,
|
||||
required this.description,
|
||||
required this.creationTimestamp,
|
||||
required this.startTimestamp,
|
||||
required this.endTimestamp,
|
||||
required this.location,
|
||||
required this.eventStatus});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'FacebookEvent{name: $name, description: $description, creationTimestamp: $creationTimestamp, startTimestamp: $startTimestamp, endTimestamp: $endTimestamp, location: $location, eventStatus: $eventStatus}';
|
||||
}
|
||||
|
||||
String toHumanString(DateFormat formatter) {
|
||||
final creationDateString = formatter.format(
|
||||
DateTime.fromMillisecondsSinceEpoch(creationTimestamp * 1000)
|
||||
.toLocal());
|
||||
final startTimeString = formatter.format(
|
||||
DateTime.fromMillisecondsSinceEpoch(startTimestamp * 1000).toLocal());
|
||||
final endTimeString = formatter.format(
|
||||
DateTime.fromMillisecondsSinceEpoch(endTimestamp * 1000).toLocal());
|
||||
return [
|
||||
if (name.isNotEmpty) 'Name: $name',
|
||||
if (description.isNotEmpty) 'Description:\n$description',
|
||||
'Creation At: $creationDateString',
|
||||
if (startTimestamp != 0) 'Start Time: $startTimeString',
|
||||
if (endTimestamp != 0) 'End Time: $endTimeString',
|
||||
'Your Status: $eventStatus',
|
||||
if (location.hasPosition) location.toHumanString(),
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
static FacebookEvent fromJson(Map<String, dynamic> json,
|
||||
{FacebookEventStatus statusType = FacebookEventStatus.unknown}) {
|
||||
final knownTopLevelKeys = [
|
||||
'name',
|
||||
'start_timestamp',
|
||||
'end_timestamp',
|
||||
'place',
|
||||
'description',
|
||||
'create_timestamp'
|
||||
];
|
||||
|
||||
logAdditionalKeys(knownTopLevelKeys, json.keys, _logger, Level.WARNING,
|
||||
'Unknown top level event keys');
|
||||
|
||||
final name = json['name'] ?? '';
|
||||
final description = json['description'] ?? '';
|
||||
final int creationTimestamp = json['create_timestamp'] ?? 0;
|
||||
final int startTimestamp = json['start_timestamp'] ?? 0;
|
||||
final int endTimestamp = json['end_timestamp'] ?? 0;
|
||||
final FacebookLocationData location = json.containsKey('place')
|
||||
? FacebookLocationData.fromJson(json['place'])
|
||||
: const FacebookLocationData();
|
||||
|
||||
return FacebookEvent(
|
||||
name: name,
|
||||
description: description,
|
||||
creationTimestamp: creationTimestamp,
|
||||
startTimestamp: startTimestamp,
|
||||
endTimestamp: endTimestamp,
|
||||
location: location,
|
||||
eventStatus: statusType);
|
||||
}
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
import 'package:logging/logging.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
import 'facebook_messenger_message.dart';
|
||||
import 'model_utils.dart';
|
||||
|
||||
class Copy<T> {
|
||||
T? copy() => null;
|
||||
}
|
||||
|
||||
class FacebookMessengerConversation with Copy<FacebookMessengerConversation> {
|
||||
static final _logger = Logger('$FacebookMessengerConversation');
|
||||
|
||||
final String id;
|
||||
final Set<String> participants;
|
||||
final List<FacebookMessengerMessage> messages;
|
||||
final String title;
|
||||
|
||||
FacebookMessengerConversation(
|
||||
{required this.id,
|
||||
required this.participants,
|
||||
required this.messages,
|
||||
required this.title});
|
||||
|
||||
factory FacebookMessengerConversation.empty() =>
|
||||
FacebookMessengerConversation(
|
||||
id: '', participants: {}, messages: [], title: '');
|
||||
|
||||
@override
|
||||
FacebookMessengerConversation copy() => FacebookMessengerConversation(
|
||||
id: id,
|
||||
participants: {...participants},
|
||||
messages: [...messages],
|
||||
title: title);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'FacebookMessengerConversation{participants: $participants, messages: $messages, title: $title}';
|
||||
}
|
||||
|
||||
int earliestTimestampMS() => messages.isEmpty ? 0 : messages.last.timestampMS;
|
||||
|
||||
int latestTimestampMS() => messages.isEmpty ? 0 : messages.first.timestampMS;
|
||||
|
||||
bool hasImages() => messages.where((m) => m.hasImages()).isNotEmpty;
|
||||
|
||||
bool hasVideos() => messages.where((m) => m.hasVideos()).isNotEmpty;
|
||||
|
||||
FacebookMessengerConversation.fromJson(Map<String, dynamic> json)
|
||||
: id = json['id'] ?? '',
|
||||
participants = {...json['participants'] as List<dynamic>? ?? []},
|
||||
messages = (json['messages'] as List<dynamic>? ?? [])
|
||||
.map((j) => FacebookMessengerMessage.fromJson(j))
|
||||
.toList(),
|
||||
title = json['title'] ?? '';
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'id': id,
|
||||
'participants': participants.toList(),
|
||||
'messages': messages.map((m) => m.toJson()).toList(),
|
||||
'title': title,
|
||||
};
|
||||
|
||||
static FacebookMessengerConversation fromFacebookJson(
|
||||
Map<String, dynamic> json) {
|
||||
final id = json['thread_path'] ?? const Uuid().v4();
|
||||
const knownTopLevelKeys = [
|
||||
'participants',
|
||||
'messages',
|
||||
'title',
|
||||
'is_still_participant',
|
||||
'thread_type',
|
||||
'thread_path',
|
||||
'magic_words',
|
||||
];
|
||||
|
||||
logAdditionalKeys(knownTopLevelKeys, json.keys, _logger, Level.WARNING,
|
||||
'Unknown top level conversation keys: ');
|
||||
|
||||
final title = json['title'] ?? '';
|
||||
final participants = <String>{};
|
||||
final messages = <FacebookMessengerMessage>[];
|
||||
|
||||
for (Map<String, dynamic> p in json['messages'] ?? <Map, dynamic>{}) {
|
||||
messages.add(FacebookMessengerMessage.fromFacebookJson(p));
|
||||
}
|
||||
|
||||
for (Map<String, dynamic> p in json['participants'] ?? <Map, dynamic>{}) {
|
||||
participants.add(p['name'] ?? '');
|
||||
}
|
||||
|
||||
return FacebookMessengerConversation(
|
||||
id: id, participants: participants, messages: messages, title: title);
|
||||
}
|
||||
}
|
|
@ -1,179 +0,0 @@
|
|||
import 'package:friendica_archive_browser/src/friendica/services/path_mapping_service.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
import 'facebook_media_attachment.dart';
|
||||
import 'model_utils.dart';
|
||||
|
||||
class FacebookMessengerMessage {
|
||||
static final _logger = Logger('$FacebookMessengerMessage');
|
||||
|
||||
final String from;
|
||||
final String message;
|
||||
final int timestampMS;
|
||||
final List<FacebookMediaAttachment> media;
|
||||
final List<FacebookMediaAttachment> stickers;
|
||||
final List<Uri> links;
|
||||
final Map<String, String> reactions;
|
||||
|
||||
FacebookMessengerMessage(
|
||||
{required this.from,
|
||||
required this.message,
|
||||
required this.timestampMS,
|
||||
this.media = const [],
|
||||
this.stickers = const [],
|
||||
this.links = const [],
|
||||
this.reactions = const {}});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'FacebookMessengerMessage{from: $from, message: $message, timestampMS: $timestampMS, media: $media, stickers: $stickers, links: $links, reactions: $reactions}';
|
||||
}
|
||||
|
||||
String toHumanString(PathMappingService mapper, DateFormat formatter) {
|
||||
final creationDateString = formatter
|
||||
.format(DateTime.fromMillisecondsSinceEpoch(timestampMS).toLocal());
|
||||
return [
|
||||
'Creation At: $creationDateString',
|
||||
if (message.isNotEmpty) 'Message: $message',
|
||||
'',
|
||||
if (links.isNotEmpty) 'Links:',
|
||||
...links.map((e) => e.toString()),
|
||||
'',
|
||||
if (stickers.isNotEmpty) 'Stickers:',
|
||||
...stickers.map((e) => e.toHumanString(mapper)),
|
||||
if (media.isNotEmpty) 'Media:',
|
||||
...media.map((e) => e.toHumanString(mapper)),
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
FacebookMessengerMessage copy(
|
||||
{String? from,
|
||||
String? message,
|
||||
int? timestampMS,
|
||||
List<FacebookMediaAttachment>? media,
|
||||
List<FacebookMediaAttachment>? stickers,
|
||||
List<Uri>? links,
|
||||
Map<String, String>? reactions}) {
|
||||
return FacebookMessengerMessage(
|
||||
from: from ?? this.from,
|
||||
message: message ?? this.message,
|
||||
timestampMS: timestampMS ?? this.timestampMS,
|
||||
media: media ?? this.media,
|
||||
stickers: stickers ?? this.stickers,
|
||||
links: links ?? this.links,
|
||||
reactions: reactions ?? this.reactions,
|
||||
);
|
||||
}
|
||||
|
||||
FacebookMessengerMessage.fromJson(Map<String, dynamic> json)
|
||||
: from = json['from'] ?? '',
|
||||
message = json['message'] ?? '',
|
||||
timestampMS = json['timestampMS'] ?? '',
|
||||
media = (json['media'] as List<dynamic>? ?? [])
|
||||
.map((j) => FacebookMediaAttachment.fromJson(j))
|
||||
.toList(),
|
||||
stickers = (json['stickers'] as List<dynamic>? ?? [])
|
||||
.map((j) => FacebookMediaAttachment.fromJson(j))
|
||||
.toList(),
|
||||
links = (json['links'] as List<dynamic>? ?? [])
|
||||
.map((j) => Uri.parse(j))
|
||||
.toList(),
|
||||
reactions = (json['reactions'] as Map<String, dynamic>? ?? {})
|
||||
.map((key, value) => MapEntry(key, value.toString()));
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'from': from,
|
||||
'message': message,
|
||||
'timestampMS': timestampMS,
|
||||
'media': media.map((m) => m.toJson()).toList(),
|
||||
'stickers': stickers.map((m) => m.toJson()).toList(),
|
||||
'links': links.map((e) => e.toString()).toList(),
|
||||
'reactions': reactions,
|
||||
};
|
||||
|
||||
bool hasImages() => media
|
||||
.where((element) =>
|
||||
element.estimatedType() == FacebookAttachmentMediaType.image)
|
||||
.isNotEmpty;
|
||||
|
||||
bool hasVideos() => media
|
||||
.where((element) =>
|
||||
element.estimatedType() == FacebookAttachmentMediaType.video)
|
||||
.isNotEmpty;
|
||||
|
||||
static FacebookMessengerMessage fromFacebookJson(Map<String, dynamic> json) {
|
||||
const knownTopLevelKeys = [
|
||||
'sender_name',
|
||||
'timestamp_ms',
|
||||
'photos',
|
||||
'reactions',
|
||||
'gifs',
|
||||
'content',
|
||||
'type',
|
||||
'share',
|
||||
'videos',
|
||||
'users',
|
||||
'sticker',
|
||||
'files',
|
||||
'call_duration',
|
||||
'missed',
|
||||
'audio_files',
|
||||
'is_unsent',
|
||||
'ip',
|
||||
];
|
||||
|
||||
logAdditionalKeys(knownTopLevelKeys, json.keys, _logger, Level.WARNING,
|
||||
'Unknown top level message keys: ');
|
||||
|
||||
final from = json['sender_name'] ?? '';
|
||||
final timestamp = json['timestamp_ms'] ?? 0;
|
||||
final message = json['content'] ?? '';
|
||||
final type = json['Generic'] ?? 'Generic';
|
||||
if (!['Generic', 'Share'].contains(type)) {
|
||||
_logger.severe("New message type: $type");
|
||||
}
|
||||
|
||||
final links = <Uri>[];
|
||||
final String linkString = json['share']?['link'] ?? '';
|
||||
if (linkString.isNotEmpty) {
|
||||
links.add(Uri.parse(linkString));
|
||||
}
|
||||
|
||||
// TODO Add Reactions
|
||||
List<FacebookMediaAttachment> mediaAttachments = [];
|
||||
for (Map<String, dynamic> photo in json['photos'] ?? []) {
|
||||
final media = FacebookMediaAttachment.fromFacebookJson(photo);
|
||||
mediaAttachments.add(media);
|
||||
}
|
||||
|
||||
for (Map<String, dynamic> video in json['videos'] ?? []) {
|
||||
final media = FacebookMediaAttachment.fromFacebookJson(video);
|
||||
mediaAttachments.add(media);
|
||||
}
|
||||
|
||||
for (Map<String, dynamic> audioFile in json['audio_files'] ?? []) {
|
||||
final path = audioFile['uri'];
|
||||
links.add(Uri.file(path));
|
||||
}
|
||||
|
||||
for (Map<String, dynamic> gif in json['gifs'] ?? []) {
|
||||
final media = FacebookMediaAttachment.fromFacebookJson(gif);
|
||||
mediaAttachments.add(media);
|
||||
}
|
||||
|
||||
final stickers = <FacebookMediaAttachment>[];
|
||||
final String path = json['sticker']?['uri'] ?? '';
|
||||
if (path.isNotEmpty) {
|
||||
stickers.add(FacebookMediaAttachment.fromUriOnly(Uri.file(path)));
|
||||
}
|
||||
|
||||
return FacebookMessengerMessage(
|
||||
from: from,
|
||||
message: message,
|
||||
timestampMS: timestamp,
|
||||
media: mediaAttachments,
|
||||
stickers: stickers,
|
||||
links: links);
|
||||
}
|
||||
}
|
|
@ -1,232 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/components/conversation_message_card.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/components/filter_control_component.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/facebook_messenger_conversation.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/facebook_messenger_message.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/model_utils.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/services/facebook_archive_service.dart';
|
||||
import 'package:friendica_archive_browser/src/screens/error_screen.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 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||
|
||||
import '../../screens/loading_status_screen.dart';
|
||||
import '../../screens/standin_status_screen.dart';
|
||||
|
||||
class FacebookConversationScreen extends StatelessWidget {
|
||||
static final _logger = Logger('$FacebookConversationScreen');
|
||||
|
||||
const FacebookConversationScreen({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final service = Provider.of<FacebookArchiveDataService>(context);
|
||||
_logger.info('Build Facebook Conversation Screen');
|
||||
|
||||
return FutureBuilder<
|
||||
Result<List<FacebookMessengerConversation>, ExecError>>(
|
||||
future: service.getConvos(),
|
||||
builder: (context, snapshot) {
|
||||
_logger.fine('Future Conversation builder called');
|
||||
|
||||
if (!snapshot.hasData ||
|
||||
snapshot.connectionState != ConnectionState.done) {
|
||||
_logger.finer('No data yet, just return status screen');
|
||||
return const LoadingStatusScreen(
|
||||
title: 'Loading Conversations',
|
||||
subTitle:
|
||||
'This can take several minutes the first time loading the archive.',
|
||||
);
|
||||
}
|
||||
|
||||
final convoResult = snapshot.requireData;
|
||||
if (convoResult.isFailure) {
|
||||
return ErrorScreen(error: convoResult.error);
|
||||
}
|
||||
|
||||
_logger.finer(
|
||||
'Now have data! ${snapshot.requireData.value.length} conversations');
|
||||
|
||||
final conversations = convoResult.value;
|
||||
|
||||
if (conversations.isEmpty) {
|
||||
return const StandInStatusScreen(
|
||||
title: 'No conversations were found');
|
||||
}
|
||||
|
||||
return _FacebookConversionsFilteredWidget(
|
||||
conversations: conversations);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class _FacebookConversionsFilteredWidget extends StatelessWidget {
|
||||
final List<FacebookMessengerConversation> conversations;
|
||||
|
||||
const _FacebookConversionsFilteredWidget(
|
||||
{Key? key, required this.conversations})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FilterControl<FacebookMessengerConversation,
|
||||
FacebookMessengerMessage>(
|
||||
allItems: conversations,
|
||||
imagesOnlyFilterFunction: (convo) => convo.hasImages(),
|
||||
videosOnlyFilterFunction: (convo) => convo.hasVideos(),
|
||||
textSearchFilterFunction: (convo, text) =>
|
||||
convo.title.contains(text) ||
|
||||
convo.messages
|
||||
.map((e) => e.message)
|
||||
.where((element) => element.contains(text))
|
||||
.isNotEmpty,
|
||||
itemToDateTimeFunction: (convo) =>
|
||||
DateTime.fromMillisecondsSinceEpoch(convo.latestTimestampMS()),
|
||||
dateRangeFilterFunction: (convo, start, stop) =>
|
||||
timestampInRange(convo.earliestTimestampMS(), start, stop) ||
|
||||
timestampInRange(convo.latestTimestampMS(), start, stop),
|
||||
getSecondary: (convo) => convo.messages,
|
||||
copyPrimary: (convo) => convo.copy(),
|
||||
secondaryItemToDateTimeFunction: (message) =>
|
||||
DateTime.fromMillisecondsSinceEpoch(message.timestampMS),
|
||||
secondaryDateRangeFilterFunction: (message, start, stop) =>
|
||||
timestampInRange(message.timestampMS, start, stop),
|
||||
secondaryImagesOnlyFilterFunction: (message) =>
|
||||
message.hasImages() || message.stickers.isNotEmpty,
|
||||
secondaryVideosOnlyFilterFunction: (message) => message.hasVideos(),
|
||||
secondaryTextSearchFilterFunction: (message, text) =>
|
||||
message.message.contains(text),
|
||||
builder: (context, conversations) {
|
||||
return _FacebookConversationsScreenWidget(
|
||||
conversations: conversations);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class _FacebookConversationsScreenWidget extends StatefulWidget {
|
||||
final List<FacebookMessengerConversation> conversations;
|
||||
|
||||
const _FacebookConversationsScreenWidget(
|
||||
{Key? key, required this.conversations})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<_FacebookConversationsScreenWidget> createState() =>
|
||||
_FacebookConversationsScreenWidgetState();
|
||||
}
|
||||
|
||||
class _FacebookConversationsScreenWidgetState
|
||||
extends State<_FacebookConversationsScreenWidget> {
|
||||
static final _logger = Logger('$_FacebookConversationsScreenWidget');
|
||||
|
||||
static final FacebookMessengerConversation noConversationSelected =
|
||||
FacebookMessengerConversation.empty();
|
||||
FacebookMessengerConversation _currentConversation = noConversationSelected;
|
||||
final ItemScrollController _scrollController = ItemScrollController();
|
||||
|
||||
_setConversation(int index) {
|
||||
if (index > widget.conversations.length) {
|
||||
_logger.severe(
|
||||
'Requested participants index greater then max: $index > ${widget.conversations.length}');
|
||||
return;
|
||||
}
|
||||
|
||||
final conversation =
|
||||
index < 0 ? noConversationSelected : widget.conversations[index];
|
||||
if (conversation == _currentConversation) {
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.finer('Jumping to $index');
|
||||
final scrollToIndex = index > 0 ? index - 1 : 0;
|
||||
_scrollController.scrollTo(
|
||||
index: scrollToIndex, duration: const Duration(seconds: 1));
|
||||
setState(() {
|
||||
_currentConversation = conversation;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_logger.fine('Build _FacebookConversationsScreenWidget');
|
||||
if (!widget.conversations.contains(_currentConversation)) {
|
||||
final selectedIndex = widget.conversations
|
||||
.indexWhere((c) => c.id == _currentConversation.id);
|
||||
_setConversation(selectedIndex);
|
||||
}
|
||||
return Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 200,
|
||||
child:
|
||||
_buildConversationParticipantsList(context, widget.conversations),
|
||||
),
|
||||
SizedBox(width: 1, child: Container(color: Colors.grey)),
|
||||
Expanded(child: _buildConversationPanel(context)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildConversationParticipantsList(
|
||||
BuildContext context, List<FacebookMessengerConversation> conversations) {
|
||||
_logger.fine('Build _buildConversationParticipantsList');
|
||||
final textTheme = Theme.of(context).textTheme;
|
||||
return ScrollablePositionedList.separated(
|
||||
itemScrollController: _scrollController,
|
||||
itemCount: conversations.length,
|
||||
itemBuilder: (context, index) {
|
||||
final conversation = conversations[index];
|
||||
return TextButton(
|
||||
onPressed: () => _setConversation(index),
|
||||
style: _currentConversation == conversation
|
||||
? TextButton.styleFrom(
|
||||
backgroundColor:
|
||||
textTheme.bodyText1?.decorationColor ?? Colors.blue)
|
||||
: null,
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(conversation.title,
|
||||
softWrap: true,
|
||||
textAlign: TextAlign.start,
|
||||
style: _currentConversation == conversation
|
||||
? textTheme.bodyText1
|
||||
: textTheme.bodyText2),
|
||||
));
|
||||
},
|
||||
separatorBuilder: (context, index) {
|
||||
return const Divider(
|
||||
color: Colors.black,
|
||||
thickness: 0.2,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildConversationPanel(BuildContext context) {
|
||||
_logger.fine('Build _buildConversationPanel');
|
||||
if (_currentConversation == noConversationSelected) {
|
||||
return const StandInStatusScreen(
|
||||
title: 'No conversation selected',
|
||||
subTitle: 'Select a conversation to display here',
|
||||
);
|
||||
}
|
||||
|
||||
final settings = Provider.of<SettingsController>(context);
|
||||
final username = settings.facebookName;
|
||||
|
||||
return ListView.separated(
|
||||
primary: false,
|
||||
restorationId: 'facebookConversationPane',
|
||||
itemCount: _currentConversation.messages.length,
|
||||
itemBuilder: (context, index) {
|
||||
final msg = _currentConversation.messages[index];
|
||||
return ConversationMessageCard(
|
||||
message: msg.from == username ? msg.copy(from: 'You') : msg);
|
||||
},
|
||||
separatorBuilder: (context, index) {
|
||||
return const SizedBox(height: 5);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/components/event_card.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/components/filter_control_component.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/facebook_event.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/model_utils.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/services/facebook_archive_service.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:provider/provider.dart';
|
||||
import 'package:result_monad/result_monad.dart';
|
||||
|
||||
import '../../screens/loading_status_screen.dart';
|
||||
import '../../screens/standin_status_screen.dart';
|
||||
|
||||
class FacebookEventsScreen extends StatelessWidget {
|
||||
static final _logger = Logger('$FacebookEventsScreen');
|
||||
|
||||
const FacebookEventsScreen({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final service = Provider.of<FacebookArchiveDataService>(context);
|
||||
_logger.fine('Build FacebookEventsScreen');
|
||||
|
||||
return FutureBuilder<Result<List<FacebookEvent>, ExecError>>(
|
||||
future: service.getEvents(),
|
||||
builder: (context, snapshot) {
|
||||
_logger.fine('Future Events builder called');
|
||||
|
||||
if (!snapshot.hasData ||
|
||||
snapshot.connectionState != ConnectionState.done) {
|
||||
return const LoadingStatusScreen(title: 'Loading events');
|
||||
}
|
||||
|
||||
final eventsResult = snapshot.requireData;
|
||||
if (eventsResult.isFailure) {
|
||||
return ErrorScreen(error: eventsResult.error);
|
||||
}
|
||||
|
||||
final events = eventsResult.value;
|
||||
|
||||
if (events.isEmpty) {
|
||||
return const StandInStatusScreen(title: 'No events were found');
|
||||
}
|
||||
_logger.fine('Build events ListView');
|
||||
return _FacebookEventsScreenWidget(events: events);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class _FacebookEventsScreenWidget extends StatelessWidget {
|
||||
static final _logger = Logger('$_FacebookEventsScreenWidget');
|
||||
final List<FacebookEvent> events;
|
||||
|
||||
const _FacebookEventsScreenWidget({Key? key, required this.events})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FilterControl<FacebookEvent, dynamic>(
|
||||
allItems: events,
|
||||
textSearchFilterFunction: (event, text) =>
|
||||
event.name.contains(text) ||
|
||||
event.description.contains(text) ||
|
||||
event.location.name.contains(text) ||
|
||||
event.location.address.contains(text),
|
||||
itemToDateTimeFunction: (event) {
|
||||
if (event.endTimestamp == 0) {
|
||||
return DateTime.fromMillisecondsSinceEpoch(
|
||||
event.startTimestamp * 1000);
|
||||
}
|
||||
return DateTime.fromMillisecondsSinceEpoch(event.endTimestamp * 1000);
|
||||
},
|
||||
dateRangeFilterFunction: (event, start, stop) =>
|
||||
timestampInRange(event.startTimestamp * 1000, start, stop) ||
|
||||
timestampInRange(event.endTimestamp * 1000, start, stop),
|
||||
builder: (context, items) {
|
||||
if (items.isEmpty) {
|
||||
return const StandInStatusScreen(
|
||||
title: 'No events meet filter criteria');
|
||||
}
|
||||
|
||||
return ListView.separated(
|
||||
primary: false,
|
||||
restorationId: 'facebookEventsListView',
|
||||
itemCount: items.length,
|
||||
itemBuilder: (context, index) {
|
||||
_logger.finer('Rendering Facebook Event List Item');
|
||||
return EventCard(event: items[index]);
|
||||
},
|
||||
separatorBuilder: (context, index) {
|
||||
return const Divider(
|
||||
color: Colors.black,
|
||||
thickness: 0.2,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -4,9 +4,7 @@ import 'dart:io';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/facebook_album.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/facebook_comment.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/facebook_event.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/facebook_friend.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/facebook_messenger_conversation.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/facebook_post.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/facebook_saved_item.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/facebook_timeline_type.dart';
|
||||
|
@ -228,148 +226,6 @@ class FacebookArchiveFolderReader extends ChangeNotifier {
|
|||
return Result.ok(allFriends);
|
||||
}
|
||||
|
||||
FutureResult<List<FacebookEvent>, ExecError> readEvents() async {
|
||||
final basePath = '$rootDirectoryPath/events';
|
||||
final invitationsFile = File('$basePath/event_invitations.json');
|
||||
final responsesFile = File('$basePath/your_event_responses.json');
|
||||
final yourEventsFile = File('$basePath/your_events.json');
|
||||
final events = <FacebookEvent>[];
|
||||
|
||||
if (!Directory(basePath).existsSync()) {
|
||||
_logger.severe('Events base folder does not exist: $basePath');
|
||||
return Result.error(
|
||||
ExecError(errorMessage: 'Events data does not exist'));
|
||||
}
|
||||
|
||||
if (invitationsFile.existsSync()) {
|
||||
final json = (await _getJson(invitationsFile.path)).fold(
|
||||
onSuccess: (json) => json,
|
||||
onError: (error) {
|
||||
_logger.severe(
|
||||
'Error $error reading json for ${invitationsFile.path}');
|
||||
return <String, dynamic>{};
|
||||
});
|
||||
final List<dynamic> invited =
|
||||
json['events_invited_v2'] ?? <Map<String, dynamic>>[];
|
||||
try {
|
||||
events.addAll(invited.map((e) => FacebookEvent.fromJson(e,
|
||||
statusType: FacebookEventStatus.invited)));
|
||||
} catch (e) {
|
||||
_logger.severe(
|
||||
'Error $e processing JSON invitations file: ${invitationsFile.path}');
|
||||
}
|
||||
} else {
|
||||
_logger.info('Invitations file does not exist; ${invitationsFile.path}');
|
||||
}
|
||||
|
||||
if (responsesFile.existsSync()) {
|
||||
final json = (await _getJson(responsesFile.path)).fold(
|
||||
onSuccess: (json) => json,
|
||||
onError: (error) {
|
||||
_logger.severe(
|
||||
'Error $error responses json for ${responsesFile.path}');
|
||||
return <String, dynamic>{};
|
||||
});
|
||||
final Map<String, dynamic> responses =
|
||||
json['event_responses_v2'] ?? <String, dynamic>{};
|
||||
final List<dynamic> joined = responses['events_joined'] ?? [];
|
||||
try {
|
||||
events.addAll(joined.map((e) =>
|
||||
FacebookEvent.fromJson(e, statusType: FacebookEventStatus.joined)));
|
||||
} catch (e) {
|
||||
_logger.severe(
|
||||
'Error $e processing JSON joined events file: ${invitationsFile.path}');
|
||||
}
|
||||
final List<dynamic> declined = responses['events_declined'] ?? [];
|
||||
try {
|
||||
events.addAll(declined.map((e) => FacebookEvent.fromJson(e,
|
||||
statusType: FacebookEventStatus.declined)));
|
||||
} catch (e) {
|
||||
_logger.severe(
|
||||
'Error $e processing JSON declined events file: ${invitationsFile.path}');
|
||||
}
|
||||
final List<dynamic> interested = responses['events_interested'] ?? [];
|
||||
try {
|
||||
events.addAll(interested.map((e) => FacebookEvent.fromJson(e,
|
||||
statusType: FacebookEventStatus.declined)));
|
||||
} catch (e) {
|
||||
_logger.severe(
|
||||
'Error $e processing JSON interested events file: ${invitationsFile.path}');
|
||||
}
|
||||
} else {
|
||||
_logger.info('Responses file does not exist; ${responsesFile.path}');
|
||||
}
|
||||
|
||||
if (yourEventsFile.existsSync()) {
|
||||
final json = (await _getJson(yourEventsFile.path)).fold(
|
||||
onSuccess: (json) => json,
|
||||
onError: (error) {
|
||||
_logger.severe(
|
||||
'Error $error your events file json for ${responsesFile.path}');
|
||||
return <String, dynamic>{};
|
||||
});
|
||||
final List<dynamic> yourEvents =
|
||||
json['your_events_v2'] ?? <Map<String, dynamic>>[];
|
||||
try {
|
||||
events.addAll(yourEvents.map((e) =>
|
||||
FacebookEvent.fromJson(e, statusType: FacebookEventStatus.owner)));
|
||||
} catch (e) {
|
||||
_logger.severe(
|
||||
'Error $e processing JSON your events file: ${yourEventsFile.path}');
|
||||
}
|
||||
} else {
|
||||
_logger.info('Your events file does not exist ${yourEventsFile.path}');
|
||||
}
|
||||
|
||||
events.sort((e1, e2) => -e1.startTimestamp.compareTo(e2.startTimestamp));
|
||||
|
||||
return Result.ok(events);
|
||||
}
|
||||
|
||||
FutureResult<List<FacebookMessengerConversation>, ExecError>
|
||||
readConversations() async {
|
||||
final path = '$rootDirectoryPath/messages';
|
||||
final folder = Directory(path);
|
||||
final conversations = <String, FacebookMessengerConversation>{};
|
||||
|
||||
if (!folder.existsSync()) {
|
||||
_logger.severe('Messages folder does not exist; $path');
|
||||
return Result.ok([]);
|
||||
}
|
||||
|
||||
await for (var entity in folder.list(recursive: true)) {
|
||||
if (entity.path.toLowerCase().endsWith('json')) {
|
||||
if (entity is Directory) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
final jsonResult = await _getJson(entity.path, level: Level.FINEST);
|
||||
if (jsonResult.isFailure) {
|
||||
_logger.severe(
|
||||
'Error ${jsonResult.error} reading JSON data for ${entity.path}');
|
||||
continue;
|
||||
}
|
||||
|
||||
final conversation =
|
||||
FacebookMessengerConversation.fromFacebookJson(jsonResult.value);
|
||||
if (conversations.containsKey(conversation.id)) {
|
||||
final existingConvo = conversations[conversation.id]!;
|
||||
existingConvo.messages.addAll(conversation.messages);
|
||||
existingConvo.messages
|
||||
.sort((m1, m2) => -m1.timestampMS.compareTo(m2.timestampMS));
|
||||
} else {
|
||||
conversations[conversation.id] = conversation;
|
||||
}
|
||||
} catch (e) {
|
||||
_logger.severe('Error $e processing conversation ${entity.path}');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Result.ok(conversations.values.toList());
|
||||
}
|
||||
|
||||
FutureResult<List<FacebookSavedItem>, ExecError> readSavedItems() async {
|
||||
final path =
|
||||
'$rootDirectoryPath/saved_items_and_collections/saved_items_and_collections.json';
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/facebook_album.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/facebook_comment.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/facebook_event.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/facebook_friend.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/facebook_media_attachment.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/facebook_messenger_conversation.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/facebook_post.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/facebook_saved_item.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/services/path_mapping_service.dart';
|
||||
|
@ -25,9 +22,7 @@ class FacebookArchiveDataService extends ChangeNotifier {
|
|||
final List<FacebookAlbum> albums = [];
|
||||
final List<FacebookPost> posts = [];
|
||||
final List<FacebookComment> comments = [];
|
||||
final List<FacebookEvent> events = [];
|
||||
final List<FacebookFriend> friends = [];
|
||||
final List<FacebookMessengerConversation> convos = [];
|
||||
final List<FacebookSavedItem> savedItems = [];
|
||||
bool canUseConvoCacheFile = true;
|
||||
|
||||
|
@ -42,8 +37,6 @@ class FacebookArchiveDataService extends ChangeNotifier {
|
|||
albums.clear();
|
||||
posts.clear();
|
||||
comments.clear();
|
||||
events.clear();
|
||||
convos.clear();
|
||||
friends.clear();
|
||||
savedItems.clear();
|
||||
notifyListeners();
|
||||
|
@ -111,28 +104,6 @@ class FacebookArchiveDataService extends ChangeNotifier {
|
|||
return Result.ok(List.unmodifiable(comments));
|
||||
}
|
||||
|
||||
FutureResult<List<FacebookEvent>, ExecError> getEvents() async {
|
||||
_logger.fine('Request for events');
|
||||
if (events.isNotEmpty) {
|
||||
_logger.fine(
|
||||
'Events already loaded, returning existing ${events.length} events');
|
||||
return Result.ok(List.unmodifiable(events));
|
||||
}
|
||||
_logger.finer('No previously pulled events reading from disk');
|
||||
final eventsResult = await _readAllEvents();
|
||||
eventsResult.match(
|
||||
onSuccess: (newEvents) {
|
||||
events.clear();
|
||||
events.addAll(newEvents);
|
||||
events.sort((e1, e2) =>
|
||||
-e1.creationTimestamp.compareTo(e2.creationTimestamp));
|
||||
},
|
||||
onError: (error) => _logger.severe('Error loading events: $error'));
|
||||
|
||||
_logger.fine('Returning ${comments.length} events');
|
||||
return Result.ok(List.unmodifiable(events));
|
||||
}
|
||||
|
||||
FutureResult<List<FacebookFriend>, ExecError> getFriends() async {
|
||||
_logger.fine('Request for friends');
|
||||
if (friends.isNotEmpty) {
|
||||
|
@ -183,62 +154,6 @@ class FacebookArchiveDataService extends ChangeNotifier {
|
|||
return Result.ok(List.unmodifiable(albums));
|
||||
}
|
||||
|
||||
FutureResult<List<FacebookMessengerConversation>, ExecError>
|
||||
getConvos() async {
|
||||
_logger.fine('Request for conversations');
|
||||
if (convos.isNotEmpty) {
|
||||
_logger.fine(
|
||||
'Conversations already loaded, returning existing ${convos.length} posts');
|
||||
return Result.ok(List.unmodifiable(convos));
|
||||
}
|
||||
|
||||
final convoCacheFile = File(_conversationCachePath);
|
||||
try {
|
||||
if (canUseConvoCacheFile && convoCacheFile.existsSync()) {
|
||||
_logger.finer(
|
||||
'Attempt to load conversations from: $_conversationCachePath');
|
||||
final newConvosTextResult = await convoCacheFile.readAsString();
|
||||
if (newConvosTextResult.isNotEmpty) {
|
||||
final newConvosData =
|
||||
jsonDecode(newConvosTextResult) as List<dynamic>;
|
||||
final newConvos = newConvosData
|
||||
.map((json) => FacebookMessengerConversation.fromJson(json))
|
||||
.toList();
|
||||
convos.clear();
|
||||
convos.addAll(newConvos);
|
||||
_logger.fine(
|
||||
'${newConvos.length} conversations loaded from disk. Returning ${convos.length} conversations');
|
||||
return Result.ok(List.unmodifiable(convos));
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
_logger.severe('Exception thrown trying to read from cache, $e');
|
||||
}
|
||||
|
||||
_logger.finer('No cache data available so reading from original archive');
|
||||
|
||||
final conversationsResult = await _readAllConvos();
|
||||
conversationsResult.match(onSuccess: (newConversations) {
|
||||
convos.clear();
|
||||
convos.addAll(newConversations);
|
||||
convos.sort((c1, c2) =>
|
||||
-c1.latestTimestampMS().compareTo(c2.latestTimestampMS()));
|
||||
}, onError: (error) {
|
||||
_logger.severe('Error loading posts: $error');
|
||||
});
|
||||
try {
|
||||
_logger.finer(
|
||||
'Writing ${convos.length} to conversation cache file $_conversationCachePath');
|
||||
String json = jsonEncode(convos);
|
||||
await convoCacheFile.writeAsString(json, flush: true);
|
||||
} catch (e) {
|
||||
_logger.severe('Error trying to write to cache file, $e');
|
||||
}
|
||||
|
||||
_logger.fine('Returning ${convos.length} conversations');
|
||||
return Result.ok(List.unmodifiable(convos));
|
||||
}
|
||||
|
||||
FutureResult<List<FacebookSavedItem>, ExecError> getSavedItems() async {
|
||||
_logger.fine('Request for saved items');
|
||||
if (savedItems.isNotEmpty) {
|
||||
|
@ -319,34 +234,6 @@ class FacebookArchiveDataService extends ChangeNotifier {
|
|||
'Unable to find any comment JSON files in $_baseArchiveFolder'));
|
||||
}
|
||||
|
||||
FutureResult<List<FacebookEvent>, ExecError> _readAllEvents() async {
|
||||
final allEvents = <FacebookEvent>[];
|
||||
bool hadSuccess = false;
|
||||
for (final topLevelDir in _topLevelDirs) {
|
||||
try {
|
||||
_logger.fine(
|
||||
'Attempting to find/parse event JSON data in ${topLevelDir.path}');
|
||||
final reader = FacebookArchiveFolderReader(topLevelDir.path);
|
||||
final eventsResult = await reader.readEvents();
|
||||
eventsResult.match(
|
||||
onSuccess: (newEvents) {
|
||||
allEvents.addAll(newEvents);
|
||||
hadSuccess = true;
|
||||
},
|
||||
onError: (error) => _logger.fine(error));
|
||||
} catch (e) {
|
||||
_logger.severe('Exception thrown trying to read events, $e');
|
||||
}
|
||||
}
|
||||
|
||||
if (hadSuccess) {
|
||||
return Result.ok(allEvents);
|
||||
}
|
||||
|
||||
return Result.error(ExecError.message(
|
||||
'Unable to find any event JSON files in $_baseArchiveFolder'));
|
||||
}
|
||||
|
||||
FutureResult<List<FacebookFriend>, ExecError> _readAllFriends() async {
|
||||
final allFriends = <FacebookFriend>[];
|
||||
bool hadSuccess = false;
|
||||
|
@ -403,35 +290,6 @@ class FacebookArchiveDataService extends ChangeNotifier {
|
|||
'Unable to find any album JSON files in $_baseArchiveFolder'));
|
||||
}
|
||||
|
||||
FutureResult<List<FacebookMessengerConversation>, ExecError>
|
||||
_readAllConvos() async {
|
||||
final allConvos = <FacebookMessengerConversation>[];
|
||||
bool hadSuccess = false;
|
||||
for (final topLevelDir in _topLevelDirs) {
|
||||
try {
|
||||
_logger.fine(
|
||||
'Attempting to find/parse conversation JSON data in ${topLevelDir.path}');
|
||||
final reader = FacebookArchiveFolderReader(topLevelDir.path);
|
||||
final convosResult = await reader.readConversations();
|
||||
convosResult.match(
|
||||
onSuccess: (newConvos) {
|
||||
allConvos.addAll(newConvos);
|
||||
hadSuccess = true;
|
||||
},
|
||||
onError: (error) => _logger.fine(error));
|
||||
} catch (e) {
|
||||
_logger.severe('Exception thrown trying to read conversations, $e');
|
||||
}
|
||||
}
|
||||
|
||||
if (hadSuccess) {
|
||||
return Result.ok(allConvos);
|
||||
}
|
||||
|
||||
return Result.error(ExecError.message(
|
||||
'Unable to find any event JSON files in $_baseArchiveFolder'));
|
||||
}
|
||||
|
||||
FutureResult<List<FacebookSavedItem>, ExecError> _readAllSavedItems() async {
|
||||
final allSavedItems = <FacebookSavedItem>[];
|
||||
bool hadSuccess = false;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// ignore_for_file: avoid_print
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:friendica_archive_browser/src/facebook/models/model_utils.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/models/model_utils.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
void main() {
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
// ignore_for_file: avoid_print
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:friendica_archive_browser/src/facebook/models/facebook_event.dart';
|
||||
import 'package:friendica_archive_browser/src/facebook/services/facebook_archive_reader.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/services/facebook_archive_reader.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
void main() {
|
||||
|
@ -41,16 +40,6 @@ void main() {
|
|||
});
|
||||
});
|
||||
|
||||
group('Test Read Events JSON', () {
|
||||
test('Read from events disk', () async {
|
||||
final events = await FacebookArchiveFolderReader(rootPath).readEvents();
|
||||
expect(events.value.length, equals(11));
|
||||
events.value
|
||||
.where((element) => element.eventStatus == FacebookEventStatus.owner)
|
||||
.forEach(print);
|
||||
});
|
||||
});
|
||||
|
||||
group('Test Read Friends JSON', () {
|
||||
test('Read from friends disk', () async {
|
||||
final friendsResult =
|
||||
|
@ -61,15 +50,6 @@ void main() {
|
|||
});
|
||||
});
|
||||
|
||||
group('Test Read Conversation JSON', () {
|
||||
test('Read conversations from disk', () async {
|
||||
final conversations =
|
||||
await FacebookArchiveFolderReader(rootPath).readConversations();
|
||||
expect(conversations.value.length, equals(1));
|
||||
conversations.value.forEach(print);
|
||||
});
|
||||
});
|
||||
|
||||
group('Test Read Saved Items JSON', () {
|
||||
test('Read savedItems from disk', () async {
|
||||
final savedItems =
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:friendica_archive_browser/src/facebook/services/facebook_file_reader.dart';
|
||||
import 'package:friendica_archive_browser/src/friendica/services/facebook_file_reader.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
void main() {
|
||||
|
@ -15,24 +15,6 @@ void main() {
|
|||
'${event.level.name} - ${event.loggerName} @ ${event.time}: ${event.message}');
|
||||
});
|
||||
|
||||
// group('Test Facebook Reading timing', () {
|
||||
// test('Normal Read vs. proper encoded read', () async {
|
||||
// final expected = [
|
||||
// 'This is malformed and should be Polish diacritical character ą.',
|
||||
// 'This should be a heart ❤.',
|
||||
// 'This should be a five stars ★★ ★★ ★.',
|
||||
// ];
|
||||
// const path = '$rootPath/mangled.txt';
|
||||
// final result = await File(path).readFacebookEncodedFileAsString();
|
||||
// expect(result.isSuccess, true);
|
||||
// final lines = result.get().split('\n');
|
||||
// expect(lines.length, equals(expected.length));
|
||||
// for(var i = 0; i < lines.length; i++) {
|
||||
// //expect(lines[i], equals(expected[i]));
|
||||
// print('|${lines[i]}| ?= |${expected[i]}|');
|
||||
// }
|
||||
// });
|
||||
|
||||
group('Test Facebook Reading', () {
|
||||
test('Read encoded text from disk', () async {
|
||||
final expected = [
|
||||
|
|
Loading…
Reference in a new issue