meta: Load conversations after PreStart

This commit is contained in:
PapaTutuWawa 2022-03-23 15:50:54 +01:00
parent 6d47472ffb
commit 1615ab347a
11 changed files with 169 additions and 347 deletions

View File

@ -4,7 +4,7 @@ lib/ui/data/generated/providers.g.dart: thirdparty/xmpp-providers/providers-A.js
lib/ui/data/generated/licenses.g.dart: pubspec.yaml
python tools/generate_licenses.py
lib/shared/events.g.dart lib/shared/commands.g.dart: data_classes.yaml
lib/shared/events.g.dart lib/shared/commands.g.dart: data_classes.yaml tools/generate_data_classes.py
python tools/generate_data_classes.py
thirdparty/xmpp-providers/providers-A.json:

View File

@ -16,6 +16,24 @@ files:
- JsonImplementation
attributes:
reason: String
- name: PreStartDoneEvent
extends:
- BackgroundEvent
implements:
- JsonImplementation
attributes:
state: String
debugEnabled: bool
permissionsToRequest: List<int>
preferences:
type: PreferencesState
deserialise: true
jid: String?
displayName: String?
avatarUrl: String?
conversations:
type: List<Conversation>?
deserialise: true
generate_builder: true
builder_name: "Event"
builder_baseclass: "BackgroundEvent"
@ -31,6 +49,11 @@ files:
jid: String
password: String
useDirectTLS: bool
- name: PerformPreStartCommand
extends:
- BackgroundCommand
implements:
- JsonImplementation
generate_builder: true
# get${builder_Name}FromJson
builder_name: "Command"

View File

@ -26,7 +26,8 @@ import "package:moxxyv2/ui/bloc/login_bloc.dart";
import "package:moxxyv2/ui/bloc/conversations_bloc.dart";
import "package:moxxyv2/ui/service/download.dart";
import "package:moxxyv2/service/service.dart";
import "package:moxxyv2/shared/commands.dart" as commands;
import "package:moxxyv2/shared/commands.dart";
import "package:moxxyv2/shared/events.dart";
import "package:moxxyv2/shared/backgroundsender.dart";
import "package:moxxyv2/shared/eventhandler.dart";
@ -37,6 +38,8 @@ import "package:flutter_background_service/flutter_background_service.dart";
import "package:get_it/get_it.dart";
import "package:logging/logging.dart";
// TODO: Merge debug settings into the preferences
void setupLogging() {
Logger.root.level = kDebugMode ? Level.ALL : Level.INFO;
Logger.root.onRecord.listen((record) {
@ -106,11 +109,36 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
void initState() {
super.initState();
WidgetsBinding.instance!.addObserver(this);
/*FlutterBackgroundService().sendData(
commands.PerformPrestartAction().toJson()
);*/
_performPreStart();
}
Future<void> _performPreStart() async {
final result = await GetIt.I.get<BackgroundServiceDataSender>().sendData(
PerformPreStartCommand()
) as PreStartDoneEvent;
// TODO: Set preferences and debug stuff.
if (result.state == preStartLoggedInState) {
GetIt.I.get<ConversationsBloc>().add(
ConversationsInitEvent(
result.displayName!,
result.conversations!
)
);
navigationKey.currentState!.pushNamedAndRemoveUntil(
conversationsRoute,
(_) => false
);
} else if (result.state == preStartNotLoggedInState) {
navigationKey.currentState!.pushNamedAndRemoveUntil(
introRoute,
(_) => false
);
}
}
@override
void dispose() {
WidgetsBinding.instance!.removeObserver(this);
@ -187,8 +215,7 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
blocklistRoute: (context) => BlocklistPage()
*/
},
// TODO: Change back to const Splashscreen()
home: Intro()
home: Splashscreen()
);
}
}

View File

