4152 onboarding tweaks (#4163)
* filter courses by short lang codes * reduce padding in course details page * update home / registration routes * refactor: replace find your people page with new course page from onboarding
This commit is contained in:
parent
783085d44c
commit
a9510d26f0
20 changed files with 691 additions and 1235 deletions
|
|
@ -37,13 +37,12 @@ import 'package:fluffychat/pangea/analytics_summary/progress_indicators_enum.dar
|
|||
import 'package:fluffychat/pangea/chat_settings/pages/edit_course.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/pages/pangea_invitation_selection.dart';
|
||||
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
|
||||
import 'package:fluffychat/pangea/course_creation/new_course_page.dart';
|
||||
import 'package:fluffychat/pangea/course_creation/selected_course_page.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/find_your_people/find_your_people.dart';
|
||||
import 'package:fluffychat/pangea/find_your_people/find_your_people_constants.dart';
|
||||
import 'package:fluffychat/pangea/guard/p_vguard.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/pages/settings_learning.dart';
|
||||
import 'package:fluffychat/pangea/login/pages/create_pangea_account_page.dart';
|
||||
import 'package:fluffychat/pangea/login/pages/language_selection_page.dart';
|
||||
import 'package:fluffychat/pangea/login/pages/login_or_signup_view.dart';
|
||||
import 'package:fluffychat/pangea/login/pages/new_trip_page.dart';
|
||||
|
|
@ -72,7 +71,7 @@ abstract class AppRoutes {
|
|||
// Matrix.of(context).widget.clients.any((client) => client.isLogged())
|
||||
// ? '/rooms'
|
||||
// : null;
|
||||
return PAuthGaurd.loggedInRedirect(context, state);
|
||||
return PAuthGaurd.homeRedirect(context, state);
|
||||
// Pangea#
|
||||
}
|
||||
|
||||
|
|
@ -84,7 +83,7 @@ abstract class AppRoutes {
|
|||
// Matrix.of(context).widget.clients.any((client) => client.isLogged())
|
||||
// ? null
|
||||
// : '/home';
|
||||
return PAuthGaurd.loggedOutRedirect(context, state);
|
||||
return PAuthGaurd.roomsRedirect(context, state);
|
||||
// Pangea#
|
||||
}
|
||||
|
||||
|
|
@ -130,20 +129,18 @@ abstract class AppRoutes {
|
|||
state,
|
||||
const Login(withEmail: true),
|
||||
),
|
||||
redirect: loggedInRedirect,
|
||||
),
|
||||
],
|
||||
// Pangea#
|
||||
),
|
||||
// #Pangea
|
||||
GoRoute(
|
||||
path: 'languages',
|
||||
path: 'signup',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const LanguageSelectionPage(),
|
||||
),
|
||||
redirect: loggedInRedirect,
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: ':langcode',
|
||||
|
|
@ -154,7 +151,6 @@ abstract class AppRoutes {
|
|||
langCode: state.pathParameters['langcode']!,
|
||||
),
|
||||
),
|
||||
redirect: loggedInRedirect,
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: 'email',
|
||||
|
|
@ -166,7 +162,6 @@ abstract class AppRoutes {
|
|||
langCode: state.pathParameters['langcode']!,
|
||||
),
|
||||
),
|
||||
redirect: loggedInRedirect,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -192,6 +187,81 @@ abstract class AppRoutes {
|
|||
),
|
||||
),
|
||||
// #Pangea
|
||||
GoRoute(
|
||||
path: '/registration',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const LanguageSelectionPage(),
|
||||
),
|
||||
redirect: PAuthGaurd.onboardingRedirect,
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: 'course',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const PlanTripPage(route: 'registration'),
|
||||
),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: 'private',
|
||||
pageBuilder: (context, state) {
|
||||
return defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const PrivateTripPage(),
|
||||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: 'public',
|
||||
pageBuilder: (context, state) {
|
||||
return defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const PublicTripPage(),
|
||||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: 'own',
|
||||
pageBuilder: (context, state) {
|
||||
return defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const NewTripPage(route: 'registration'),
|
||||
);
|
||||
},
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: ':courseid',
|
||||
pageBuilder: (context, state) {
|
||||
return defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
SelectedCourse(
|
||||
state.pathParameters['courseid']!,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
GoRoute(
|
||||
path: ':langcode',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
CreatePangeaAccountPage(
|
||||
langCode: state.pathParameters['langcode']!,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
GoRoute(
|
||||
path: '/join_with_link',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
|
|
@ -210,84 +280,6 @@ abstract class AppRoutes {
|
|||
JoinWithAlias(alias: state.uri.queryParameters['alias']),
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/course',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const LanguageSelectionPage(),
|
||||
),
|
||||
redirect: loggedOutRedirect,
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: ':langcode',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
PlanTripPage(
|
||||
langCode: state.pathParameters['langcode']!,
|
||||
),
|
||||
),
|
||||
redirect: loggedOutRedirect,
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: 'private',
|
||||
pageBuilder: (context, state) {
|
||||
return defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
PrivateTripPage(
|
||||
langCode: state.pathParameters['langcode']!,
|
||||
),
|
||||
);
|
||||
},
|
||||
redirect: loggedOutRedirect,
|
||||
),
|
||||
GoRoute(
|
||||
path: 'public',
|
||||
pageBuilder: (context, state) {
|
||||
return defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
PublicTripPage(
|
||||
langCode: state.pathParameters['langcode']!,
|
||||
),
|
||||
);
|
||||
},
|
||||
redirect: loggedOutRedirect,
|
||||
),
|
||||
GoRoute(
|
||||
path: 'own',
|
||||
pageBuilder: (context, state) {
|
||||
return defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
NewTripPage(
|
||||
langCode: state.pathParameters['langcode']!,
|
||||
),
|
||||
);
|
||||
},
|
||||
redirect: loggedOutRedirect,
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: ':courseid',
|
||||
pageBuilder: (context, state) {
|
||||
return defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
SelectedCourse(
|
||||
state.pathParameters['courseid']!,
|
||||
),
|
||||
);
|
||||
},
|
||||
redirect: loggedOutRedirect,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
// Pangea#
|
||||
ShellRoute(
|
||||
// Never use a transition on the shell route. Changing the PageBuilder
|
||||
|
|
@ -404,33 +396,55 @@ abstract class AppRoutes {
|
|||
// : child,
|
||||
// ),
|
||||
// routes: [
|
||||
// Pangea#
|
||||
GoRoute(
|
||||
path: 'communities',
|
||||
redirect: loggedOutRedirect,
|
||||
path: 'course',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const FindYourPeople(),
|
||||
const PlanTripPage(route: 'rooms'),
|
||||
),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: 'newcourse',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const NewCourse(),
|
||||
),
|
||||
redirect: loggedOutRedirect,
|
||||
path: 'private',
|
||||
pageBuilder: (context, state) {
|
||||
return defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const PrivateTripPage(),
|
||||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: 'public',
|
||||
pageBuilder: (context, state) {
|
||||
return defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const PublicTripPage(),
|
||||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: 'own',
|
||||
pageBuilder: (context, state) {
|
||||
return defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const NewTripPage(route: 'rooms'),
|
||||
);
|
||||
},
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: ':courseId',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
SelectedCourse(state.pathParameters['courseId']!),
|
||||
),
|
||||
redirect: loggedOutRedirect,
|
||||
path: ':courseid',
|
||||
pageBuilder: (context, state) {
|
||||
return defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
SelectedCourse(
|
||||
state.pathParameters['courseid']!,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -776,7 +790,8 @@ abstract class AppRoutes {
|
|||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
NewCourse(
|
||||
NewTripPage(
|
||||
route: 'rooms',
|
||||
spaceId: state.pathParameters['spaceid']!,
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,162 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
|
||||
import 'package:fluffychat/pangea/course_creation/course_plan_filter_widget.dart';
|
||||
import 'package:fluffychat/pangea/course_creation/course_plan_tile_widget.dart';
|
||||
import 'package:fluffychat/pangea/course_creation/course_search_provider.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/models/language_model.dart';
|
||||
import 'package:fluffychat/widgets/layouts/max_width_body.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class NewCourse extends StatefulWidget {
|
||||
final String? spaceId;
|
||||
const NewCourse({
|
||||
super.key,
|
||||
this.spaceId,
|
||||
});
|
||||
|
||||
@override
|
||||
State<NewCourse> createState() => NewCourseController();
|
||||
}
|
||||
|
||||
class NewCourseController extends State<NewCourse> with CourseSearchProvider {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const double titleFontSize = 16.0;
|
||||
const double descFontSize = 12.0;
|
||||
|
||||
const double iconSize = 12.0;
|
||||
final spaceId = widget.spaceId;
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
spaceId != null
|
||||
? L10n.of(context).addCoursePlan
|
||||
: L10n.of(context).newCourse,
|
||||
),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: MaxWidthBody(
|
||||
showBorder: false,
|
||||
withScrolling: false,
|
||||
maxWidth: 500.0,
|
||||
child: Column(
|
||||
spacing: 12.0,
|
||||
children: [
|
||||
Text(
|
||||
L10n.of(context).newCourseSubtitle,
|
||||
style: const TextStyle(
|
||||
fontSize: titleFontSize,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsetsGeometry.symmetric(
|
||||
vertical: 4.0,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Wrap(
|
||||
spacing: 8.0,
|
||||
runSpacing: 8.0,
|
||||
alignment: WrapAlignment.start,
|
||||
children: [
|
||||
CoursePlanFilter<LanguageModel>(
|
||||
value: instructionLanguageFilter,
|
||||
onChanged: setInstructionLanguageFilter,
|
||||
items: MatrixState
|
||||
.pangeaController.pLanguageStore.baseOptions,
|
||||
displayname: (v) =>
|
||||
v.getDisplayName(context) ?? v.displayName,
|
||||
enableSearch: true,
|
||||
defaultName:
|
||||
L10n.of(context).languageOfInstructionsLabel,
|
||||
shortName: L10n.of(context).allLanguages,
|
||||
),
|
||||
CoursePlanFilter<LanguageModel>(
|
||||
value: targetLanguageFilter,
|
||||
onChanged: setTargetLanguageFilter,
|
||||
items: MatrixState
|
||||
.pangeaController.pLanguageStore.targetOptions,
|
||||
displayname: (v) =>
|
||||
v.getDisplayName(context) ?? v.displayName,
|
||||
enableSearch: true,
|
||||
defaultName: L10n.of(context).targetLanguageLabel,
|
||||
shortName: L10n.of(context).allLanguages,
|
||||
),
|
||||
CoursePlanFilter<LanguageLevelTypeEnum>(
|
||||
value: languageLevelFilter,
|
||||
onChanged: setLanguageLevelFilter,
|
||||
items: LanguageLevelTypeEnum.values,
|
||||
displayname: (v) => v.string,
|
||||
defaultName: L10n.of(context).cefrLevelLabel,
|
||||
shortName: L10n.of(context).allCefrLevels,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Builder(
|
||||
builder: (context) {
|
||||
if (error != null) {
|
||||
return Center(
|
||||
child: ErrorIndicator(
|
||||
message: L10n.of(context).failedToLoadCourses,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator.adaptive(),
|
||||
);
|
||||
}
|
||||
|
||||
if (courses.isEmpty) {
|
||||
return Center(
|
||||
child: Text(L10n.of(context).noCoursesFound),
|
||||
);
|
||||
}
|
||||
|
||||
return Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: courses.length,
|
||||
itemBuilder: (context, index) => Padding(
|
||||
padding: const EdgeInsetsGeometry.fromLTRB(
|
||||
4.0,
|
||||
4.0,
|
||||
4.0,
|
||||
16.0,
|
||||
),
|
||||
child: CoursePlanTile(
|
||||
course: courses[index],
|
||||
onTap: () => context.go(
|
||||
spaceId != null
|
||||
? "/rooms/spaces/$spaceId/addcourse/${courses[index].uuid}"
|
||||
: "/rooms/communities/newcourse/${courses[index].uuid}",
|
||||
),
|
||||
titleFontSize: titleFontSize,
|
||||
chipFontSize: descFontSize,
|
||||
chipIconSize: iconSize,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -10,7 +10,6 @@ import 'package:fluffychat/pangea/course_plans/course_plan_builder.dart';
|
|||
import 'package:fluffychat/pangea/course_plans/map_clipper.dart';
|
||||
import 'package:fluffychat/pangea/course_settings/pin_clipper.dart';
|
||||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||
import 'package:fluffychat/widgets/layouts/max_width_body.dart';
|
||||
|
||||
class SelectedCourseView extends StatelessWidget {
|
||||
final SelectedCourseController controller;
|
||||
|
|
@ -40,255 +39,266 @@ class SelectedCourseView extends StatelessWidget {
|
|||
: L10n.of(context).newCourse,
|
||||
),
|
||||
),
|
||||
body: CoursePlanBuilder(
|
||||
courseId: controller.widget.courseId,
|
||||
onNotFound: () => context.go("/rooms/communities/newcourse"),
|
||||
builder: (context, courseController) {
|
||||
final course = courseController.course;
|
||||
return MaxWidthBody(
|
||||
showBorder: false,
|
||||
withScrolling: false,
|
||||
maxWidth: 500.0,
|
||||
child: course == null
|
||||
? const Center(child: CircularProgressIndicator.adaptive())
|
||||
: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: ListView.builder(
|
||||
itemCount: course.loadedTopics.length + 1,
|
||||
itemBuilder: (context, index) {
|
||||
if (index == 0) {
|
||||
return Column(
|
||||
spacing: 8.0,
|
||||
children: [
|
||||
ClipPath(
|
||||
clipper: MapClipper(),
|
||||
child: ImageByUrl(
|
||||
imageUrl: course.imageUrl,
|
||||
width: 100.0,
|
||||
borderRadius:
|
||||
BorderRadius.circular(0.0),
|
||||
replacement: Container(
|
||||
width: 100.0,
|
||||
height: 100.0,
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.secondary,
|
||||
body: SafeArea(
|
||||
child: CoursePlanBuilder(
|
||||
courseId: controller.widget.courseId,
|
||||
onNotFound: () => context.go("/rooms/course/own"),
|
||||
builder: (context, courseController) {
|
||||
final course = courseController.course;
|
||||
return Container(
|
||||
alignment: Alignment.topCenter,
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 500.0),
|
||||
child: course == null
|
||||
? const Center(child: CircularProgressIndicator.adaptive())
|
||||
: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: ListView.builder(
|
||||
itemCount: course.loadedTopics.length + 1,
|
||||
itemBuilder: (context, index) {
|
||||
if (index == 0) {
|
||||
return Column(
|
||||
spacing: 8.0,
|
||||
children: [
|
||||
ClipPath(
|
||||
clipper: MapClipper(),
|
||||
child: ImageByUrl(
|
||||
imageUrl: course.imageUrl,
|
||||
width: 100.0,
|
||||
borderRadius:
|
||||
BorderRadius.circular(0.0),
|
||||
replacement: Container(
|
||||
width: 100.0,
|
||||
height: 100.0,
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
theme.colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
course.title,
|
||||
style: const TextStyle(
|
||||
fontSize: titleFontSize,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
course.description,
|
||||
style: const TextStyle(
|
||||
fontSize: descFontSize,
|
||||
),
|
||||
),
|
||||
CourseInfoChips(
|
||||
course,
|
||||
fontSize: descFontSize,
|
||||
iconSize: smallIconSize,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 4.0,
|
||||
bottom: 8.0,
|
||||
),
|
||||
child: Row(
|
||||
spacing: 4.0,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.map,
|
||||
size: largeIconSize,
|
||||
),
|
||||
Text(
|
||||
L10n.of(context).coursePlan,
|
||||
style: const TextStyle(
|
||||
fontSize: titleFontSize,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
index--;
|
||||
final topic = course.loadedTopics[index];
|
||||
return Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(vertical: 4.0),
|
||||
child: Row(
|
||||
spacing: 8.0,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ClipPath(
|
||||
clipper: PinClipper(),
|
||||
child: ImageByUrl(
|
||||
imageUrl: topic.imageUrl,
|
||||
width: 45.0,
|
||||
replacement: Container(
|
||||
width: 45.0,
|
||||
height: 45.0,
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.secondary,
|
||||
Text(
|
||||
course.title,
|
||||
style: const TextStyle(
|
||||
fontSize: titleFontSize,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Column(
|
||||
spacing: 4.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
topic.title,
|
||||
style: const TextStyle(
|
||||
fontSize: titleFontSize,
|
||||
),
|
||||
Text(
|
||||
course.description,
|
||||
style: const TextStyle(
|
||||
fontSize: descFontSize,
|
||||
),
|
||||
Text(
|
||||
topic.description,
|
||||
style: const TextStyle(
|
||||
fontSize: descFontSize,
|
||||
),
|
||||
),
|
||||
CourseInfoChips(
|
||||
course,
|
||||
fontSize: descFontSize,
|
||||
iconSize: smallIconSize,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 4.0,
|
||||
bottom: 8.0,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsetsGeometry
|
||||
.symmetric(
|
||||
vertical: 2.0,
|
||||
),
|
||||
child: Row(
|
||||
spacing: 8.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (topic.location != null)
|
||||
CourseInfoChip(
|
||||
icon: Icons.location_on,
|
||||
text: topic.location!,
|
||||
fontSize: descFontSize,
|
||||
iconSize: smallIconSize,
|
||||
),
|
||||
CourseInfoChip(
|
||||
icon:
|
||||
Icons.event_note_outlined,
|
||||
text: L10n.of(context)
|
||||
.numActivityPlans(
|
||||
topic.loadedActivities
|
||||
.length,
|
||||
),
|
||||
fontSize: descFontSize,
|
||||
iconSize: smallIconSize,
|
||||
child: Row(
|
||||
spacing: 4.0,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.map,
|
||||
size: largeIconSize,
|
||||
),
|
||||
Text(
|
||||
L10n.of(context).coursePlan,
|
||||
style: const TextStyle(
|
||||
fontSize: titleFontSize,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
index--;
|
||||
final topic = course.loadedTopics[index];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 4.0,
|
||||
),
|
||||
child: Row(
|
||||
spacing: 8.0,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
ClipPath(
|
||||
clipper: PinClipper(),
|
||||
child: ImageByUrl(
|
||||
imageUrl: topic.imageUrl,
|
||||
width: 45.0,
|
||||
replacement: Container(
|
||||
width: 45.0,
|
||||
height: 45.0,
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
theme.colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Column(
|
||||
spacing: 4.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
topic.title,
|
||||
style: const TextStyle(
|
||||
fontSize: titleFontSize,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
topic.description,
|
||||
style: const TextStyle(
|
||||
fontSize: descFontSize,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsetsGeometry
|
||||
.symmetric(
|
||||
vertical: 2.0,
|
||||
),
|
||||
child: Row(
|
||||
spacing: 8.0,
|
||||
mainAxisSize:
|
||||
MainAxisSize.min,
|
||||
children: [
|
||||
if (topic.location != null)
|
||||
CourseInfoChip(
|
||||
icon: Icons.location_on,
|
||||
text: topic.location!,
|
||||
fontSize: descFontSize,
|
||||
iconSize: smallIconSize,
|
||||
),
|
||||
CourseInfoChip(
|
||||
icon: Icons
|
||||
.event_note_outlined,
|
||||
text: L10n.of(context)
|
||||
.numActivityPlans(
|
||||
topic.loadedActivities
|
||||
.length,
|
||||
),
|
||||
fontSize: descFontSize,
|
||||
iconSize: smallIconSize,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.surface,
|
||||
border: Border(
|
||||
top: BorderSide(
|
||||
color: theme.dividerColor,
|
||||
width: 1.0,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
spacing: 8.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
spacing: 12.0,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.edit,
|
||||
size: mediumIconSize,
|
||||
),
|
||||
Flexible(
|
||||
child: Text(
|
||||
L10n.of(context).editCourseLater,
|
||||
style:
|
||||
const TextStyle(fontSize: descFontSize),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
spacing: 12.0,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.shield,
|
||||
size: mediumIconSize,
|
||||
),
|
||||
Flexible(
|
||||
child: Text(
|
||||
L10n.of(context).newCourseAccess,
|
||||
style:
|
||||
const TextStyle(fontSize: descFontSize),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor:
|
||||
theme.colorScheme.primaryContainer,
|
||||
foregroundColor:
|
||||
theme.colorScheme.onPrimaryContainer,
|
||||
),
|
||||
onPressed: () => showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => spaceId != null
|
||||
? controller.addCourseToSpace(course)
|
||||
: controller.launchCourse(course),
|
||||
),
|
||||
child: Row(
|
||||
spacing: 8.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.map_outlined),
|
||||
Text(
|
||||
spaceId != null
|
||||
? L10n.of(context).addCoursePlan
|
||||
: L10n.of(context).createCourse,
|
||||
style: const TextStyle(
|
||||
fontSize: titleFontSize,
|
||||
),
|
||||
),
|
||||
],
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.surface,
|
||||
border: Border(
|
||||
top: BorderSide(
|
||||
color: theme.dividerColor,
|
||||
width: 1.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
spacing: 8.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
spacing: 12.0,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.edit,
|
||||
size: mediumIconSize,
|
||||
),
|
||||
Flexible(
|
||||
child: Text(
|
||||
L10n.of(context).editCourseLater,
|
||||
style: const TextStyle(
|
||||
fontSize: descFontSize,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
spacing: 12.0,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.shield,
|
||||
size: mediumIconSize,
|
||||
),
|
||||
Flexible(
|
||||
child: Text(
|
||||
L10n.of(context).newCourseAccess,
|
||||
style: const TextStyle(
|
||||
fontSize: descFontSize,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor:
|
||||
theme.colorScheme.primaryContainer,
|
||||
foregroundColor:
|
||||
theme.colorScheme.onPrimaryContainer,
|
||||
),
|
||||
onPressed: () => showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => spaceId != null
|
||||
? controller.addCourseToSpace(course)
|
||||
: controller.launchCourse(course),
|
||||
),
|
||||
child: Row(
|
||||
spacing: 8.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.map_outlined),
|
||||
Text(
|
||||
spaceId != null
|
||||
? L10n.of(context).addCoursePlan
|
||||
: L10n.of(context).createCourse,
|
||||
style: const TextStyle(
|
||||
fontSize: titleFontSize,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -160,13 +160,13 @@ class CoursePlansRepo {
|
|||
if (filter.languageOfInstructions != null) {
|
||||
where["and"].add({
|
||||
"l1": {
|
||||
"equals": filter.languageOfInstructions!.langCode,
|
||||
"equals": filter.languageOfInstructions!.langCodeShort,
|
||||
},
|
||||
});
|
||||
}
|
||||
if (filter.targetLanguage != null) {
|
||||
where["and"].add({
|
||||
"l2": {"equals": filter.targetLanguage!.langCode},
|
||||
"l2": {"equals": filter.targetLanguage!.langCodeShort},
|
||||
});
|
||||
}
|
||||
} else if (numberOfFilter == 1) {
|
||||
|
|
|
|||
|
|
@ -1,102 +0,0 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/find_your_people/find_your_people_view.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class FindYourPeople extends StatefulWidget {
|
||||
const FindYourPeople({super.key});
|
||||
|
||||
@override
|
||||
State<FindYourPeople> createState() => FindYourPeopleState();
|
||||
}
|
||||
|
||||
class FindYourPeopleState extends State<FindYourPeople> {
|
||||
final TextEditingController searchController = TextEditingController();
|
||||
|
||||
Object? error;
|
||||
bool loading = true;
|
||||
|
||||
Timer? _coolDown;
|
||||
|
||||
final List<PublicRoomsChunk> spaceItems = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
setSpaceItems();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
searchController.dispose();
|
||||
_coolDown?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void onSearchEnter(String text, {bool globalSearch = true}) {
|
||||
if (text.isEmpty) {
|
||||
setSpaceItems();
|
||||
return;
|
||||
}
|
||||
|
||||
_coolDown?.cancel();
|
||||
_coolDown = Timer(const Duration(milliseconds: 500), setSpaceItems);
|
||||
}
|
||||
|
||||
Future<void> setSpaceItems() async {
|
||||
setState(() {
|
||||
loading = true;
|
||||
error = null;
|
||||
spaceItems.clear();
|
||||
});
|
||||
|
||||
try {
|
||||
final resp = await Matrix.of(context).client.queryPublicRooms(
|
||||
filter: PublicRoomQueryFilter(
|
||||
roomTypes: ['m.space'],
|
||||
genericSearchTerm: searchController.text,
|
||||
),
|
||||
limit: 100,
|
||||
);
|
||||
spaceItems.addAll(resp.chunk);
|
||||
spaceItems.sort((a, b) {
|
||||
int getPriority(item) {
|
||||
final bool hasTopic = item.topic != null && item.topic!.isNotEmpty;
|
||||
final bool hasAvatar = item.avatarUrl != null;
|
||||
|
||||
if (hasTopic && hasAvatar) return 0; // Highest priority
|
||||
if (hasAvatar) return 1; // Second priority
|
||||
if (hasTopic) return 2; // Third priority
|
||||
return 3; // Lowest priority
|
||||
}
|
||||
|
||||
return getPriority(a).compareTo(getPriority(b));
|
||||
});
|
||||
} catch (e, s) {
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
'searchText': searchController.text,
|
||||
},
|
||||
);
|
||||
error = e;
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
loading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FindYourPeopleView(controller: this);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,249 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
|
||||
import 'package:fluffychat/pangea/find_your_people/find_your_people.dart';
|
||||
import 'package:fluffychat/pangea/find_your_people/public_space_tile.dart';
|
||||
import 'package:fluffychat/pangea/spaces/utils/space_code.dart';
|
||||
|
||||
class FindYourPeopleView extends StatelessWidget {
|
||||
final FindYourPeopleState controller;
|
||||
|
||||
const FindYourPeopleView({
|
||||
super.key,
|
||||
required this.controller,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final isColumnMode = FluffyThemes.isColumnMode(context);
|
||||
|
||||
return Scaffold(
|
||||
appBar: isColumnMode
|
||||
? null
|
||||
: AppBar(
|
||||
leading: IconButton(
|
||||
icon: Icon(
|
||||
Icons.chevron_left,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
title: Icon(
|
||||
Icons.groups_outlined,
|
||||
size: 24.0,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
centerTitle: false,
|
||||
leadingWidth: 48.0,
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.join_full,
|
||||
color: theme.colorScheme.primary,
|
||||
size: 24.0,
|
||||
),
|
||||
const SizedBox(width: 8.0),
|
||||
Text(
|
||||
L10n.of(context).joinWithCode,
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
fontSize: 14.0,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
onPressed: () =>
|
||||
SpaceCodeUtil.joinWithSpaceCodeDialog(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
floatingActionButton: FloatingActionButton.extended(
|
||||
onPressed: () => context.push('/rooms/communities/newcourse'),
|
||||
icon: const Icon(Icons.add_box_outlined),
|
||||
label: Text(
|
||||
L10n.of(context).newCourse,
|
||||
overflow: TextOverflow.fade,
|
||||
),
|
||||
),
|
||||
body: Padding(
|
||||
padding: isColumnMode
|
||||
? const EdgeInsets.symmetric(
|
||||
horizontal: 24.0,
|
||||
vertical: 20.0,
|
||||
)
|
||||
: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
spacing: 16.0,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (isColumnMode)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 16.0,
|
||||
horizontal: 12.0,
|
||||
),
|
||||
child: Text(
|
||||
L10n.of(context).findCourse,
|
||||
style: const TextStyle(fontSize: 32.0),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
spacing: isColumnMode ? 32.0 : 16.0,
|
||||
children: [
|
||||
LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return SizedBox(
|
||||
width: constraints.maxWidth,
|
||||
child: Wrap(
|
||||
alignment: WrapAlignment.spaceBetween,
|
||||
spacing: 10,
|
||||
runSpacing: 10,
|
||||
children: [
|
||||
ConstrainedBox(
|
||||
constraints: isColumnMode
|
||||
? const BoxConstraints(
|
||||
minWidth: 200.0,
|
||||
maxWidth: 400.0,
|
||||
)
|
||||
: BoxConstraints(
|
||||
maxWidth:
|
||||
MediaQuery.of(context).size.width -
|
||||
24.0,
|
||||
),
|
||||
child: SizedBox(
|
||||
height: 40.0,
|
||||
width: isColumnMode ? 300.0 : null,
|
||||
child: TextField(
|
||||
controller: controller.searchController,
|
||||
onChanged: controller.onSearchEnter,
|
||||
textInputAction: TextInputAction.search,
|
||||
decoration: InputDecoration(
|
||||
filled: !isColumnMode,
|
||||
fillColor: isColumnMode
|
||||
? null
|
||||
: theme.colorScheme.secondaryContainer,
|
||||
border: OutlineInputBorder(
|
||||
borderSide: isColumnMode
|
||||
? const BorderSide()
|
||||
: BorderSide.none,
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
),
|
||||
contentPadding: const EdgeInsets.fromLTRB(
|
||||
0,
|
||||
0,
|
||||
20.0,
|
||||
0,
|
||||
),
|
||||
hintText: L10n.of(context).findCourse,
|
||||
hintStyle: TextStyle(
|
||||
color:
|
||||
theme.colorScheme.onPrimaryContainer,
|
||||
fontWeight: FontWeight.normal,
|
||||
fontSize: 16.0,
|
||||
),
|
||||
floatingLabelBehavior:
|
||||
FloatingLabelBehavior.never,
|
||||
prefixIcon: IconButton(
|
||||
onPressed: () {},
|
||||
icon: Icon(
|
||||
Icons.search_outlined,
|
||||
color: theme
|
||||
.colorScheme.onPrimaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isColumnMode)
|
||||
Wrap(
|
||||
children: [
|
||||
TextButton(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.join_full,
|
||||
color: theme
|
||||
.colorScheme.onPrimaryContainer,
|
||||
size: 24.0,
|
||||
),
|
||||
const SizedBox(width: 8.0),
|
||||
Text(
|
||||
L10n.of(context).joinWithCode,
|
||||
style: TextStyle(
|
||||
color: theme
|
||||
.colorScheme.onPrimaryContainer,
|
||||
fontSize: 16.0,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
onPressed: () =>
|
||||
SpaceCodeUtil.joinWithSpaceCodeDialog(
|
||||
context,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
controller.error != null
|
||||
? Column(
|
||||
spacing: 8.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ErrorIndicator(
|
||||
message: L10n.of(context).oopsSomethingWentWrong,
|
||||
),
|
||||
IconButton(
|
||||
onPressed: controller.setSpaceItems,
|
||||
icon: const Icon(Icons.refresh),
|
||||
),
|
||||
],
|
||||
)
|
||||
: controller.loading
|
||||
? const CircularProgressIndicator.adaptive()
|
||||
: controller.spaceItems.isEmpty
|
||||
? Text(
|
||||
L10n.of(context).nothingFound,
|
||||
)
|
||||
: Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: controller.spaceItems.length,
|
||||
itemBuilder: (context, index) {
|
||||
final space =
|
||||
controller.spaceItems[index];
|
||||
return Padding(
|
||||
padding: isColumnMode
|
||||
? const EdgeInsets.only(
|
||||
bottom: 32.0,
|
||||
)
|
||||
: const EdgeInsets.only(
|
||||
bottom: 16.0,
|
||||
),
|
||||
child: PublicSpaceTile(space: space),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,119 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/analytics_summary/learning_progress_indicator_button.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_rooms_chunk_extension.dart';
|
||||
import 'package:fluffychat/pangea/public_spaces/public_room_bottom_sheet.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
|
||||
class PublicSpaceTile extends StatelessWidget {
|
||||
final PublicRoomsChunk space;
|
||||
const PublicSpaceTile({super.key, required this.space});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bool isColumnMode = FluffyThemes.isColumnMode(context);
|
||||
|
||||
return HoverButton(
|
||||
onPressed: () => PublicRoomBottomSheet.show(
|
||||
context: context,
|
||||
chunk: space,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
hoverOpacity: 0.1,
|
||||
child: Padding(
|
||||
padding: isColumnMode
|
||||
? const EdgeInsets.all(12.0)
|
||||
: const EdgeInsets.all(0.0),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: isColumnMode ? 80.0 : 58.0,
|
||||
child: Row(
|
||||
children: [
|
||||
(space.avatarUrl != null)
|
||||
? Avatar(
|
||||
mxContent: space.avatarUrl,
|
||||
name: space.name,
|
||||
size: isColumnMode ? 80.0 : 58.0,
|
||||
borderRadius: BorderRadius.circular(
|
||||
10,
|
||||
),
|
||||
)
|
||||
: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
10,
|
||||
),
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: space.defaultAvatar(),
|
||||
width: isColumnMode ? 80.0 : 58.0,
|
||||
height: isColumnMode ? 80.0 : 58.0,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
spacing: 8.0,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
space.name ?? '',
|
||||
style: TextStyle(
|
||||
fontSize: isColumnMode ? 20.0 : 14.0,
|
||||
height: 1.2,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
Row(
|
||||
spacing: 10,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.group,
|
||||
size: isColumnMode ? 20.0 : 16.0,
|
||||
),
|
||||
Text(
|
||||
L10n.of(context).countParticipants(
|
||||
space.numJoinedMembers,
|
||||
),
|
||||
style: TextStyle(
|
||||
fontSize: isColumnMode ? 16.0 : 12.0,
|
||||
height: 1.2,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (isColumnMode && space.topic != null && space.topic!.isNotEmpty)
|
||||
Text(
|
||||
space.topic!,
|
||||
style: const TextStyle(
|
||||
fontSize: 16.0,
|
||||
height: 1.2,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@ class PAuthGaurd {
|
|||
static PangeaController? pController;
|
||||
|
||||
/// Redirect for /home routes
|
||||
static FutureOr<String?> loggedInRedirect(
|
||||
static FutureOr<String?> homeRedirect(
|
||||
BuildContext context,
|
||||
GoRouterState state,
|
||||
) async {
|
||||
|
|
@ -24,11 +24,19 @@ class PAuthGaurd {
|
|||
Matrix.of(context).widget.clients.any((client) => client.isLogged());
|
||||
if (!isLogged) return null;
|
||||
|
||||
return _onboardingRedirect(context, state);
|
||||
// If user hasn't set their L2,
|
||||
// and their URL doesn’t include ‘course,’ redirect
|
||||
final bool hasSetL2 = await pController!.userController.isUserL2Set;
|
||||
final langCode = state.pathParameters['langcode'];
|
||||
return !hasSetL2
|
||||
? langCode != null
|
||||
? '/registration/$langCode'
|
||||
: '/registration'
|
||||
: '/rooms';
|
||||
}
|
||||
|
||||
/// Redirect for onboarding and /rooms routes
|
||||
static FutureOr<String?> loggedOutRedirect(
|
||||
/// Redirect for /rooms routes
|
||||
static FutureOr<String?> roomsRedirect(
|
||||
BuildContext context,
|
||||
GoRouterState state,
|
||||
) async {
|
||||
|
|
@ -42,24 +50,30 @@ class PAuthGaurd {
|
|||
return '/home';
|
||||
}
|
||||
|
||||
return _onboardingRedirect(context, state);
|
||||
}
|
||||
|
||||
static Future<String?> _onboardingRedirect(
|
||||
BuildContext context,
|
||||
GoRouterState state,
|
||||
) async {
|
||||
// If user hasn't set their L2,
|
||||
// and their URL doesn’t include ‘course,’ redirect
|
||||
final bool hasSetL2 = await pController!.userController.isUserL2Set;
|
||||
final bool shouldRedirect =
|
||||
!hasSetL2 && !(state.fullPath?.contains('course') ?? false);
|
||||
|
||||
final langCode = state.pathParameters['langcode'];
|
||||
return shouldRedirect
|
||||
return !hasSetL2
|
||||
? langCode != null
|
||||
? '/course/$langCode'
|
||||
: '/course'
|
||||
? '/registration/$langCode'
|
||||
: '/registration'
|
||||
: null;
|
||||
}
|
||||
|
||||
/// Redirect for onboarding routes
|
||||
static FutureOr<String?> onboardingRedirect(
|
||||
BuildContext context,
|
||||
GoRouterState state,
|
||||
) async {
|
||||
if (pController == null) {
|
||||
return Matrix.of(context).client.isLogged() ? null : '/home';
|
||||
}
|
||||
|
||||
final isLogged = Matrix.of(context).widget.clients.any(
|
||||
(client) => client.isLogged(),
|
||||
);
|
||||
return isLogged ? null : '/home';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
158
lib/pangea/login/pages/create_pangea_account_page.dart
Normal file
158
lib/pangea/login/pages/create_pangea_account_page.dart
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class CreatePangeaAccountPage extends StatefulWidget {
|
||||
final String langCode;
|
||||
const CreatePangeaAccountPage({
|
||||
super.key,
|
||||
required this.langCode,
|
||||
});
|
||||
|
||||
@override
|
||||
CreatePangeaAccountPageState createState() => CreatePangeaAccountPageState();
|
||||
}
|
||||
|
||||
class CreatePangeaAccountPageState extends State<CreatePangeaAccountPage> {
|
||||
bool _loadingProfile = true;
|
||||
Object? _profileError;
|
||||
|
||||
final List<String> avatarPaths = const [
|
||||
"assets/pangea/Avatar_1.png",
|
||||
"assets/pangea/Avatar_2.png",
|
||||
"assets/pangea/Avatar_3.png",
|
||||
"assets/pangea/Avatar_4.png",
|
||||
"assets/pangea/Avatar_5.png",
|
||||
];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_createUserInPangea();
|
||||
}
|
||||
|
||||
Future<void> _setAvatar() async {
|
||||
final client = Matrix.of(context).client;
|
||||
try {
|
||||
final random = Random();
|
||||
final selectedAvatarPath =
|
||||
avatarPaths[random.nextInt(avatarPaths.length)];
|
||||
|
||||
final ByteData byteData = await rootBundle.load(selectedAvatarPath);
|
||||
final Uint8List bytes = byteData.buffer.asUint8List();
|
||||
final file = MatrixFile(
|
||||
bytes: bytes,
|
||||
name: selectedAvatarPath,
|
||||
);
|
||||
await client.setAvatar(file);
|
||||
} catch (err, s) {
|
||||
ErrorHandler.logError(
|
||||
e: err,
|
||||
s: s,
|
||||
data: {},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _createUserInPangea() async {
|
||||
setState(() {
|
||||
_loadingProfile = true;
|
||||
_profileError = null;
|
||||
});
|
||||
|
||||
final l2Set = await MatrixState.pangeaController.userController.isUserL2Set;
|
||||
if (l2Set) {
|
||||
context.go('/registration/course');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final updateFuture = [
|
||||
_setAvatar(),
|
||||
MatrixState.pangeaController.userController.updateProfile(
|
||||
(profile) {
|
||||
final systemLang = MatrixState
|
||||
.pangeaController.languageController.systemLanguage?.langCode;
|
||||
|
||||
if (systemLang != null) {
|
||||
profile.userSettings.sourceLanguage = systemLang;
|
||||
}
|
||||
|
||||
profile.userSettings.targetLanguage = widget.langCode;
|
||||
profile.userSettings.createdAt = DateTime.now();
|
||||
return profile;
|
||||
},
|
||||
waitForDataInSync: true,
|
||||
),
|
||||
MatrixState.pangeaController.userController.updateAnalyticsProfile(
|
||||
targetLanguage: PLanguageStore.byLangCode(widget.langCode),
|
||||
baseLanguage:
|
||||
MatrixState.pangeaController.languageController.systemLanguage,
|
||||
level: 1,
|
||||
),
|
||||
];
|
||||
|
||||
await Future.wait(updateFuture).timeout(
|
||||
const Duration(seconds: 30),
|
||||
onTimeout: () {
|
||||
throw TimeoutException(L10n.of(context).oopsSomethingWentWrong);
|
||||
},
|
||||
);
|
||||
|
||||
await MatrixState.pangeaController.subscriptionController.reinitialize();
|
||||
context.go('/registration/course');
|
||||
} catch (err) {
|
||||
if (err is MatrixException) {
|
||||
_profileError = err.errorMessage;
|
||||
} else {
|
||||
_profileError = err;
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() => _loadingProfile = false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_loadingProfile && _profileError != null) {
|
||||
context.go('/registration/course');
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
body: SafeArea(
|
||||
child: Center(
|
||||
child: _loadingProfile
|
||||
? const CircularProgressIndicator.adaptive()
|
||||
: _profileError != null
|
||||
? Column(
|
||||
spacing: 8.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ErrorIndicator(
|
||||
message: L10n.of(context).oopsSomethingWentWrong,
|
||||
),
|
||||
TextButton(
|
||||
onPressed: _createUserInPangea,
|
||||
child: Text(L10n.of(context).tryAgain),
|
||||
),
|
||||
],
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -29,6 +29,7 @@ class LanguageSelectionPageState extends State<LanguageSelectionPage> {
|
|||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final languages = MatrixState.pangeaController.pLanguageStore.targetOptions;
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(L10n.of(context).languages),
|
||||
|
|
@ -114,11 +115,16 @@ class LanguageSelectionPageState extends State<LanguageSelectionPage> {
|
|||
),
|
||||
ElevatedButton(
|
||||
onPressed: _selectedLanguage != null
|
||||
? () => context.go(
|
||||
Matrix.of(context).client.isLogged()
|
||||
? "/course/${_selectedLanguage!.langCode}"
|
||||
: "/home/languages/${_selectedLanguage!.langCode}",
|
||||
)
|
||||
? () {
|
||||
context.go(
|
||||
GoRouterState.of(context)
|
||||
.fullPath
|
||||
?.contains('home') ==
|
||||
true
|
||||
? '/home/signup/${_selectedLanguage!.langCode}'
|
||||
: '/registration/${_selectedLanguage!.langCode}',
|
||||
);
|
||||
}
|
||||
: null,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: theme.colorScheme.surface,
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ class LoginOrSignupViewState extends State<LoginOrSignupView> {
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: () => context.go('/home/languages'),
|
||||
onPressed: () => context.go('/home/signup'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: theme.colorScheme.surface,
|
||||
foregroundColor: theme.colorScheme.onSurface,
|
||||
|
|
|
|||
|
|
@ -10,14 +10,16 @@ import 'package:fluffychat/pangea/course_creation/course_plan_filter_widget.dart
|
|||
import 'package:fluffychat/pangea/course_creation/course_search_provider.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/models/language_model.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class NewTripPage extends StatefulWidget {
|
||||
final String langCode;
|
||||
final String route;
|
||||
final String? spaceId;
|
||||
|
||||
const NewTripPage({
|
||||
super.key,
|
||||
required this.langCode,
|
||||
required this.route,
|
||||
this.spaceId,
|
||||
});
|
||||
|
||||
@override
|
||||
|
|
@ -29,7 +31,7 @@ class NewTripPageState extends State<NewTripPage> with CourseSearchProvider {
|
|||
void initState() {
|
||||
super.initState();
|
||||
|
||||
final target = PLanguageStore.byLangCode(widget.langCode);
|
||||
final target = MatrixState.pangeaController.languageController.userL2;
|
||||
if (target != null) {
|
||||
setTargetLanguageFilter(target);
|
||||
}
|
||||
|
|
@ -43,6 +45,7 @@ class NewTripPageState extends State<NewTripPage> with CourseSearchProvider {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final spaceId = widget.spaceId;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Row(
|
||||
|
|
@ -50,7 +53,11 @@ class NewTripPageState extends State<NewTripPage> with CourseSearchProvider {
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.map_outlined),
|
||||
Text(L10n.of(context).startOwnTripTitle),
|
||||
Text(
|
||||
spaceId != null
|
||||
? L10n.of(context).addCoursePlan
|
||||
: L10n.of(context).startOwnTripTitle,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
@ -133,7 +140,9 @@ class NewTripPageState extends State<NewTripPage> with CourseSearchProvider {
|
|||
padding: const EdgeInsets.only(bottom: 10.0),
|
||||
child: InkWell(
|
||||
onTap: () => context.go(
|
||||
'/course/${widget.langCode}/own/${course.uuid}',
|
||||
spaceId != null
|
||||
? '/rooms/spaces/$spaceId/addcourse/${courses[index].uuid}'
|
||||
: '/${widget.route}/course/own/${course.uuid}',
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
child: Container(
|
||||
|
|
|
|||
|
|
@ -1,134 +1,18 @@
|
|||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/pangea_logo_svg.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart';
|
||||
import 'package:fluffychat/utils/localized_exception_extension.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class PlanTripPage extends StatefulWidget {
|
||||
final String langCode;
|
||||
class PlanTripPage extends StatelessWidget {
|
||||
final String route;
|
||||
const PlanTripPage({
|
||||
required this.route,
|
||||
super.key,
|
||||
required this.langCode,
|
||||
});
|
||||
|
||||
@override
|
||||
State<PlanTripPage> createState() => PlanTripPageState();
|
||||
}
|
||||
|
||||
class PlanTripPageState extends State<PlanTripPage> {
|
||||
bool _loadingProfile = true;
|
||||
Object? _profileError;
|
||||
|
||||
final List<String> avatarPaths = const [
|
||||
"assets/pangea/Avatar_1.png",
|
||||
"assets/pangea/Avatar_2.png",
|
||||
"assets/pangea/Avatar_3.png",
|
||||
"assets/pangea/Avatar_4.png",
|
||||
"assets/pangea/Avatar_5.png",
|
||||
];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_createUserInPangea();
|
||||
}
|
||||
|
||||
Future<void> _setAvatar() async {
|
||||
final client = Matrix.of(context).client;
|
||||
try {
|
||||
final random = Random();
|
||||
final selectedAvatarPath =
|
||||
avatarPaths[random.nextInt(avatarPaths.length)];
|
||||
|
||||
final ByteData byteData = await rootBundle.load(selectedAvatarPath);
|
||||
final Uint8List bytes = byteData.buffer.asUint8List();
|
||||
final file = MatrixFile(
|
||||
bytes: bytes,
|
||||
name: selectedAvatarPath,
|
||||
);
|
||||
await client.setAvatar(file);
|
||||
} catch (err, s) {
|
||||
ErrorHandler.logError(
|
||||
e: err,
|
||||
s: s,
|
||||
data: {},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _createUserInPangea() async {
|
||||
final l2Set = await MatrixState.pangeaController.userController.isUserL2Set;
|
||||
if (l2Set) {
|
||||
if (mounted) setState(() => _loadingProfile = false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_loadingProfile = true;
|
||||
_profileError = null;
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
final updateFuture = [
|
||||
_setAvatar(),
|
||||
MatrixState.pangeaController.userController.updateProfile(
|
||||
(profile) {
|
||||
final systemLang = MatrixState
|
||||
.pangeaController.languageController.systemLanguage?.langCode;
|
||||
|
||||
if (systemLang != null) {
|
||||
profile.userSettings.sourceLanguage = systemLang;
|
||||
}
|
||||
|
||||
profile.userSettings.targetLanguage = widget.langCode;
|
||||
profile.userSettings.createdAt = DateTime.now();
|
||||
return profile;
|
||||
},
|
||||
waitForDataInSync: true,
|
||||
),
|
||||
MatrixState.pangeaController.userController.updateAnalyticsProfile(
|
||||
targetLanguage: PLanguageStore.byLangCode(widget.langCode),
|
||||
baseLanguage:
|
||||
MatrixState.pangeaController.languageController.systemLanguage,
|
||||
level: 1,
|
||||
),
|
||||
];
|
||||
|
||||
await Future.wait(updateFuture).timeout(
|
||||
const Duration(seconds: 30),
|
||||
onTimeout: () {
|
||||
throw TimeoutException(L10n.of(context).oopsSomethingWentWrong);
|
||||
},
|
||||
);
|
||||
|
||||
await MatrixState.pangeaController.subscriptionController.reinitialize();
|
||||
} catch (err) {
|
||||
if (err is MatrixException) {
|
||||
_profileError = err.errorMessage;
|
||||
} else {
|
||||
_profileError = err.toLocalizedString(context);
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() => _loadingProfile = false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
|
@ -142,113 +26,108 @@ class PlanTripPageState extends State<PlanTripPage> {
|
|||
Text(L10n.of(context).planTrip),
|
||||
],
|
||||
),
|
||||
automaticallyImplyLeading: route == 'registration',
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Center(
|
||||
child: _loadingProfile
|
||||
? const CircularProgressIndicator.adaptive()
|
||||
: _profileError != null
|
||||
? const ErrorIndicator(
|
||||
message: "Failed to create profile",
|
||||
)
|
||||
: Container(
|
||||
padding: const EdgeInsets.all(30.0),
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 350,
|
||||
maxHeight: 600,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(30.0),
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 350,
|
||||
maxHeight: 600,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
PangeaLogoSvg(
|
||||
width: 100.0,
|
||||
forceColor: theme.colorScheme.onSurface,
|
||||
),
|
||||
Column(
|
||||
spacing: 16.0,
|
||||
children: [
|
||||
Text(
|
||||
L10n.of(context).howAreYouTraveling,
|
||||
style: theme.textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () => context.go(
|
||||
'/$route/course/private',
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: theme.colorScheme.surface,
|
||||
foregroundColor: theme.colorScheme.onSurface,
|
||||
side: BorderSide(
|
||||
width: 1,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
spacing: 4.0,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
PangeaLogoSvg(
|
||||
width: 100.0,
|
||||
forceColor: theme.colorScheme.onSurface,
|
||||
),
|
||||
Column(
|
||||
spacing: 16.0,
|
||||
children: [
|
||||
Text(
|
||||
L10n.of(context).howAreYouTraveling,
|
||||
style: theme.textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () => context.go(
|
||||
"/course/${widget.langCode}/private",
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: theme.colorScheme.surface,
|
||||
foregroundColor: theme.colorScheme.onSurface,
|
||||
side: BorderSide(
|
||||
width: 1,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
spacing: 4.0,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.map_outlined),
|
||||
Text(L10n.of(context).unlockPrivateTrip),
|
||||
],
|
||||
),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () => context.go(
|
||||
"/course/${widget.langCode}/public",
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: theme.colorScheme.surface,
|
||||
foregroundColor: theme.colorScheme.onSurface,
|
||||
side: BorderSide(
|
||||
width: 1,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
spacing: 4.0,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Symbols.map_search),
|
||||
Text(L10n.of(context).joinPublicTrip),
|
||||
],
|
||||
),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () => context.go(
|
||||
"/course/${widget.langCode}/own",
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: theme.colorScheme.surface,
|
||||
foregroundColor: theme.colorScheme.onSurface,
|
||||
side: BorderSide(
|
||||
width: 1,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
spacing: 4.0,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.map_outlined),
|
||||
Text(L10n.of(context).startOwnTrip),
|
||||
],
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
contentPadding: const EdgeInsets.all(0.0),
|
||||
leading: const Icon(Icons.school),
|
||||
title: Text(
|
||||
L10n.of(context).tripPlanDesc,
|
||||
style: theme.textTheme.labelLarge,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Icon(Icons.map_outlined),
|
||||
Text(L10n.of(context).unlockPrivateTrip),
|
||||
],
|
||||
),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () => context.go(
|
||||
'/$route/course/public',
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: theme.colorScheme.surface,
|
||||
foregroundColor: theme.colorScheme.onSurface,
|
||||
side: BorderSide(
|
||||
width: 1,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
spacing: 4.0,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Symbols.map_search),
|
||||
Text(L10n.of(context).joinPublicTrip),
|
||||
],
|
||||
),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () => context.go(
|
||||
'/$route/course/own',
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: theme.colorScheme.surface,
|
||||
foregroundColor: theme.colorScheme.onSurface,
|
||||
side: BorderSide(
|
||||
width: 1,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
spacing: 4.0,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.map_outlined),
|
||||
Text(L10n.of(context).startOwnTrip),
|
||||
],
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
contentPadding: const EdgeInsets.all(0.0),
|
||||
leading: const Icon(Icons.school),
|
||||
title: Text(
|
||||
L10n.of(context).tripPlanDesc,
|
||||
style: theme.textTheme.labelLarge,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -5,10 +5,8 @@ import 'package:fluffychat/pangea/common/widgets/pangea_logo_svg.dart';
|
|||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class PrivateTripPage extends StatefulWidget {
|
||||
final String langCode;
|
||||
const PrivateTripPage({
|
||||
super.key,
|
||||
required this.langCode,
|
||||
});
|
||||
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -5,14 +5,11 @@ import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
|
|||
import 'package:fluffychat/pangea/course_creation/course_plan_filter_widget.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/models/language_model.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class PublicTripPage extends StatefulWidget {
|
||||
final String langCode;
|
||||
const PublicTripPage({
|
||||
super.key,
|
||||
required this.langCode,
|
||||
});
|
||||
|
||||
@override
|
||||
|
|
@ -31,7 +28,7 @@ class PublicTripPageState extends State<PublicTripPage> {
|
|||
void initState() {
|
||||
super.initState();
|
||||
|
||||
final target = PLanguageStore.byLangCode(widget.langCode);
|
||||
final target = MatrixState.pangeaController.languageController.userL2;
|
||||
if (target != null) {
|
||||
setTargetLanguageFilter(target);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ class SignupPageController extends State<SignupPage> {
|
|||
},
|
||||
);
|
||||
|
||||
if (!resp.isError) context.go("/course/${widget.langCode}");
|
||||
if (!resp.isError) context.go('/registration/${widget.langCode}');
|
||||
}
|
||||
|
||||
Future<void> _signupFuture() async {
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ class SignupPageView extends StatelessWidget {
|
|||
),
|
||||
ElevatedButton(
|
||||
onPressed: () => context.go(
|
||||
'/home/languages/${controller.widget.langCode}/email',
|
||||
'/home/signup/${controller.widget.langCode}/email',
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: theme.colorScheme.surface,
|
||||
|
|
|
|||
|
|
@ -80,7 +80,9 @@ class SubscriptionController extends BaseController {
|
|||
_isInitializing = true;
|
||||
await _initialize();
|
||||
_isInitializing = false;
|
||||
initCompleter.complete();
|
||||
if (!initCompleter.isCompleted) {
|
||||
initCompleter.complete();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> reinitialize() async {
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ class _MainView extends StatelessWidget {
|
|||
return Settings(key: state.pageKey);
|
||||
}
|
||||
|
||||
if (path.contains('communities')) {
|
||||
if (path.contains('course')) {
|
||||
return Center(
|
||||
child: SizedBox(
|
||||
width: 250.0,
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ class SpacesNavigationRail extends StatelessWidget {
|
|||
.startsWith('/rooms/settings');
|
||||
// #Pangea
|
||||
final isAnalytics = path?.contains('analytics') ?? false;
|
||||
final isCommunities = path?.contains('communities') ?? false;
|
||||
final isCourse = path?.contains('course') ?? false;
|
||||
final isColumnMode = FluffyThemes.isColumnMode(context);
|
||||
|
||||
final width = isColumnMode
|
||||
|
|
@ -125,7 +125,7 @@ class SpacesNavigationRail extends StatelessWidget {
|
|||
isSelected: activeSpaceId == null &&
|
||||
!isSettings &&
|
||||
!isAnalytics &&
|
||||
!isCommunities,
|
||||
!isCourse,
|
||||
// onTap: onGoToChats,
|
||||
// icon: const Padding(
|
||||
// padding: EdgeInsets.all(10.0),
|
||||
|
|
@ -159,16 +159,16 @@ class SpacesNavigationRail extends StatelessWidget {
|
|||
// toolTip: L10n.of(context).createNewSpace,
|
||||
backgroundColor: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(0),
|
||||
isSelected: isCommunities,
|
||||
isSelected: isCourse,
|
||||
onTap: () {
|
||||
context.go('/rooms/communities');
|
||||
context.go('/rooms/course');
|
||||
},
|
||||
icon: ClipPath(
|
||||
clipper: MapClipper(),
|
||||
child: Container(
|
||||
width: width - (isColumnMode ? 32.0 : 24.0),
|
||||
height: width - (isColumnMode ? 32.0 : 24.0),
|
||||
color: isCommunities
|
||||
color: isCourse
|
||||
? Theme.of(context)
|
||||
.colorScheme
|
||||
.primaryContainer
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue