You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
322 lines
10 KiB
Dart
322 lines
10 KiB
Dart
import 'package:collection/collection.dart';
|
|
import 'package:fluffychat/config/app_config.dart';
|
|
import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart';
|
|
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
|
import 'package:fluffychat/utils/localized_exception_extension.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
|
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
|
import 'package:matrix/matrix.dart';
|
|
|
|
import '../../../widgets/matrix.dart';
|
|
import '../../utils/firebase_analytics.dart';
|
|
import 'add_class_and_invite.dart';
|
|
|
|
//PTODO - auto invite students when you add a space and delete the add_class_and_invite.dart file
|
|
class AddToSpaceToggles extends StatefulWidget {
|
|
final String? roomId;
|
|
final bool startOpen;
|
|
final String? activeSpaceId;
|
|
final AddToClassMode mode;
|
|
|
|
const AddToSpaceToggles({
|
|
super.key,
|
|
this.roomId,
|
|
this.startOpen = false,
|
|
this.activeSpaceId,
|
|
required this.mode,
|
|
});
|
|
|
|
@override
|
|
AddToSpaceState createState() => AddToSpaceState();
|
|
}
|
|
|
|
class AddToSpaceState extends State<AddToSpaceToggles> {
|
|
late Room? room;
|
|
late List<SuggestionStatus> parents;
|
|
late List<Room> possibleParents;
|
|
late bool isOpen;
|
|
|
|
AddToSpaceState({Key? key});
|
|
|
|
@override
|
|
void initState() {
|
|
//if roomId is null, it means this widget is being used in the creation flow
|
|
room = widget.roomId != null
|
|
? Matrix.of(context).client.getRoomById(widget.roomId!)
|
|
: null;
|
|
|
|
possibleParents = Matrix.of(context)
|
|
.client
|
|
.rooms
|
|
.where(
|
|
widget.mode == AddToClassMode.exchange
|
|
? (Room r) => r.isPangeaClass && widget.roomId != r.id
|
|
: (Room r) =>
|
|
(r.isPangeaClass || r.isExchange) && widget.roomId != r.id,
|
|
)
|
|
.toList();
|
|
|
|
parents = widget.roomId != null
|
|
? possibleParents
|
|
.where(
|
|
(r) =>
|
|
r.spaceChildren.any((room) => room.roomId == widget.roomId),
|
|
)
|
|
.map((r) => SuggestionStatus(false, r))
|
|
.cast<SuggestionStatus>()
|
|
.toList()
|
|
: [];
|
|
|
|
if (widget.activeSpaceId != null) {
|
|
final activeSpace =
|
|
Matrix.of(context).client.getRoomById(widget.activeSpaceId!);
|
|
if (activeSpace != null && activeSpace.canIAddSpaceChild(null)) {
|
|
parents.add(SuggestionStatus(true, activeSpace));
|
|
} else {
|
|
ErrorHandler.logError(
|
|
e: Exception('activeSpaceId ${widget.activeSpaceId} not found'),
|
|
);
|
|
}
|
|
}
|
|
|
|
//sort possibleParents
|
|
//if possibleParent in parents, put first
|
|
//use sort but use any instead of contains because contains uses == and we want to compare by id
|
|
possibleParents.sort((a, b) {
|
|
if (parents.any((suggestionStatus) => suggestionStatus.room.id == a.id)) {
|
|
return -1;
|
|
} else if (parents
|
|
.any((suggestionStatus) => suggestionStatus.room.id == b.id)) {
|
|
return 1;
|
|
} else {
|
|
return a.name.compareTo(b.name);
|
|
}
|
|
});
|
|
|
|
isOpen = widget.startOpen;
|
|
initSuggestedParents();
|
|
super.initState();
|
|
}
|
|
|
|
Future<void> initSuggestedParents() async {
|
|
if (room != null) {
|
|
for (var i = 0; i < parents.length; i++) {
|
|
final parent = parents[i];
|
|
final bool suggested =
|
|
await room?.suggestedInSpace(parent.room) ?? false;
|
|
parents[i].suggested = suggested;
|
|
}
|
|
setState(() {});
|
|
}
|
|
}
|
|
|
|
Future<void> _addSingleSpace(String roomToAddId, Room newParent) async {
|
|
GoogleAnalytics.addParent(roomToAddId, newParent.classCode);
|
|
await newParent.setSpaceChild(
|
|
roomToAddId,
|
|
suggested: isSuggestedInSpace(newParent),
|
|
);
|
|
await setSuggested(true, newParent);
|
|
}
|
|
|
|
Future<void> addSpaces(String roomToAddId) async {
|
|
final List<Future<void>> addFutures = [];
|
|
for (final SuggestionStatus newParent in parents) {
|
|
addFutures.add(_addSingleSpace(roomToAddId, newParent.room));
|
|
}
|
|
await addFutures.wait;
|
|
}
|
|
|
|
Future<void> handleAdd(bool add, Room possibleParent) async {
|
|
//in this case, the room has already been made so we handle adding as it happens
|
|
if (room != null) {
|
|
await showFutureLoadingDialog(
|
|
context: context,
|
|
future: () => add
|
|
? _addSingleSpace(room!.id, possibleParent)
|
|
: possibleParent.removeSpaceChild(room!.id),
|
|
onError: (e) {
|
|
// if error occurs, do not change value of toggle
|
|
add = !add;
|
|
return (e as Object?)?.toLocalizedString(context) ??
|
|
e?.toString() ??
|
|
L10n.of(context)!.oopsSomethingWentWrong;
|
|
},
|
|
);
|
|
}
|
|
|
|
setState(
|
|
() => add
|
|
? parents.add(SuggestionStatus(true, possibleParent))
|
|
: parents.removeWhere(
|
|
(suggestionStatus) =>
|
|
suggestionStatus.room.id == possibleParent.id,
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<void> setSuggested(bool suggest, Room possibleParent) async {
|
|
if (room != null) {
|
|
await showFutureLoadingDialog(
|
|
context: context,
|
|
future: () => room!.setSuggestedInSpace(suggest, possibleParent),
|
|
);
|
|
}
|
|
|
|
for (final SuggestionStatus suggestionStatus in parents) {
|
|
if (suggestionStatus.room.id == possibleParent.id) {
|
|
suggestionStatus.suggested = suggest;
|
|
}
|
|
}
|
|
|
|
setState(() {});
|
|
}
|
|
|
|
bool isSuggestedInSpace(Room parent) =>
|
|
parents.firstWhereOrNull((r) => r.room.id == parent.id)?.suggested ??
|
|
false;
|
|
|
|
Widget getAddToSpaceToggleItem(int index) {
|
|
final Room possibleParent = possibleParents[index];
|
|
final String possibleParentName = possibleParent.getLocalizedDisplayname();
|
|
final bool canAdd = possibleParent.canIAddSpaceChild(room);
|
|
|
|
return Opacity(
|
|
opacity: canAdd ? 1 : 0.5,
|
|
child: Column(
|
|
children: [
|
|
SwitchListTile.adaptive(
|
|
title: possibleParent.nameAndRoomTypeIcon(),
|
|
activeColor: AppConfig.activeToggleColor,
|
|
value: parents.any((r) => r.room.id == possibleParent.id),
|
|
onChanged: (bool add) => canAdd
|
|
? handleAdd(add, possibleParent)
|
|
: ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(L10n.of(context)!.noPermission),
|
|
),
|
|
),
|
|
),
|
|
AnimatedSize(
|
|
duration: const Duration(milliseconds: 300),
|
|
curve: Curves.easeInOut,
|
|
child: parents.any((r) => r.room.id == possibleParent.id)
|
|
? SwitchListTile.adaptive(
|
|
title: Row(
|
|
children: [
|
|
const SizedBox(width: 32),
|
|
Expanded(
|
|
child: Text(
|
|
L10n.of(context)!.suggestTo(possibleParentName),
|
|
style: TextStyle(
|
|
color: Theme.of(context).colorScheme.secondary,
|
|
),
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
subtitle: Row(
|
|
children: [
|
|
const SizedBox(width: 32),
|
|
Expanded(
|
|
child: Text(
|
|
widget.mode == AddToClassMode.chat
|
|
? L10n.of(context)!
|
|
.suggestChatDesc(possibleParentName)
|
|
: L10n.of(context)!.suggestExchangeDesc(
|
|
possibleParentName,
|
|
),
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
activeColor: AppConfig.activeToggleColor,
|
|
value: isSuggestedInSpace(possibleParent),
|
|
onChanged: (bool suggest) => canAdd
|
|
? setSuggested(suggest, possibleParent)
|
|
: ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(L10n.of(context)!.noPermission),
|
|
),
|
|
),
|
|
)
|
|
: Container(),
|
|
),
|
|
Divider(
|
|
height: 0.5,
|
|
color: Theme.of(context).colorScheme.secondary.withAlpha(25),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final String title = widget.mode == AddToClassMode.exchange
|
|
? L10n.of(context)!.addToClass
|
|
: L10n.of(context)!.addToClassOrExchange;
|
|
final String subtitle = widget.mode == AddToClassMode.exchange
|
|
? L10n.of(context)!.addToClassDesc
|
|
: L10n.of(context)!.addToClassOrExchangeDesc;
|
|
|
|
return Column(
|
|
children: [
|
|
ListTile(
|
|
title: Text(
|
|
title,
|
|
style: TextStyle(
|
|
color: Theme.of(context).colorScheme.secondary,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
subtitle: Text(subtitle),
|
|
leading: CircleAvatar(
|
|
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
|
foregroundColor: Theme.of(context).textTheme.bodyLarge!.color,
|
|
child: const Icon(Icons.workspaces_outlined),
|
|
),
|
|
trailing: Icon(
|
|
isOpen
|
|
? Icons.keyboard_arrow_down_outlined
|
|
: Icons.keyboard_arrow_right_outlined,
|
|
),
|
|
onTap: () {
|
|
setState(() => isOpen = !isOpen);
|
|
},
|
|
),
|
|
if (isOpen) ...[
|
|
const Divider(height: 1),
|
|
possibleParents.isNotEmpty
|
|
? Column(
|
|
children: possibleParents
|
|
.mapIndexed((index, _) => getAddToSpaceToggleItem(index))
|
|
.toList(),
|
|
)
|
|
: Center(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Text(
|
|
L10n.of(context)!.inNoSpaces,
|
|
style: TextStyle(
|
|
color: Theme.of(context).colorScheme.secondary,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
class SuggestionStatus {
|
|
bool suggested;
|
|
final Room room;
|
|
|
|
SuggestionStatus(this.suggested, this.room);
|
|
}
|