usability test updates - don't stack space details, always suggest new chats in spaces

pull/1183/head
ggurdin 1 year ago
parent d29d5ce379
commit f15d79179d

@ -3674,7 +3674,7 @@
"bestAnswerFeedback": "That's correct!",
"definitionDefaultPrompt": "What does this word mean?",
"practiceDefaultPrompt": "What is the best answer?",
"correctionDefaultPrompt": "What is the best correction?",
"correctionDefaultPrompt": "What is the best replacement?",
"itStartDefaultPrompt": "Do you want help translating?",
"languageLevelWarning": "Please select a class language level",
"lockedChatWarning": "🔒 This chat has been locked",

@ -26,7 +26,6 @@ import 'package:fluffychat/pages/settings_security/settings_security.dart';
import 'package:fluffychat/pages/settings_style/settings_style.dart';
import 'package:fluffychat/pangea/guard/p_vguard.dart';
import 'package:fluffychat/pangea/pages/analytics/student_analytics/student_analytics.dart';
import 'package:fluffychat/pangea/pages/class_settings/class_settings_page.dart';
import 'package:fluffychat/pangea/pages/exchange/add_exchange_to_class.dart';
import 'package:fluffychat/pangea/pages/find_partner/find_partner.dart';
import 'package:fluffychat/pangea/pages/p_user_age/p_user_age.dart';
@ -150,19 +149,6 @@ abstract class AppRoutes {
: child,
),
routes: [
// #Pangea
GoRoute(
path: '/spaces/:roomid',
pageBuilder: (context, state) => defaultPageBuilder(
context,
state,
ChatDetails(
roomId: state.pathParameters['roomid']!,
),
),
redirect: loggedOutRedirect,
),
// Pangea#
GoRoute(
path: '/rooms',
redirect: loggedOutRedirect,
@ -521,17 +507,6 @@ abstract class AppRoutes {
),
redirect: loggedOutRedirect,
),
// #Pangea
GoRoute(
path: 'class_settings',
pageBuilder: (context, state) => defaultPageBuilder(
context,
state,
const ClassSettingsPage(),
),
redirect: loggedOutRedirect,
),
// Pangea#
GoRoute(
path: 'invite',
pageBuilder: (context, state) => defaultPageBuilder(

@ -481,7 +481,16 @@ class ChatController extends State<ChatPageWithRoom>
}
// Do not send read markers when app is not in foreground
if (kIsWeb && !Matrix.of(context).webHasFocus) return;
// #Pangea
try {
// Pangea#
if (kIsWeb && !Matrix.of(context).webHasFocus) return;
// #Pangea
} catch (err, s) {
ErrorHandler.logError(e: err, s: s);
return;
}
// Pangea#
if (!kIsWeb &&
WidgetsBinding.instance.lifecycleState != AppLifecycleState.resumed) {
return;

@ -61,11 +61,22 @@ class ChatDetailsView extends StatelessWidget {
);
return Scaffold(
appBar: AppBar(
leading: controller.widget.embeddedCloseButton ??
const Center(child: BackButton()),
leading:
// #Pangea
!room.isSpace
?
// Pangea#
controller.widget.embeddedCloseButton ??
const Center(child: BackButton())
// #Pangea
: BackButton(
onPressed: () => context.go("/rooms"),
)
// Pangea#
,
elevation: Theme.of(context).appBarTheme.elevation,
actions: <Widget>[
// #Pangeas
// #Pangea
//if (room.canonicalAlias.isNotEmpty)
// IconButton(
// tooltip: L10n.of(context)!.share,

@ -75,7 +75,12 @@ class ChatListView extends StatelessWidget {
label: L10n.of(context)!.allChats,
// Pangea#
),
if (controller.spaces.isNotEmpty)
if (controller.spaces.isNotEmpty
// #Pangea
&&
!FluffyThemes.isColumnMode(context)
// Pangea#
)
// #Pangea
// const NavigationDestination(
// icon: Icon(Icons.workspaces_outlined),

@ -2,6 +2,7 @@ import 'dart:async';
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:collection/collection.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/chat_list/chat_list.dart';
import 'package:fluffychat/pages/chat_list/chat_list_item.dart';
import 'package:fluffychat/pages/chat_list/search_title.dart';
@ -173,7 +174,7 @@ class _SpaceViewState extends State<SpaceView> {
if (spaceChild.roomId == widget.controller.activeSpaceId) {
// #Pangea
// context.go('/rooms/${spaceChild.roomId}');
context.push('/spaces/${spaceChild.roomId}');
context.go('/rooms/${spaceChild.roomId}/details');
// Pangea#
} else {
widget.controller.setActiveSpace(spaceChild.roomId);
@ -545,12 +546,25 @@ class _SpaceViewState extends State<SpaceView> {
titleSpacing: 0,
title: ListTile(
leading: BackButton(
onPressed: () =>
widget.controller.setActiveSpace(parentSpace?.id),
// #Pangea
onPressed: () {
!FluffyThemes.isColumnMode(context) ||
parentSpace?.id != null
? widget.controller.setActiveSpace(parentSpace?.id)
: widget.controller.onDestinationSelected(0);
},
// onPressed: () =>
// widget.controller.setActiveSpace(parentSpace?.id),
// Pangea#
),
title: Text(
parentSpace == null
? L10n.of(context)!.allSpaces
// #Pangea
// ? L10n.of(context)!.allSpaces
? !FluffyThemes.isColumnMode(context)
? L10n.of(context)!.allSpaces
: L10n.of(context)!.allChats
// Pangea#
: parentSpace.getLocalizedDisplayname(
MatrixLocals(L10n.of(context)!),
),
@ -721,8 +735,8 @@ class _SpaceViewState extends State<SpaceView> {
),
// #Pangea
// onTap: () => _onJoinSpaceChild(spaceChild),
onTap: () => context.push(
'/spaces/${spaceChild.roomId}',
onTap: () => context.go(
'/rooms/${spaceChild.roomId}/details',
),
// Pangea#
),

@ -91,7 +91,7 @@ class NewGroupView extends StatelessWidget {
const Divider(height: 1),
AddToSpaceToggles(
key: controller.addToSpaceKey,
startOpen: false,
startOpen: true,
activeSpaceId: controller.activeSpaceId,
mode: AddToClassMode.chat,
),

@ -216,7 +216,7 @@ class NewSpaceController extends State<NewSpace> {
);
MatrixState.pangeaController.classController
.setActiveSpaceIdInChatListController(spaceId);
context.push('/spaces/$spaceId');
context.go("/rooms/$spaceId/details");
return;
}
@ -245,7 +245,7 @@ class NewSpaceController extends State<NewSpace> {
// context.pop<String>(spaceId);
MatrixState.pangeaController.classController
.setActiveSpaceIdInChatListController(spaceId);
context.push('/spaces/$spaceId');
context.go("/rooms/$spaceId/details");
// Pangea#
} catch (e) {
setState(() {

@ -138,7 +138,7 @@ class NewSpaceView extends StatelessWidget {
if (!controller.newClassMode)
AddToSpaceToggles(
key: controller.addToSpaceKey,
startOpen: false,
startOpen: true,
mode: !controller.newClassMode
? AddToClassMode.exchange
: AddToClassMode.chat,

@ -1,96 +0,0 @@
import 'dart:async';
import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/widgets/layouts/empty_page.dart';
import '../../../widgets/matrix.dart';
import '../../utils/error_handler.dart';
import '../../utils/set_class_name.dart';
import '../../widgets/space/class_settings.dart';
import 'class_settings_view.dart';
import 'p_class_widgets/room_rules_editor.dart';
class ClassSettingsPage extends StatefulWidget {
const ClassSettingsPage({super.key});
@override
State<ClassSettingsPage> createState() => ClassSettingsController();
}
class ClassSettingsController extends State<ClassSettingsPage> {
PangeaController pangeaController = MatrixState.pangeaController;
final GlobalKey<RoomRulesState> rulesEditorKey = GlobalKey<RoomRulesState>();
final GlobalKey<ClassSettingsState> classSettingsKey =
GlobalKey<ClassSettingsState>();
Room? room;
String? get roomId => GoRouterState.of(context).pathParameters['roomid'];
Future<void> handleSave(BuildContext context) async {
if (classSettingsKey.currentState!.sameLanguages) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(L10n.of(context)!.noIdenticalLanguages),
),
);
return;
}
if (rulesEditorKey.currentState != null) {
await rulesEditorKey.currentState?.setRoomRules(roomId!);
} else {
debugger(when: kDebugMode);
ErrorHandler.logError(m: "Null rules editor state");
}
if (classSettingsKey.currentState != null) {
await classSettingsKey.currentState?.setClassSettings(
roomId!,
);
} else {
debugger(when: kDebugMode);
ErrorHandler.logError(m: "Null class settings state");
}
}
void goback(BuildContext context) {
context.push("/spaces/$roomId");
}
String get className =>
Matrix.of(context).client.getRoomById(roomId!)?.name ?? '';
@override
void initState() {
// TODO: implement initState
super.initState();
Future.delayed(Duration.zero, () {
room = Matrix.of(context).client.getRoomById(roomId!);
if (room == null) {
debugger(when: kDebugMode);
context.pop();
}
setState(() {});
});
}
//PTODO - show loading widget
void setDisplaynameAction() => setClassDisplayname(context, roomId);
bool showEditNameIcon = false;
void hoverEditNameIcon(bool hovering) =>
setState(() => showEditNameIcon = !showEditNameIcon);
@override
Widget build(BuildContext context) => room != null
? ClassSettingsPageView(controller: this)
: const EmptyPage();
}

@ -1,85 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:go_router/go_router.dart';
import 'package:fluffychat/pangea/pages/class_settings/class_settings_page.dart';
import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/room_rules_editor.dart';
import 'package:fluffychat/pangea/widgets/space/class_settings.dart';
import '../../../widgets/layouts/max_width_body.dart';
class ClassSettingsPageView extends StatelessWidget {
final ClassSettingsController controller;
const ClassSettingsPageView({super.key, required this.controller});
@override
Widget build(BuildContext context) {
debugPrint("in class settings page with roomId ${controller.roomId}");
// PTODO-Lala - make the page scrollable anywhere, not just in the area of the elements
// so like, the user should be able scroll using the mouse wheel from anywhere within this view
// currently, your cursor needs be horizontally within the tiles in order to scroll
return Scaffold(
appBar: AppBar(
leading: GoRouterState.of(context).path?.startsWith('/spaces/') ?? false
? null
: IconButton(
icon: const Icon(Icons.close_outlined),
onPressed: () => controller.goback(context),
),
centerTitle: true,
title: Text(L10n.of(context)!.classSettings),
),
body: ListView(
children: [
MaxWidthBody(
child: ListTile(
title: Center(
child: TextButton.icon(
onPressed: controller.setDisplaynameAction,
onHover: controller.hoverEditNameIcon,
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 25),
),
label: Visibility(
visible: controller.showEditNameIcon,
child: Icon(
Icons.edit,
color: Theme.of(context).colorScheme.onBackground,
),
),
icon: Text(
controller.className,
style: TextStyle(
color: Theme.of(context).colorScheme.secondary,
fontWeight: FontWeight.bold,
),
),
),
),
),
),
MaxWidthBody(
child: Column(
children: [
ClassSettings(
roomId: controller.roomId,
startOpen: true,
),
RoomRulesEditor(roomId: controller.roomId),
],
),
),
],
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () => showFutureLoadingDialog(
context: context,
future: () => controller.handleSave(context),
),
label: Text(L10n.of(context)!.saveChanges),
icon: const Icon(Icons.save_outlined),
),
);
}
}

@ -1,41 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:go_router/go_router.dart';
class ClassSettingsButton extends StatelessWidget {
const ClassSettingsButton({super.key});
// final PangeaController _pangeaController = MatrixState.pangeaController;
@override
Widget build(BuildContext context) {
// final roomId = GoRouterState.of(context).pathParameters['roomid'];
final iconColor = Theme.of(context).textTheme.bodyLarge!.color;
return Column(
children: [
ListTile(
// enabled: roomId != null &&
// _pangeaController.classController
// .getClassModelBySpaceIdLocal(roomId) !=
// null,
title: Text(
L10n.of(context)!.classSettings,
style: TextStyle(
color: Theme.of(context).colorScheme.secondary,
fontWeight: FontWeight.bold,
),
),
subtitle: Text(L10n.of(context)!.classSettingsDesc),
leading: CircleAvatar(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
foregroundColor: iconColor,
child: const Icon(Icons.settings_outlined),
),
onTap: () => context.go('/class_settings'),
),
],
);
}
}

@ -1,122 +0,0 @@
import 'dart:developer';
import 'package:flutter/foundation.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:go_router/go_router.dart';
import 'package:matrix/matrix.dart' as sdk;
import 'package:matrix/matrix.dart';
import 'package:fluffychat/pangea/pages/new_class/new_class_view.dart';
import 'package:fluffychat/pangea/utils/class_code.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:fluffychat/widgets/matrix.dart';
import '../../controllers/pangea_controller.dart';
import '../../widgets/space/class_settings.dart';
import '../class_settings/p_class_widgets/room_rules_editor.dart';
class NewClass extends StatefulWidget {
const NewClass({super.key});
@override
NewClassController createState() => NewClassController();
}
class NewClassController extends State<NewClass> {
TextEditingController controller = TextEditingController();
final PangeaController pangeaController = MatrixState.pangeaController;
final GlobalKey<RoomRulesState> rulesEditorKey = GlobalKey<RoomRulesState>();
final GlobalKey<ClassSettingsState> classSettingsKey =
GlobalKey<ClassSettingsState>();
void submitAction([_]) async {
//TODO: validate that object is complete
final matrix = Matrix.of(context);
if (controller.text.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(L10n.of(context)!.classNameRequired),
),
);
return;
}
if (classSettingsKey.currentState == null) {
debugger(when: kDebugMode);
}
if (classSettingsKey.currentState!.sameLanguages) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(L10n.of(context)!.noIdenticalLanguages),
),
);
return;
}
final roomID = await showFutureLoadingDialog(
context: context,
future: () async {
final String roomID = await matrix.client.createRoom(
//PTODO - investigate effects of changing visibility from public
preset: sdk.CreateRoomPreset.publicChat,
creationContent: {
'type': RoomCreationTypes.mSpace,
},
visibility: sdk.Visibility.public,
// roomAliasName: controller.text.isNotEmpty
// ? "${matrix.client.userID!.localpart}-${controller.text.trim().toLowerCase().replaceAll(' ', '_')}"
// : null,
roomAliasName: ClassCodeUtil.generateClassCode(),
name: controller.text.isNotEmpty ? controller.text : null,
);
if (rulesEditorKey.currentState != null) {
await rulesEditorKey.currentState!.setRoomRules(roomID);
} else {
debugger(when: kDebugMode);
ErrorHandler.logError(m: "Null rules editor state");
}
if (classSettingsKey.currentState != null) {
await classSettingsKey.currentState!.setClassSettings(
roomID,
);
} else {
debugger(when: kDebugMode);
ErrorHandler.logError(m: "Null class settings state");
}
return roomID;
},
onError: (e) {
debugger(when: kDebugMode);
return e;
},
);
if (roomID.error == null && roomID.result is String) {
pangeaController.classController.setActiveSpaceIdInChatListController(
roomID.result!,
);
context.push('/spaces/${roomID.result!}');
} else {
debugger(when: kDebugMode);
ErrorHandler.logError(e: roomID.error, s: StackTrace.current);
}
}
@override
void initState() {
// TODO: implement initState
super.initState();
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) => NewSpaceView(this);
}

@ -1,88 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/pangea/constants/class_default_values.dart';
import 'package:fluffychat/pangea/pages/new_class/new_class.dart';
import 'package:fluffychat/widgets/layouts/max_width_body.dart';
import '../../widgets/space/class_settings.dart';
import '../class_settings/p_class_widgets/room_rules_editor.dart';
class NewSpaceView extends StatelessWidget {
// #Pangea
// final NewSpaceController controller;
final NewClassController controller;
// Pangea#
const NewSpaceView(this.controller, {super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// #Pangea
centerTitle: true,
// Pangea#
title: Text(L10n.of(context)!.createNewClass),
),
body: MaxWidthBody(
// #Pangea
child: ListView(
// child: Column(
// mainAxisSize: MainAxisSize.min,
// #Pangea
children: <Widget>[
Padding(
padding: const EdgeInsets.all(12.0),
child: TextField(
// #Pangea
maxLength: ClassDefaultValues.maxClassName,
maxLengthEnforcement: MaxLengthEnforcement.enforced,
// #Pangea
controller: controller.controller,
autofocus: true,
autocorrect: false,
textInputAction: TextInputAction.go,
onSubmitted: controller.submitAction,
decoration: InputDecoration(
labelText: L10n.of(context)!.spaceName,
prefixIcon: const Icon(Icons.people_outlined),
hintText: L10n.of(context)!.enterASpacepName,
),
),
),
// #Pangea
ClassSettings(
key: controller.classSettingsKey,
roomId: null,
startOpen: true,
),
RoomRulesEditor(
key: controller.rulesEditorKey,
roomId: null,
),
const SizedBox(height: 45),
// SwitchListTile.adaptive(
// title: Text(L10n.of(context)!.spaceIsPublic),
// value: controller.publicGroup,
// onChanged: controller.setPublicGroup,
// ),
// ListTile(
// trailing: const Padding(
// padding: EdgeInsets.symmetric(horizontal: 16.0),
// child: Icon(Icons.info_outlined),
// ),
// subtitle: Text(L10n.of(context)!.newSpaceDescription),
// ),
// #Pangea
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: controller.submitAction,
child: const Icon(Icons.arrow_forward_outlined),
),
);
}
}

@ -1,11 +1,9 @@
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/widgets/matrix.dart';
bool canAddToSpace(Room space, PangeaController pangeaController) {
final bool pangeaPermission =
pangeaController.permissionsController.canUserGroupChat(roomID: space.id);
@ -27,8 +25,9 @@ Future<void> pangeaAddToSpace(
Room space,
List<String> selectedRoomIds,
BuildContext context,
PangeaController pangeaController,
) async {
PangeaController pangeaController, {
bool suggested = true,
}) async {
if (!canAddToSpace(space, pangeaController)) {
throw L10n.of(context)!.noAddToSpacePermissions;
}
@ -37,6 +36,6 @@ Future<void> pangeaAddToSpace(
if (room != null && chatIsInSpace(room, space)) {
throw L10n.of(context)!.alreadyInSpace;
}
await space.setSpaceChild(roomId);
await space.setSpaceChild(roomId, suggested: suggested);
}
}

@ -21,7 +21,7 @@ void chatListHandleSpaceTap(
controller.setActiveSpace(space.id);
if (FluffyThemes.isColumnMode(context)) {
context.push('/spaces/${space.id}');
context.go('/rooms/${space.id}/details');
} else if (controller.activeChat != null &&
!space.isFirstOrSecondChild(controller.activeChat!)) {
context.go("/rooms");
@ -108,6 +108,9 @@ void chatListHandleSpaceTap(
showAlertDialog(context);
}
break;
case Membership.leave:
autoJoin(space);
break;
default:
setActiveSpaceAndCloseChat();
ErrorHandler.logError(

Loading…
Cancel
Save