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

View File

@ -16,4 +16,6 @@ class DBRosterItem {
late List<String> groups; late List<String> groups;
late String subscription; 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 { 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) { if (command.share) {
xmpp.getPresenceManager().sendSubscriptionRequestApproval(command.jid); if (item.ask == "subscribe") {
roster.acceptSubscriptionRequest(command.jid);
} else {
roster.sendSubscriptionRequest(command.jid);
}
} else { } 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.jid,
item.name ?? item.jid.split("@")[0], item.name ?? item.jid.split("@")[0],
item.subscription, item.subscription,
item.ask ?? "",
groups: item.groups groups: item.groups
); );
@ -102,6 +103,7 @@ Future<RosterDiffEvent> rosterDiff(List<RosterItem> currentRoster, List<XmppRost
item.jid, item.jid,
item.jid.split("@")[0], item.jid.split("@")[0],
item.subscription, item.subscription,
item.ask ?? "",
groups: item.groups groups: item.groups
)); ));
} }
@ -134,8 +136,13 @@ class RosterService {
/// and, if it was successful, create the database entry. Returns the /// and, if it was successful, create the database entry. Returns the
/// [RosterItem] model object. /// [RosterItem] model object.
Future<RosterItem> addToRosterWrapper(String avatarUrl, String jid, String title) async { Future<RosterItem> addToRosterWrapper(String avatarUrl, String jid, String title) async {
// TODO: Correct? final item = await GetIt.I.get<DatabaseService>().addRosterItemFromData(
final item = await GetIt.I.get<DatabaseService>().addRosterItemFromData(avatarUrl, jid, title, "to"); avatarUrl,
jid,
title,
"none",
""
);
final result = await GetIt.I.get<XmppConnection>().getRosterManager().addToRoster(jid, title); final result = await GetIt.I.get<XmppConnection>().getRosterManager().addToRoster(jid, title);
if (!result) { if (!result) {
// TODO: Signal error? // 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. // Indicates, if [jid] is a regular user, if the user is in the roster.
bool inRoster, bool inRoster,
// The subscription state of the roster item // The subscription state of the roster item
String? subscription String subscription
) = _Conversation; ) = _Conversation;
// JSON // JSON

View File

@ -6,16 +6,18 @@ class RosterItem extends Equatable {
final String jid; final String jid;
final String title; final String title;
final String subscription; final String subscription;
final String ask;
final List<String> groups; final List<String> groups;
final int id; 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) RosterItem.fromJson(Map<String, dynamic> json)
: avatarUrl = json["avatarUrl"], : avatarUrl = json["avatarUrl"],
jid = json["jid"], jid = json["jid"],
title = json["title"], title = json["title"],
subscription = json["subscription"], subscription = json["subscription"],
ask = json["ask"],
groups = List<String>.from(json["groups"]!), groups = List<String>.from(json["groups"]!),
id = json["id"]; id = json["id"];
@ -24,6 +26,7 @@ class RosterItem extends Equatable {
"jid": jid, "jid": jid,
"title": title, "title": title,
"subscription": subscription, "subscription": subscription,
"ask": ask,
"groups": groups, "groups": groups,
"id": id "id": id
}; };
@ -32,5 +35,5 @@ class RosterItem extends Equatable {
bool get stringify => true; bool get stringify => true;
@override @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<ProfilePageRequestedEvent>(_onProfileRequested);
on<ConversationUpdatedEvent>(_onConversationUpdated); on<ConversationUpdatedEvent>(_onConversationUpdated);
on<AvatarSetEvent>(_onAvatarSet); on<AvatarSetEvent>(_onAvatarSet);
on<SetSubscriptionStateEvent>(_onSetSubscriptionState);
} }
Future<void> _onProfileRequested(ProfilePageRequestedEvent event, Emitter<ProfileState> emit) async { Future<void> _onProfileRequested(ProfilePageRequestedEvent event, Emitter<ProfileState> emit) async {
@ -71,4 +72,13 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
awaitable: false 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); 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)), padding: const EdgeInsets.symmetric(horizontal: 32.0).add(EdgeInsets.only(top: 8.0)),
child: SettingsTile.switchTile( child: SettingsTile.switchTile(
title: "Share online status", 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", switchValue: state.conversation!.subscription == "to" || state.conversation!.subscription == "both",
onToggle: (value) { onToggle: (value) {
GetIt.I.get<BackgroundServiceDataSender>().sendData( context.read<ProfileBloc>().add(
SetShareOnlineStatusCommand(jid: state.conversation!.jid, share: value), SetSubscriptionStateEvent(
awaitable: false state.conversation!.jid,
value
)
); );
} }
) )

View File

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