feat: Add account bundles

This commit is contained in:
Christian Pauly 2021-09-18 16:25:57 +02:00
parent 0e914be5b6
commit 31815a2fc2
4 changed files with 120 additions and 32 deletions

View File

@ -19,6 +19,7 @@ import 'package:uni_links/uni_links.dart';
import 'package:vrouter/vrouter.dart';
import '../main.dart';
import '../widgets/matrix.dart';
import '../../utils/account_bundles.dart';
import '../utils/matrix_sdk_extensions.dart/matrix_file_extension.dart';
import '../utils/url_launcher.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
@ -416,6 +417,61 @@ class ChatListController extends State<ChatList> {
i - (Matrix.of(context).hasComplexBundles ? 1 : 0);
});
void setActiveBundle(String bundle) => setState(() {
_activeSpaceId = null;
selectedRoomIds.clear();
Matrix.of(context).activeBundle = bundle;
});
void editBundlesForAccount(String userId) async {
final client = Matrix.of(context)
.widget
.clients[Matrix.of(context).getClientIndexByMatrixId(userId)];
final action = await showConfirmationDialog<EditBundleAction>(
context: context,
title: 'Edit bundles for this account',
actions: [
AlertDialogAction(
key: EditBundleAction.addToBundle,
label: 'Add to bundle',
),
if (Matrix.of(context).activeBundle != null)
AlertDialogAction(
key: EditBundleAction.removeFromBundle,
label: 'Remove from this bundle',
),
],
);
if (action == null) return;
switch (action) {
case EditBundleAction.addToBundle:
final bundle = await showTextInputDialog(
context: context,
title: 'Bundle name',
textFields: [DialogTextField(hintText: 'Bundle name')]);
if (bundle.isEmpty && bundle.single.isEmpty) return;
await showFutureLoadingDialog(
context: context,
future: () => client.setAccountBundle(bundle.single),
);
break;
case EditBundleAction.removeFromBundle:
await showFutureLoadingDialog(
context: context,
future: () =>
client.removeFromAccountBundle(Matrix.of(context).activeBundle),
);
}
}
void resetActiveBundle() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
setState(() {
Matrix.of(context).activeBundle = null;
});
});
}
@override
Widget build(BuildContext context) {
Matrix.of(context).navigatorContext = context;
@ -431,3 +487,5 @@ class ChatListController extends State<ChatList> {
return ChatListView(this);
}
}
enum EditBundleAction { addToBundle, removeFromBundle }

View File