@ -90,7 +90,7 @@ class DatabaseService {
}
/// Loads all conversations from the database and adds them to the state and cache.
Future<void> loadConversations({ bool notify = true }) async {
Future<List<Conversation>> loadConversations({ bool notify = true }) async {
final conversationsRaw = await isar.dBConversations.where().findAll();
final tmp = List<Conversation>.empty(growable: true);
@ -103,6 +103,8 @@ class DatabaseService {
_conversationCache[conv.id] = conv;
}
return tmp;
if (notify) {
// TODO
/*

View File

@ -3,12 +3,16 @@ import "package:moxxyv2/shared/events.dart";
import "package:moxxyv2/shared/eventhandler.dart";
import "package:moxxyv2/service/service.dart";
import "package:moxxyv2/service/xmpp.dart";
import "package:moxxyv2/service/preferences.dart";
import "package:moxxyv2/service/roster.dart";
import "package:moxxyv2/service/database.dart";
import "package:moxxyv2/xmpp/connection.dart";
import "package:moxxyv2/xmpp/settings.dart";
import "package:moxxyv2/xmpp/jid.dart";
import "package:logging/logging.dart";
import "package:get_it/get_it.dart";
import "package:permission_handler/permission_handler.dart";
Future<void> performLoginHandler(BaseEvent c, { dynamic extra }) async {
final command = c as LoginCommand;
@ -21,8 +25,7 @@ Future<void> performLoginHandler(BaseEvent c, { dynamic extra }) async {
password: command.password,
useDirectTLS: command.useDirectTLS,
allowPlainAuth: false
),
true
), true
);
if (result.success) {
@ -43,3 +46,57 @@ Future<void> performLoginHandler(BaseEvent c, { dynamic extra }) async {
);
}
}
Future<void> performPreStart(BaseEvent c, { dynamic extra }) async {
final command = c as PerformPreStartCommand;
final id = extra as String;
final xmpp = GetIt.I.get<XmppService>();
final account = await xmpp.getAccountData();
final settings = await xmpp.getConnectionSettings();
final state = await xmpp.getXmppState();
final preferences = await GetIt.I.get<PreferencesService>().getPreferences();
GetIt.I.get<Logger>().finest("account != null: " + (account != null).toString());
GetIt.I.get<Logger>().finest("settings != null: " + (settings != null).toString());
if (account!= null && settings != null) {
await GetIt.I.get<RosterService>().loadRosterFromDatabase();
// Check some permissions
final storagePerm = await Permission.storage.status;
final List<int> permissions = List.empty(growable: true);
if (storagePerm.isDenied /*&& !state.askedStoragePermission*/) {
permissions.add(Permission.storage.value);
await xmpp.modifyXmppState((state) => state.copyWith(
askedStoragePermission: true
));
}
sendEvent(
PreStartDoneEvent(
state: "logged_in",
jid: account.jid,
displayName: account.displayName,
avatarUrl: state.avatarUrl,
debugEnabled: state.debugEnabled,
permissionsToRequest: permissions,
preferences: preferences,
conversations: await GetIt.I.get<DatabaseService>().loadConversations()
),
id: id
);
} else {
sendEvent(
PreStartDoneEvent(
state: "not_logged_in",
debugEnabled: state.debugEnabled,
permissionsToRequest: List<int>.empty(),
preferences: preferences
),
id: id
);
}
}

View File

