feat: add join space onboarding step (#2771)

This commit is contained in:
ggurdin 2025-05-12 16:09:17 -04:00 committed by GitHub
parent 757ffae1bb
commit 8ae997e642
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 224 additions and 28 deletions

View file

@ -4922,5 +4922,15 @@
}
}
},
"leaderboard": "Leaderboard"
"leaderboard": "Leaderboard",
"welcomeUser": "Welcome {user}",
"@welcomeUser": {
"placeholders": {
"user": {
"type": "String"
}
}
},
"joinSpaceOnboardingDesc": "Do you have an invite code or link to a learning community?",
"skipForNow": "Skip for now"
}

View file

@ -37,6 +37,7 @@ import 'package:fluffychat/pangea/layouts/bottom_nav_layout.dart';
import 'package:fluffychat/pangea/learning_settings/pages/settings_learning.dart';
import 'package:fluffychat/pangea/login/pages/login_or_signup_view.dart';
import 'package:fluffychat/pangea/login/pages/signup.dart';
import 'package:fluffychat/pangea/login/pages/space_code_onboarding.dart';
import 'package:fluffychat/pangea/login/pages/user_settings.dart';
import 'package:fluffychat/pangea/spaces/constants/space_constants.dart';
import 'package:fluffychat/pangea/spaces/utils/join_with_alias.dart';
@ -173,6 +174,19 @@ abstract class AppRoutes {
const UserSettingsPage(),
),
redirect: loggedOutRedirect,
routes: [
GoRoute(
path: 'join_space',
pageBuilder: (context, state) {
return defaultPageBuilder(
context,
state,
const SpaceCodeOnboarding(),
);
},
redirect: loggedOutRedirect,
),
],
),
ShellRoute(
pageBuilder: chatListShellRouteBuilder,

View file

@ -19,7 +19,6 @@ import 'package:fluffychat/pages/chat_list/chat_list_view.dart';
import 'package:fluffychat/pangea/chat_list/utils/app_version_util.dart';
import 'package:fluffychat/pangea/chat_list/utils/chat_list_handle_space_tap.dart';
import 'package:fluffychat/pangea/chat_settings/constants/pangea_room_types.dart';
import 'package:fluffychat/pangea/common/constants/local.key.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/common/utils/firebase_analytics.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
@ -43,6 +42,7 @@ import '../../widgets/matrix.dart';
import 'package:fluffychat/utils/tor_stub.dart'
if (dart.library.html) 'package:tor_detector_web/tor_detector_web.dart';
enum PopupMenuAction {
settings,
invite,
@ -533,9 +533,7 @@ class ChatListController extends State<ChatList>
// #Pangea
final String? justInputtedCode =
MatrixState.pangeaController.classController.chatBox.read(
PLocalKey.justInputtedCode,
);
MatrixState.pangeaController.classController.justInputtedCode();
final newSpaceCode = space?.classCode(context);
if (newSpaceCode == justInputtedCode) return;

View file

@ -5,7 +5,6 @@ import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/chat_list/chat_list.dart';
import 'package:fluffychat/pangea/common/constants/local.key.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
import 'package:fluffychat/widgets/matrix.dart';
@ -50,9 +49,8 @@ void chatListHandleSpaceTap(
(element) =>
element.isSpace && element.membership == Membership.join,
);
final justInputtedCode = MatrixState
.pangeaController.classController.chatBox
.read(PLocalKey.justInputtedCode);
final justInputtedCode =
MatrixState.pangeaController.classController.justInputtedCode();
if (rooms.any((s) => s.spaceChildren.any((c) => c.roomId == space.id))) {
autoJoin(space);
} else if (justInputtedCode != null &&

View file

@ -4,10 +4,12 @@ import 'package:flutter/material.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/widgets/mxc_image.dart';
class PangeaLoginScaffold extends StatelessWidget {
final String mainAssetPath;
final Uint8List? mainAssetBytes;
final Uri? mainAssetUrl;
final List<Widget> children;
final bool showAppName;
final AppBar? customAppBar;
@ -16,6 +18,7 @@ class PangeaLoginScaffold extends StatelessWidget {
required this.children,
this.mainAssetPath = "assets/pangea/PangeaChat_Glow_Logo.png",
this.mainAssetBytes,
this.mainAssetUrl,
this.showAppName = true,
this.customAppBar,
super.key,
@ -58,10 +61,22 @@ class PangeaLoginScaffold extends StatelessWidget {
mainAssetBytes!,
fit: BoxFit.cover,
)
: Image.asset(
mainAssetPath,
fit: BoxFit.cover,
),
: mainAssetUrl != null
? mainAssetUrl!.toString().startsWith("mxc")
? MxcImage(
uri: mainAssetUrl,
fit: BoxFit.cover,
width: isColumnMode ? 175 : 125,
height: isColumnMode ? 175 : 125,
)
: Image.network(
mainAssetUrl.toString(),
fit: BoxFit.cover,
)
: Image.asset(
mainAssetPath,
fit: BoxFit.cover,
),
),
),
if (showAppName)

View file

@ -0,0 +1,76 @@
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/login/pages/space_code_onboarding_view.dart';
import 'package:fluffychat/pangea/spaces/constants/space_constants.dart';
import 'package:fluffychat/widgets/matrix.dart';
class SpaceCodeOnboarding extends StatefulWidget {
const SpaceCodeOnboarding({super.key});
@override
State<SpaceCodeOnboarding> createState() => SpaceCodeOnboardingState();
}
class SpaceCodeOnboardingState extends State<SpaceCodeOnboarding> {
Profile? profile;
Client get client => Matrix.of(context).client;
final TextEditingController codeController = TextEditingController();
@override
void initState() {
_setProfile();
codeController.addListener(() {
if (mounted) setState(() {});
});
super.initState();
}
@override
void dispose() {
codeController.dispose();
super.dispose();
}
Future<void> _setProfile() async {
try {
profile = await client.getProfileFromUserId(
client.userID!,
);
} catch (e, s) {
ErrorHandler.logError(
e: e,
s: s,
data: {
'userId': client.userID,
},
);
} finally {
if (mounted) setState(() {});
}
}
Future<void> submitCode() async {
String code = codeController.text.trim();
if (code.isEmpty) return;
try {
final link = Uri.parse(Uri.parse(code).fragment);
if (link.queryParameters.containsKey(SpaceConstants.classCode)) {
code = link.queryParameters[SpaceConstants.classCode]!;
}
} catch (e) {
debugPrint("Text input is not a URL: $e");
}
await MatrixState.pangeaController.classController
.joinClasswithCode(context, code);
}
@override
Widget build(BuildContext context) =>
SpaceCodeOnboardingView(controller: this);
}

View file

@ -0,0 +1,68 @@
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/login/pages/pangea_login_scaffold.dart';
import 'package:fluffychat/pangea/login/pages/space_code_onboarding.dart';
import 'package:fluffychat/pangea/login/widgets/full_width_button.dart';
import 'package:fluffychat/pangea/user/utils/p_logout.dart';
class SpaceCodeOnboardingView extends StatelessWidget {
final SpaceCodeOnboardingState controller;
const SpaceCodeOnboardingView({
super.key,
required this.controller,
});
@override
Widget build(BuildContext context) {
return PangeaLoginScaffold(
customAppBar: AppBar(
leading: BackButton(
onPressed: () => pLogoutAction(
context,
bypassWarning: true,
),
),
),
showAppName: false,
mainAssetUrl: controller.profile?.avatarUrl,
children: [
Text(
L10n.of(context).welcomeUser(
controller.profile?.displayName ??
controller.client.userID?.localpart ??
"",
),
style: Theme.of(context)
.textTheme
.titleLarge
?.copyWith(fontWeight: FontWeight.bold),
),
const SizedBox(height: 8.0),
Text(
L10n.of(context).joinSpaceOnboardingDesc,
textAlign: TextAlign.center,
),
const SizedBox(height: 8.0),
FullWidthTextField(
hintText: L10n.of(context).enterCodeToJoin,
controller: controller.codeController,
onSubmitted: (_) => controller.submitCode,
),
FullWidthButton(
title: L10n.of(context).join,
onPressed: controller.submitCode,
enabled: controller.codeController.text.isNotEmpty,
),
const SizedBox(height: 8.0),
TextButton(
child: Text(L10n.of(context).skipForNow),
onPressed: () => context.go("/rooms"),
),
],
);
}
}

View file

@ -207,7 +207,11 @@ class UserSettingsState extends State<UserSettingsPage> {
throw TimeoutException(L10n.of(context).oopsSomethingWentWrong);
},
);
context.go('/rooms');
context.go(
_pangeaController.classController.cachedClassCode == null
? '/user_age/join_space'
: '/rooms',
);
} catch (err) {
if (err is MatrixException) {
profileCreationError = err.errorMessage;

View file

@ -23,22 +23,35 @@ import '../../common/controllers/base_controller.dart';
class ClassController extends BaseController {
late PangeaController _pangeaController;
// Storage Initialization
final GetStorage chatBox = GetStorage("chat_list_storage");
final GetStorage linkBox = GetStorage("link_storage");
static final GetStorage _classStorage = GetStorage('class_storage');
ClassController(PangeaController pangeaController) : super() {
_pangeaController = pangeaController;
}
Future<void> joinCachedSpaceCode(BuildContext context) async {
final String? classCode = linkBox.read(
Future<void> cacheSpaceCode(String code) async {
if (code.isEmpty) return;
await _classStorage.write(
PLocalKey.cachedClassCodeToJoin,
code,
);
}
final String? alias = _classStorage.read(PLocalKey.cachedAliasToJoin);
String? justInputtedCode() {
return _classStorage.read(PLocalKey.justInputtedCode);
}
String? get cachedClassCode {
return _classStorage.read(PLocalKey.cachedClassCodeToJoin);
}
String? get cachedAlias {
return _classStorage.read(PLocalKey.cachedAliasToJoin);
}
Future<void> joinCachedSpaceCode(BuildContext context) async {
final String? classCode = cachedClassCode;
final String? alias = cachedAlias;
if (classCode != null) {
await joinClasswithCode(
@ -46,7 +59,7 @@ class ClassController extends BaseController {
classCode,
);
await linkBox.remove(
await _classStorage.remove(
PLocalKey.cachedClassCodeToJoin,
);
} else if (alias != null) {
@ -141,7 +154,7 @@ class ClassController extends BaseController {
}
final chosenClassId = foundClasses.first;
await chatBox.write(
await _classStorage.write(
PLocalKey.justInputtedCode,
classCode,
);

View file

@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:fluffychat/pangea/common/constants/local.key.dart';
import 'package:fluffychat/widgets/matrix.dart';
//if on home with classcode in url and not logged in, then save it soemhow and after llogin, join class automatically
@ -32,10 +31,11 @@ class _JoinClassWithLinkState extends State<JoinClassWithLink> {
);
return;
}
await MatrixState.pangeaController.classController.linkBox.write(
PLocalKey.cachedClassCodeToJoin,
widget.classCode,
);
if (widget.classCode != null) {
await MatrixState.pangeaController.classController
.cacheSpaceCode(widget.classCode!);
}
context.go("/home");
});
}