@ -1,6 +1,8 @@
import 'dart:math';
import 'package:async/async.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:flutter/widgets.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/pages/chat_list.dart';
import 'package:fluffychat/widgets/connection_status_header.dart';
@ -23,18 +25,30 @@ class ChatListView extends StatelessWidget {
List<BottomNavigationBarItem> getBottomBarItems(BuildContext context) {
final displayClients = Matrix.of(context).hasComplexBundles
? Matrix.of(context).accountBundles[Matrix.of(context).activeBundle ??
Matrix.of(context).client.accountBundles.first.name]
Matrix.of(context).client.accountBundles.first.name] ??
[]
: Matrix.of(context).widget.clients;
if (displayClients.isEmpty) {
displayClients.addAll(Matrix.of(context).widget.clients);
controller.resetActiveBundle();
}
final items = displayClients.map((client) {
return BottomNavigationBarItem(
label: client.userID,
icon: FutureBuilder<Profile>(
future: client.ownProfile,
builder: (context, snapshot) {
return Avatar(
snapshot.data?.avatarUrl,
snapshot.data?.displayName ?? client.userID.localpart,
size: 32,
return InkWell(
borderRadius: BorderRadius.circular(32),
onTap: () => controller.setActiveClient(
Matrix.of(context).getClientIndexByMatrixId(client.userID)),
onLongPress: () =>
controller.editBundlesForAccount(client.userID),
child: Avatar(
snapshot.data?.avatarUrl,
snapshot.data?.displayName ?? client.userID.localpart,
size: 32,
),
);
}),
);
@ -50,11 +64,13 @@ class ChatListView extends StatelessWidget {
Icons.menu,
color: Theme.of(context).textTheme.bodyText1.color,
),
onSelected: controller.setActiveBundle,
itemBuilder: (context) => Matrix.of(context)
.accountBundles
.keys
.map(
(bundle) => PopupMenuItem(
value: bundle,
child: Text(bundle),
),
)
@ -264,22 +280,31 @@ class ChatListView extends StatelessWidget {
)
: null,
bottomNavigationBar: Matrix.of(context).isMultiAccount
? SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SizedBox(
width: max(
MediaQuery.of(context).size.width,
Matrix.of(context).widget.clients.length * 84.0,
),
child: BottomNavigationBar(
onTap: controller.setActiveClient,
currentIndex: Matrix.of(context).activeClient +
(Matrix.of(context).hasComplexBundles ? 1 : 0),
showUnselectedLabels: false,
showSelectedLabels: true,
type: BottomNavigationBarType.shifting,
selectedItemColor: Theme.of(context).primaryColor,
items: getBottomBarItems(context),
? StreamBuilder(
stream: StreamGroup.merge(Matrix.of(context)
.widget
.clients
.map((client) => client.onSync.stream.where((s) =>
s.accountData != null &&
s.accountData
.any((e) => e.type == accountBundlesType)))),
builder: (context, _) => SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SizedBox(
width: max(
MediaQuery.of(context).size.width,
Matrix.of(context).widget.clients.length * 84.0,
),
child: BottomNavigationBar(
onTap: controller.setActiveClient,
currentIndex: Matrix.of(context).activeClient +
(Matrix.of(context).hasComplexBundles ? 1 : 0),
showUnselectedLabels: false,
showSelectedLabels: true,
type: BottomNavigationBarType.shifting,
selectedItemColor: Theme.of(context).primaryColor,
items: getBottomBarItems(context),
),
),
),
)

View File

@ -43,13 +43,13 @@ class AccountBundle {
};
}
const _accountBundlesType = 'im.fluffychat.account_bundles';
const accountBundlesType = 'im.fluffychat.account_bundles';
extension AccountBundlesExtension on Client {
List<AccountBundle> get accountBundles {
List<AccountBundle> ret;
if (accountData.containsKey(_accountBundlesType)) {
ret = AccountBundles.fromJson(accountData[_accountBundlesType].content)
if (accountData.containsKey(accountBundlesType)) {
ret = AccountBundles.fromJson(accountData[accountBundlesType].content)
.bundles;
}
ret ??= [];
@ -63,9 +63,10 @@ extension AccountBundlesExtension on Client {
}
Future<void> setAccountBundle(String name, [int priority]) async {
final data = AccountBundles.fromJson(
accountData[_accountBundlesType]?.content ?? {});
final data =
AccountBundles.fromJson(accountData[accountBundlesType]?.content ?? {});
var foundBundle = false;
data.bundles ??= [];
for (final bundle in data.bundles) {
if (bundle.name == name) {
bundle.priority = priority;
@ -76,22 +77,23 @@ extension AccountBundlesExtension on Client {
if (!foundBundle) {
data.bundles.add(AccountBundle(name: name, priority: priority));
}
await setAccountData(userID, _accountBundlesType, data.toJson());
await setAccountData(userID, accountBundlesType, data.toJson());
}
Future<void> removeFromAccountBundle(String name) async {
if (!accountData.containsKey(_accountBundlesType)) {
if (!accountData.containsKey(accountBundlesType)) {
return; // nothing to do
}
final data =
AccountBundles.fromJson(accountData[_accountBundlesType].content);
AccountBundles.fromJson(accountData[accountBundlesType].content);
if (data.bundles == null) return;
data.bundles.removeWhere((b) => b.name == name);
await setAccountData(userID, _accountBundlesType, data.toJson());
await setAccountData(userID, accountBundlesType, data.toJson());
}
String get sendPrefix {
final data = AccountBundles.fromJson(
accountData[_accountBundlesType]?.content ?? {});
final data =
AccountBundles.fromJson(accountData[accountBundlesType]?.content ?? {});
return data.prefix;
}
}

View File

@ -70,6 +70,9 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
bool get isMultiAccount => widget.clients.length > 1;
int getClientIndexByMatrixId(String matrixId) =>
widget.clients.indexWhere((client) => client.userID == matrixId);
int get _safeActiveClient {
if (activeClient < 0 || activeClient >= widget.clients.length) {
return 0;