service: Implement setting the subscription state of a contact

This commit is contained in:
PapaTutuWawa 2022-04-08 20:29:46 +02:00
parent b813f5e5e1
commit dfecaba50e
10 changed files with 71 additions and 21 deletions

View File

@ -25,7 +25,7 @@ SharedMedium sharedMediumDbToModel(DBSharedMedium s) {
);
}
Conversation conversationDbToModel(DBConversation c, bool inRoster, String? subscription) {
Conversation conversationDbToModel(DBConversation c, bool inRoster, String subscription) {
final media = c.sharedMedia.map(sharedMediumDbToModel).toList();
media.sort((a, b) => a.timestamp.compareTo(b.timestamp));
@ -50,6 +50,7 @@ RosterItem rosterDbToModel(DBRosterItem i) {
avatarUrl: i.avatarUrl,
jid: i.jid,
subscription: i.subscription,
ask: i.ask,
groups: i.groups,
title: i.title
);
@ -117,7 +118,7 @@ class DatabaseService {
final conv = conversationDbToModel(
c,
rosterItem != null,
rosterItem?.subscription
rosterItem?.subscription ?? "none"
);
tmp.add(conv);
_conversationCache[conv.id] = conv;
@ -181,7 +182,7 @@ class DatabaseService {
});
final rosterItem = await getRosterItemByJid(c.jid);
final conversation = conversationDbToModel(c, rosterItem != null, rosterItem?.subscription);
final conversation = conversationDbToModel(c, rosterItem != null, rosterItem?.subscription ?? "none");
_conversationCache[c.id!] = conversation;
return conversation;
}
@ -205,7 +206,7 @@ class DatabaseService {
});
final rosterItem = await getRosterItemByJid(c.jid);
final conversation = conversationDbToModel(c, rosterItem != null, rosterItem?.subscription);
final conversation = conversationDbToModel(c, rosterItem != null, rosterItem?.subscription ?? "none");
_conversationCache[c.id!] = conversation;
return conversation;
@ -361,12 +362,13 @@ class DatabaseService {
}
/// Create a roster item from data
Future<RosterItem> addRosterItemFromData(String avatarUrl, String jid, String title, String subscription, { List<String>? groups }) async {
Future<RosterItem> addRosterItemFromData(String avatarUrl, String jid, String title, String subscription, String ask, { List<String>? groups }) async {
final rosterItem = DBRosterItem()
..jid = jid
..title = title
..avatarUrl = avatarUrl
..subscription = subscription
..ask = ask
..groups = groups ?? const [];
await isar.writeTxn((isar) async {
@ -380,7 +382,7 @@ class DatabaseService {
}
/// Updates the roster item with id [id] inside the database.
Future<RosterItem> updateRosterItem({ required int id, String? avatarUrl, String? title, String? subscription, List<String>? groups }) async {
Future<RosterItem> updateRosterItem({ required int id, String? avatarUrl, String? title, String? subscription, String? ask, List<String>? groups }) async {
final i = (await isar.dBRosterItems.get(id))!;
if (avatarUrl != null) {
i.avatarUrl = avatarUrl;
@ -394,6 +396,9 @@ class DatabaseService {
if (subscription != null) {
i.subscription = subscription;
}
if (ask != null) {
i.ask = ask;
}
await isar.writeTxn((isar) async {
await isar.dBRosterItems.put(i);

View File

@ -16,4 +16,6 @@ class DBRosterItem {
late List<String> groups;
late String subscription;
late String ask;
}

View File

@ -270,11 +270,24 @@ Future<void> performSetAvatar(SetAvatarCommand command, { dynamic extra }) async
}
Future<void> performSetShareOnlineStatus(SetShareOnlineStatusCommand command, { dynamic extra }) async {
final xmpp = GetIt.I.get<XmppConnection>();
final roster = GetIt.I.get<RosterService>();
final db = GetIt.I.get<DatabaseService>();
final item = await db.getRosterItemByJid(command.jid);
// TODO: Maybe log
if (item == null) return;
if (command.share) {
xmpp.getPresenceManager().sendSubscriptionRequestApproval(command.jid);
if (item.ask == "subscribe") {
roster.acceptSubscriptionRequest(command.jid);
} else {
roster.sendSubscriptionRequest(command.jid);
}
} else {
xmpp.getPresenceManager().sendUnsubscriptionRequest(command.jid);
if (item.ask == "subscribe") {
roster.rejectSubscriptionRequest(command.jid);
} else {
roster.sendUnsubscriptionRequest(command.jid);
}
}
}

View File

@ -61,6 +61,7 @@ Future<RosterDiffEvent> rosterDiff(List<RosterItem> currentRoster, List<XmppRost
item.jid,
item.name ?? item.jid.split("@")[0],
item.subscription,
item.ask ?? "",
groups: item.groups
);
@ -102,6 +103,7 @@ Future<RosterDiffEvent> rosterDiff(List<RosterItem> currentRoster, List<XmppRost
item.jid,
item.jid.split("@")[0],
item.subscription,
item.ask ?? "",
groups: item.groups
));
}
@ -134,8 +136,13 @@ class RosterService {
/// and, if it was successful, create the database entry. Returns the
/// [RosterItem] model object.
Future<RosterItem> addToRosterWrapper(String avatarUrl, String jid, String title) async {
// TODO: Correct?
final item = await GetIt.I.get<DatabaseService>().addRosterItemFromData(avatarUrl, jid, title, "to");
final item = await GetIt.I.get<DatabaseService>().addRosterItemFromData(
avatarUrl,
jid,
title,
"none",
""
);
final result = await GetIt.I.get<XmppConnection>().getRosterManager().addToRoster(jid, title);
if (!result) {
// TODO: Signal error?

View File

@ -23,7 +23,7 @@ class Conversation with _$Conversation {
// Indicates, if [jid] is a regular user, if the user is in the roster.
bool inRoster,
// The subscription state of the roster item
String? subscription
String subscription
) = _Conversation;
// JSON

View File

@ -6,16 +6,18 @@ class RosterItem extends Equatable {
final String jid;
final String title;
final String subscription;
final String ask;
final List<String> groups;
final int id;
const RosterItem({ required this.avatarUrl, required this.jid, required this.title, required this.subscription, required this.groups, required this.id });
const RosterItem({ required this.avatarUrl, required this.jid, required this.title, required this.subscription, required this.groups, required this.ask, required this.id });
RosterItem.fromJson(Map<String, dynamic> json)
: avatarUrl = json["avatarUrl"],
jid = json["jid"],
title = json["title"],
subscription = json["subscription"],
ask = json["ask"],
groups = List<String>.from(json["groups"]!),
id = json["id"];
@ -24,6 +26,7 @@ class RosterItem extends Equatable {
"jid": jid,
"title": title,
"subscription": subscription,
"ask": ask,
"groups": groups,
"id": id
};
@ -32,5 +35,5 @@ class RosterItem extends Equatable {
bool get stringify => true;
@override
List<Object> get props => [ avatarUrl, jid, title, subscription, id, groups ];
List<Object> get props => [ avatarUrl, jid, title, subscription, id, groups, ask ];
}

View File

@ -18,6 +18,7 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
on<ProfilePageRequestedEvent>(_onProfileRequested);
on<ConversationUpdatedEvent>(_onConversationUpdated);
on<AvatarSetEvent>(_onAvatarSet);
on<SetSubscriptionStateEvent>(_onSetSubscriptionState);
}
Future<void> _onProfileRequested(ProfilePageRequestedEvent event, Emitter<ProfileState> emit) async {
@ -71,4 +72,13 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
awaitable: false
);
}
Future<void> _onSetSubscriptionState(SetSubscriptionStateEvent event, Emitter<ProfileState> emit) async {
// TODO: Maybe already emit the state change to have it instant and debounce it until
// everything else is done
GetIt.I.get<BackgroundServiceDataSender>().sendData(
SetShareOnlineStatusCommand(jid: event.jid, share: event.shareStatus),
awaitable: false
);
}
}

View File

@ -35,3 +35,11 @@ class AvatarSetEvent extends ProfileEvent {
AvatarSetEvent(this.path, this.hash);
}
/// Triggered by the UI when the subscription state should be set
class SetSubscriptionStateEvent extends ProfileEvent {
final String jid;
final bool shareStatus;
SetSubscriptionStateEvent(this.jid, this.shareStatus);
}

View File

@ -50,14 +50,13 @@ class ProfilePage extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 32.0).add(EdgeInsets.only(top: 8.0)),
child: SettingsTile.switchTile(
title: "Share online status",
// TODO: This
// TODO: Requires that we also store the subscription state in the
// database.
switchValue: state.conversation!.subscription == "to" || state.conversation!.subscription == "both",
onToggle: (value) {
GetIt.I.get<BackgroundServiceDataSender>().sendData(
SetShareOnlineStatusCommand(jid: state.conversation!.jid, share: value),
awaitable: false
context.read<ProfileBloc>().add(
SetSubscriptionStateEvent(
state.conversation!.jid,
value
)
);
}
)

View File

@ -12,9 +12,10 @@ class XmppRosterItem {
final String jid;
final String? name;
final String subscription;
final String? ask;
final List<String> groups;
XmppRosterItem({ required this.jid, required this.subscription, this.name, this.groups = const [] });
XmppRosterItem({ required this.jid, required this.subscription, this.ask, this.name, this.groups = const [] });
}
enum RosterRemovalResult {
@ -100,6 +101,7 @@ class RosterManager extends XmppManagerBase {
item: XmppRosterItem(
jid: item.attributes["jid"]!,
subscription: item.attributes["subscription"]!,
ask: item.attributes["ask"],
name: item.attributes["name"],
),
ver: query.attributes["ver"]
@ -145,6 +147,7 @@ class RosterManager extends XmppManagerBase {
name: item.attributes["name"],
jid: item.attributes["jid"]!,
subscription: item.attributes["subscription"]!,
ask: item.attributes["ask"],
groups: item.findTags("group").map((groupNode) => groupNode.innerText()).toList()
)).toList();