@ -46,7 +46,6 @@ import "package:get_it/get_it.dart";
import "package:isar/isar.dart";
import "package:path_provider/path_provider.dart";
import "package:logging/logging.dart";
import "package:permission_handler/permission_handler.dart";
import "package:uuid/uuid.dart";
import "package:moxxyv2/service/db/conversation.dart";
@ -225,7 +224,8 @@ Future<FlutterBackgroundService> initializeService() async {
void setupBackgroundEventHandler() {
final handler = EventHandler();
handler.addMatchers([
EventTypeMatcher<LoginCommand>(performLoginHandler)
EventTypeMatcher<LoginCommand>(performLoginHandler),
EventTypeMatcher<PerformPreStartCommand>(performPreStart)
]);
GetIt.I.registerSingleton<EventHandler>(handler);

View File

@ -7,334 +7,10 @@ import "package:moxxyv2/shared/models/message.dart";
part "events.g.dart";
const preStartLoggedInState = "logged_in";
const preStartNotLoggedInState = "not_logged_in";
class BackgroundEvent extends BaseEvent implements JsonImplementation {
// NOTE: This is just to make the type system happy
Map<String, dynamic> toJson() => {};
}
/*
const rosterDiffType = "RosterDiff";
class RosterDiffEvent extends BaseIsolateEvent {
final List<RosterItem> newItems;
final List<RosterItem> changedItems;
final List<String> removedItems;
RosterDiffEvent({ this.newItems = const [], this.changedItems = const [], this.removedItems = const [] });
RosterDiffEvent.fromJson(Map<String, dynamic> json) :
newItems = List<RosterItem>.from(json["newItems"]!.map((i) => RosterItem.fromJson(i))),
changedItems = List<RosterItem>.from(json["changedItems"]!.map((i) => RosterItem.fromJson(i))),
removedItems = List<String>.from(json["removedItems"]!) {
assert(json["type"] == rosterDiffType);
}
@override
Map<String, dynamic> toJson() => {
"type": rosterDiffType,
"newItems": newItems.map((i) => i.toJson()).toList(),
"changedItems": changedItems.map((i) => i.toJson()).toList(),
"removedItems": removedItems
};
}
const addToRosterResultType = "AddToRosterResult";
class AddToRosterResultEvent extends BaseIsolateEvent {
final String result;
final String? msg;
final String? jid;
AddToRosterResultEvent({ required this.result, this.msg, this.jid });
AddToRosterResultEvent.fromJson(Map<String, dynamic> json) :
result = json["result"]!,
msg = json["msg"],
jid = json["jid"] {
assert(json["type"] == addToRosterResultType);
}
@override
Map<String, dynamic> toJson() => {
"type": addToRosterResultType,
"result": result,
"msg": msg,
"jid": jid
};
}
const loadConversationsResultType = "LoadConversationsResult";
class LoadConversationsResultEvent extends BaseIsolateEvent {
final List<Conversation> conversations;
LoadConversationsResultEvent({ required this.conversations });
LoadConversationsResultEvent.fromJson(Map<String, dynamic> json) :
conversations = List<Conversation>.from(json["conversations"]!.map((i) => Conversation.fromJson(i))) {
assert(json["type"] == loadConversationsResultType);
}
@override
Map<String, dynamic> toJson() => {
"type": loadConversationsResultType,
"conversations": conversations.map((c) => c.toJson()).toList()
};
}
const loadMessagesForJidType = "LoadMessagesForJidResult";
class LoadMessagesForJidEvent extends BaseIsolateEvent {
final List<Message> messages;
final String jid;
LoadMessagesForJidEvent({ required this.jid, required this.messages });
LoadMessagesForJidEvent.fromJson(Map<String, dynamic> json) :
jid = json["jid"]!,
messages = List<Message>.from(json["messages"]!.map((m) => Message.fromJson(m))) {
assert(json["type"] == loadMessagesForJidType);
}
@override
Map<String, dynamic> toJson() => {
"type": loadMessagesForJidType,
"jid": jid,
"messages": messages.map((m) => m.toJson()).toList()
};
}
const conversationUpdatedType = "ConversationUpdatedEvent";
class ConversationUpdatedEvent extends BaseIsolateEvent {
final Conversation conversation;
ConversationUpdatedEvent({ required this.conversation });
ConversationUpdatedEvent.fromJson(Map<String, dynamic> json) :
conversation = Conversation.fromJson(json["conversation"]!) {
assert(json["type"] == conversationUpdatedType);
}
@override
Map<String, dynamic> toJson() => {
"type": conversationUpdatedType,
"conversation": conversation.toJson()
};
}
const conversationCreatedType = "ConversationCreatedEvent";
class ConversationCreatedEvent extends BaseIsolateEvent {
final Conversation conversation;
ConversationCreatedEvent({ required this.conversation });
ConversationCreatedEvent.fromJson(Map<String, dynamic> json) :
conversation = Conversation.fromJson(json["conversation"]!) {
assert(json["type"] == conversationCreatedType);
}
@override
Map<String, dynamic> toJson() => {
"type": conversationCreatedType,
"conversation": conversation.toJson()
};
}
const messageSendType = "MessageSendResult";
class MessageSendResultEvent extends BaseIsolateEvent {
final Message message;
MessageSendResultEvent({ required this.message });
MessageSendResultEvent.fromJson(Map<String, dynamic> json) :
message = Message.fromJson(json["message"]!) {
assert(json["type"] == messageSendType);
}
@override
Map<String, dynamic> toJson() => {
"type": messageSendType,
"message": message.toJson()
};
}
const messageUpdatedType = "MessageUpdatedEvent";
class MessageUpdatedEvent extends BaseIsolateEvent {
final Message message;
MessageUpdatedEvent({ required this.message });
MessageUpdatedEvent.fromJson(Map<String, dynamic> json) :
message = Message.fromJson(json["message"]!) {
assert(json["type"] == messageUpdatedType);
}
@override
Map<String, dynamic> toJson() => {
"type": messageUpdatedType, "message": message.toJson()
};
}
const messageReceivedType = "MessageReceivedEvent";
class MessageReceivedEvent extends BaseIsolateEvent {
final Message message;
MessageReceivedEvent({ required this.message });
MessageReceivedEvent.fromJson(Map<String, dynamic> json) :
message = Message.fromJson(json["message"]!) {
assert(json["type"] == messageReceivedType);
}
@override
Map<String, dynamic> toJson() => {
"type": messageReceivedType,
"message": message.toJson()
};
}
const connectionStateType = "ConnectionStateEvent";
class ConnectionStateEvent extends BaseIsolateEvent {
final String state;
ConnectionStateEvent({ required this.state });
ConnectionStateEvent.fromJson(Map<String, dynamic> json) :
state = json["state"]! {
assert(json["type"] == connectionStateType);
}
@override
Map<String, dynamic> toJson() => {
"type": connectionStateType,
"state": state
};
}
const loginSuccessfulType = "LoginSuccessfulEvent";
class LoginSuccessfulEvent extends BaseIsolateEvent {
final String displayName;
final String jid;
LoginSuccessfulEvent({ required this.displayName, required this.jid });
LoginSuccessfulEvent.fromJson(Map<String, dynamic> json) :
jid = json["jid"]!,
displayName = json["displayName"]!
{
assert(json["type"] == loginSuccessfulType);
}
@override
Map<String, dynamic> toJson() => {
"type": loginSuccessfulType,
"jid": jid,
"displayName": displayName
};
}
const loginFailedType = "LoginFailedEvent";
class LoginFailedEvent extends BaseIsolateEvent {
final String reason;
LoginFailedEvent({ required this.reason });
LoginFailedEvent.fromJson(Map<String, dynamic> json) :
reason = json["reason"]! {
assert(json["type"] == loginFailedType);
}
@override
Map<String, dynamic> toJson() => {
"type": loginFailedType,
"reason": reason
};
}
const preStartResultType = "PreStartResult";
class PreStartResultEvent extends BaseIsolateEvent {
final String state;
final bool debugEnabled;
final List<int> permissionsToRequest;
final PreferencesState preferences;
final String? jid;
final String? displayName;
final String? avatarUrl;
PreStartResultEvent({
required this.state,
required this.debugEnabled,
required this.permissionsToRequest,
required this.preferences,
this.jid,
this.displayName,
this.avatarUrl,
});
PreStartResultEvent.fromJson(Map<String, dynamic> json) :
state = json["state"]!,
debugEnabled = json["debugEnabled"]!,
preferences = PreferencesState.fromJson(json["preferences"]!),
jid = json["jid"],
displayName = json["displayName"],
avatarUrl = json["avatarUrl"],
permissionsToRequest = List<int>.from(json["permissionsToRequest"]!) {
assert(json["type"] == preStartResultType);
}
@override
Map<String, dynamic> toJson() => {
"type": preStartResultType,
"state": state,
"debugEnabled": debugEnabled,
"jid": jid,
"displayName": displayName,
"avatarUrl": avatarUrl,
"permissionsToRequest": permissionsToRequest,
"preferences": preferences.toJson()
};
}
const downloadProgressType = "DownloadProgressEvent";
class DownloadProgressEvent extends BaseIsolateEvent {
final int id;
final double progress;
DownloadProgressEvent({ required this.id, required this.progress });
DownloadProgressEvent.fromJson(Map<String, dynamic> json) :
id = json["id"]!,
progress = json["progress"]!.toDouble() {
assert(json["type"] == downloadProgressType);
}
@override
Map<String, dynamic> toJson() => {
"type": downloadProgressType,
"id": id,
"progress": progress
};
}
const newConversationDoneEventType = "NewConversationDoneEvent";
class NewConversationDoneEvent extends BaseIsolateEvent {
final String jid;
NewConversationDoneEvent({ required this.jid });
NewConversationDoneEvent.fromJson(Map<String, dynamic> json) :
jid = json["jid"]! {
assert(json["type"] == newConversationDoneEventType);
}
@override
Map<String, dynamic> toJson() => {
"type": newConversationDoneEventType,
"jid": jid
};
}
const blocklistDiffEventType = "BlocklistDiffEvent";
class BlocklistDiffEvent extends BaseIsolateEvent {
final List<String> newBlockedItems;
final List<String> removedBlockedItems;
BlocklistDiffEvent({
this.newBlockedItems = const [],
this.removedBlockedItems = const []
});
BlocklistDiffEvent.fromJson(Map<String, dynamic> json) :
newBlockedItems = List<String>.from(json["newBlockedItems"]!),
removedBlockedItems = List<String>.from(json["removedBlockedItems"]!) {
assert(json["type"] == blocklistDiffEventType);
}
@override
Map<String, dynamic> toJson() => {
"type": blocklistDiffEventType,
"newBlockedItems": newBlockedItems,
"removedBlockedItems": removedBlockedItems
};
}
*/

View File

@ -20,7 +20,8 @@ class ConversationsBloc extends Bloc<ConversationsEvent, ConversationsState> {
Future<void> _onLoggedIn(ConversationsInitEvent event, Emitter<ConversationsState> emit) async {
return emit(
state.copyWith(
displayName: event.displayName
displayName: event.displayName,
conversations: event.conversations
)
);
}

View File

@ -4,6 +4,7 @@ abstract class ConversationsEvent {}
class ConversationsInitEvent extends ConversationsEvent {
final String displayName;
final List<Conversation> conversations;
ConversationsInitEvent(this.displayName);
ConversationsInitEvent(this.displayName, this.conversations);
}

View File

@ -89,7 +89,9 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> {
GetIt.I.get<ConversationsBloc>().add(
ConversationsInitEvent(
result.displayName
result.displayName,
// TODO
[]
)
);
GetIt.I.get<NavigationBloc>().add(

View File

@ -1,3 +1,5 @@
import re
import yaml
def generateToJson(name, attributes):
@ -10,11 +12,29 @@ def generateToJson(name, attributes):
json += "\t};\n"
return json
def generateFromJsonListBuilder(attrName, attrType, deserialise=False):
if "List<" in attrType:
listType = re.match("List\<(.*?)\>", attrType).groups()[0]
suffix = " ?? []" if "?" in attrType else "!"
data = "(json[\"" + attrName + "\"]" + suffix + ")"
if deserialise:
data += ".map((item) => {}.fromJson(item))".format(listType)
return "\t\t" + attrName + ": List<" + listType + ">.from(" + data + "),\n";
if deserialise:
return "\t\t" + attrName + ": " + attrType + ".fromJson(json[\"" + attrName + "\"]" + ("" if attrType.endswith("?") else "!") + "),\n"
return "\t\t" + attrName + ": json[\"" + attrName + "\"]" + ("" if attrType.endswith("?") else "!") + ",\n"
def generateFromJson(name, attributes):
json = "\tstatic " + name + " fromJson(Map<String, dynamic> json) => " + name + "(\n"
for attr in attributes:
json += "\t\t" + attr + ": json[\"" + attr + "\"]" + ("" if attributes[attr].endswith("?") else "!") + ",\n"
json += generateFromJsonListBuilder(
attr,
getType(attributes[attr]),
getSerialise(attributes[attr])
)
json += "\t);\n"
@ -32,6 +52,16 @@ def generateBuilder(builderName, builderBaseClass, classes):
func += "}\n"
return func
def getType(val):
if type(val) is dict:
return val["type"]
return val
def getSerialise(val):
if type(val) is dict:
return val["deserialise"]
return False
def handleRequired(type_):
return "required " if not type_.endswith("?") else ""
@ -55,12 +85,15 @@ def main():
content += f"class {c['name']} extends {extends} implements {implements}" + " {\n"
attributes = c.get("attributes", [])
for attr in attributes:
content += "\tfinal {} {};\n".format(c["attributes"][attr], attr)
content += "\tfinal {} {};\n".format(getType(c["attributes"][attr]), attr)
content += "\n"
content += "\t" + c["name"] + "({ " + ", ".join([
handleRequired(c["attributes"][name]) + "this." + name for name in attributes
]) + " });\n\n"
if attributes:
content += "\t" + c["name"] + "({ " + ", ".join([
handleRequired(getType(c["attributes"][name])) + "this." + name for name in attributes
]) + " });\n\n"
else:
content += "\t" + c["name"] + "();\n\n";
content += "\t// JSON stuff\n"
content += generateToJson(c["name"], attributes)