feat: find your people page
This commit is contained in:
parent
c8fdcda3fb
commit
01d797e53f
19 changed files with 600 additions and 839 deletions
|
|
@ -4975,5 +4975,6 @@
|
|||
"canBeFoundViaInvitation": "\u2022 invitation",
|
||||
"canBeFoundViaCodeOrLink": "\u2022 code or link",
|
||||
"canBeFoundViaKnock": "\u2022 request to join and admin approval",
|
||||
"anyoneCanJoin": "Anyone can join! However, admin can kick and ban whoever misbehaves. Those who are banned may not return!"
|
||||
"anyoneCanJoin": "Anyone can join! However, admin can kick and ban whoever misbehaves. Those who are banned may not return!",
|
||||
"createYourSpace": "Create your space"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ import 'package:fluffychat/pages/settings_style/settings_style.dart';
|
|||
import 'package:fluffychat/pangea/activity_generator/activity_generator.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_planner_page.dart';
|
||||
import 'package:fluffychat/pangea/activity_suggestions/suggestions_page.dart';
|
||||
import 'package:fluffychat/pangea/find_your_people/find_your_people.dart';
|
||||
import 'package:fluffychat/pangea/find_your_people/find_your_people_side_view.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/login_or_signup_view.dart';
|
||||
|
|
@ -42,7 +44,6 @@ import 'package:fluffychat/pangea/spaces/constants/space_constants.dart';
|
|||
import 'package:fluffychat/pangea/spaces/utils/join_with_alias.dart';
|
||||
import 'package:fluffychat/pangea/spaces/utils/join_with_link.dart';
|
||||
import 'package:fluffychat/pangea/subscription/pages/settings_subscription.dart';
|
||||
import 'package:fluffychat/pangea/user/pages/find_partner.dart';
|
||||
import 'package:fluffychat/widgets/config_viewer.dart';
|
||||
import 'package:fluffychat/widgets/layouts/empty_page.dart';
|
||||
import 'package:fluffychat/widgets/layouts/two_column_layout.dart';
|
||||
|
|
@ -153,17 +154,11 @@ abstract class AppRoutes {
|
|||
),
|
||||
GoRoute(
|
||||
path: '/join_with_alias',
|
||||
pageBuilder: (context, state) => Matrix.of(context).client.isLogged()
|
||||
? chatListShellRouteBuilder(
|
||||
context,
|
||||
state,
|
||||
JoinWithAlias(alias: state.uri.queryParameters['alias']),
|
||||
)
|
||||
: defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
JoinWithAlias(alias: state.uri.queryParameters['alias']),
|
||||
),
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
JoinWithAlias(alias: state.uri.queryParameters['alias']),
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/user_age',
|
||||
|
|
@ -195,8 +190,13 @@ abstract class AppRoutes {
|
|||
pageBuilder: (context, state, child) => noTransitionPageBuilder(
|
||||
context,
|
||||
state,
|
||||
// #Pangea
|
||||
// FluffyThemes.isColumnMode(context) &&
|
||||
// state.fullPath?.startsWith('/rooms/settings') == false
|
||||
FluffyThemes.isColumnMode(context) &&
|
||||
state.fullPath?.startsWith('/rooms/settings') == false
|
||||
state.fullPath?.startsWith('/rooms/settings') == false &&
|
||||
state.fullPath?.startsWith('/rooms/communities') == false
|
||||
// Pangea#
|
||||
? TwoColumnLayout(
|
||||
mainView: ChatList(
|
||||
activeChat: state.pathParameters['roomid'],
|
||||
|
|
@ -302,16 +302,30 @@ abstract class AppRoutes {
|
|||
redirect: loggedOutRedirect,
|
||||
),
|
||||
// #Pangea
|
||||
GoRoute(
|
||||
path: 'partner',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
ShellRoute(
|
||||
pageBuilder: (context, state, child) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const FindPartner(),
|
||||
FluffyThemes.isColumnMode(context)
|
||||
? TwoColumnLayout(
|
||||
mainView: const FindYourPeopleSideView(),
|
||||
sideView: child,
|
||||
dividerColor: Colors.transparent,
|
||||
)
|
||||
: child,
|
||||
),
|
||||
redirect: loggedOutRedirect,
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: 'communities',
|
||||
redirect: loggedOutRedirect,
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const FindYourPeople(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
// #Pangea
|
||||
GoRoute(
|
||||
path: 'homepage',
|
||||
redirect: loggedOutRedirect,
|
||||
|
|
@ -748,27 +762,5 @@ abstract class AppRoutes {
|
|||
redirect: loggedOutRedirect,
|
||||
),
|
||||
];
|
||||
|
||||
static Page chatListShellRouteBuilder(
|
||||
context,
|
||||
state,
|
||||
child,
|
||||
) =>
|
||||
noTransitionPageBuilder(
|
||||
context,
|
||||
state,
|
||||
FluffyThemes.isColumnMode(context) &&
|
||||
state.fullPath?.startsWith('/rooms/settings') == false
|
||||
? TwoColumnLayout(
|
||||
mainView: ChatList(
|
||||
activeChat: state.pathParameters['roomid'],
|
||||
activeSpaceId: state.uri.queryParameters['spaceId'],
|
||||
displayNavigationRail:
|
||||
state.path?.startsWith('/rooms/settings') != true,
|
||||
),
|
||||
sideView: child,
|
||||
)
|
||||
: child,
|
||||
);
|
||||
// Pangea#
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@ class ChatListView extends StatelessWidget {
|
|||
activeSpaceId: controller.activeSpaceId,
|
||||
onGoToChats: controller.clearActiveSpace,
|
||||
onGoToSpaceId: controller.setActiveSpace,
|
||||
// #Pangea
|
||||
clearActiveSpace: controller.clearActiveSpace,
|
||||
// Pangea#
|
||||
),
|
||||
Container(
|
||||
color: Theme.of(context).dividerColor,
|
||||
|
|
|
|||
|
|
@ -2,14 +2,18 @@ import 'package:flutter/material.dart';
|
|||
|
||||
import 'package:fluffychat/widgets/hover_builder.dart';
|
||||
|
||||
class LearningProgressIndicatorButton extends StatelessWidget {
|
||||
class HoverButton extends StatelessWidget {
|
||||
final VoidCallback? onPressed;
|
||||
final Widget child;
|
||||
final BorderRadius? borderRadius;
|
||||
final double hoverOpacity;
|
||||
|
||||
const LearningProgressIndicatorButton({
|
||||
const HoverButton({
|
||||
super.key,
|
||||
required this.onPressed,
|
||||
required this.child,
|
||||
this.borderRadius,
|
||||
this.hoverOpacity = 0.2,
|
||||
});
|
||||
|
||||
@override
|
||||
|
|
@ -23,9 +27,12 @@ class LearningProgressIndicatorButton extends StatelessWidget {
|
|||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: hovered
|
||||
? Theme.of(context).colorScheme.primary.withAlpha(50)
|
||||
? Theme.of(context)
|
||||
.colorScheme
|
||||
.primary
|
||||
.withAlpha((hoverOpacity * 255).round())
|
||||
: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(36.0),
|
||||
borderRadius: borderRadius ?? BorderRadius.circular(36.0),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 2.0,
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ class LearningProgressIndicatorsState
|
|||
spacing: 16.0,
|
||||
children: ConstructTypeEnum.values
|
||||
.map(
|
||||
(c) => LearningProgressIndicatorButton(
|
||||
(c) => HoverButton(
|
||||
onPressed: () {
|
||||
showDialog<AnalyticsPopupWrapper>(
|
||||
context: context,
|
||||
|
|
@ -124,7 +124,7 @@ class LearningProgressIndicatorsState
|
|||
.toList(),
|
||||
),
|
||||
),
|
||||
LearningProgressIndicatorButton(
|
||||
HoverButton(
|
||||
onPressed: () => showDialog(
|
||||
context: context,
|
||||
builder: (c) => const SettingsLearning(),
|
||||
|
|
|
|||
102
lib/pangea/find_your_people/find_your_people.dart
Normal file
102
lib/pangea/find_your_people/find_your_people.dart
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
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();
|
||||
|
||||
String? 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.toString();
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
loading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FindYourPeopleView(controller: this);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
class FindYourPeopleConstants {
|
||||
static const String sideBearFileName = "Bear_Find_your_people.png";
|
||||
}
|
||||
48
lib/pangea/find_your_people/find_your_people_side_view.dart
Normal file
48
lib/pangea/find_your_people/find_your_people_side_view.dart
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pangea/find_your_people/find_your_people_constants.dart';
|
||||
import 'package:fluffychat/widgets/navigation_rail.dart';
|
||||
|
||||
class FindYourPeopleSideView extends StatelessWidget {
|
||||
const FindYourPeopleSideView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
if (FluffyThemes.isColumnMode(context) ||
|
||||
AppConfig.displayNavigationRail) ...[
|
||||
SpacesNavigationRail(
|
||||
activeSpaceId: null,
|
||||
onGoToChats: () => context.go('/rooms'),
|
||||
onGoToSpaceId: (spaceId) => context.go('/rooms?spaceId=$spaceId'),
|
||||
),
|
||||
Container(
|
||||
color: Colors.transparent,
|
||||
width: 1,
|
||||
),
|
||||
],
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
width: 250.0,
|
||||
child: CachedNetworkImage(
|
||||
imageUrl:
|
||||
"${AppConfig.assetsBaseURL}/${FindYourPeopleConstants.sideBearFileName}",
|
||||
errorWidget: (context, url, error) => const SizedBox(),
|
||||
placeholder: (context, url) => const Center(
|
||||
child: CircularProgressIndicator.adaptive(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
242
lib/pangea/find_your_people/find_your_people_view.dart
Normal file
242
lib/pangea/find_your_people/find_your_people_view.dart
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import 'package:fluffychat/config/themes.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: 20.0,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
centerTitle: false,
|
||||
leadingWidth: 48.0,
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.join_full,
|
||||
color: theme.colorScheme.primary,
|
||||
size: 20.0,
|
||||
),
|
||||
const SizedBox(width: 8.0),
|
||||
Text(
|
||||
L10n.of(context).joinWithCode,
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
fontSize: 10.0,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
onPressed: () =>
|
||||
SpaceCodeUtil.joinWithSpaceCodeDialog(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
floatingActionButton: isColumnMode
|
||||
? null
|
||||
: FloatingActionButton.extended(
|
||||
onPressed: () => context.push('/rooms/newspace'),
|
||||
icon: const Icon(Icons.add_box_outlined),
|
||||
label: Text(
|
||||
L10n.of(context).space,
|
||||
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).findYourPeople,
|
||||
style: const TextStyle(fontSize: 32.0),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
spacing: isColumnMode ? 32.0 : 16.0,
|
||||
children: [
|
||||
Container(
|
||||
height: 48.0,
|
||||
padding: isColumnMode
|
||||
? const EdgeInsets.symmetric(horizontal: 12)
|
||||
: null,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
spacing: 10,
|
||||
children: [
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: 40.0,
|
||||
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: EdgeInsets.zero,
|
||||
hintText: L10n.of(context).findYourPeople,
|
||||
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)
|
||||
TextButton(
|
||||
child: Row(
|
||||
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: 14.0,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
onPressed: () =>
|
||||
SpaceCodeUtil.joinWithSpaceCodeDialog(context),
|
||||
),
|
||||
if (isColumnMode)
|
||||
TextButton(
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.add_box_outlined,
|
||||
color: theme.colorScheme.onPrimaryContainer,
|
||||
size: 24.0,
|
||||
),
|
||||
const SizedBox(width: 8.0),
|
||||
Text(
|
||||
L10n.of(context).createYourSpace,
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.onPrimaryContainer,
|
||||
fontSize: 14.0,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
onPressed: () => context.push('/rooms/newspace'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
controller.error != null
|
||||
? Row(
|
||||
spacing: 8.0,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
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),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
104
lib/pangea/find_your_people/public_space_tile.dart
Normal file
104
lib/pangea/find_your_people/public_space_tile.dart
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pangea/analytics_summary/learning_progress_indicator_button.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: [
|
||||
Avatar(
|
||||
mxContent: space.avatarUrl,
|
||||
name: space.name,
|
||||
size: isColumnMode ? 80.0 : 58.0,
|
||||
borderRadius: BorderRadius.circular(
|
||||
10,
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
spacing: 8.0,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
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 ? 30.0 : 16.0,
|
||||
),
|
||||
Text(
|
||||
L10n.of(context).countParticipants(
|
||||
space.numJoinedMembers,
|
||||
),
|
||||
style: TextStyle(
|
||||
fontSize: isColumnMode ? 20.0 : 12.0,
|
||||
height: 1.2,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (isColumnMode && space.topic != null && space.topic!.isNotEmpty)
|
||||
Text(
|
||||
space.topic!,
|
||||
style: const TextStyle(
|
||||
fontSize: 20.0,
|
||||
height: 1.2,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
class AgeLimits {
|
||||
static const int toAccessFeatures = 18;
|
||||
static const int toUseTheApp = 13;
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
import 'user_model.dart';
|
||||
|
||||
class UserProfileSearchResponse {
|
||||
int count;
|
||||
String? next;
|
||||
String? previous;
|
||||
List<PangeaProfile> results;
|
||||
|
||||
UserProfileSearchResponse({
|
||||
required this.count,
|
||||
required this.next,
|
||||
required this.previous,
|
||||
required this.results,
|
||||
});
|
||||
|
||||
factory UserProfileSearchResponse.fromJson(Map<String, dynamic> json) {
|
||||
return UserProfileSearchResponse(
|
||||
count: json["count"],
|
||||
next: json["next"],
|
||||
previous: json["previous"],
|
||||
results: json["results"]
|
||||
.map((p) => PangeaProfile.fromJson(p))
|
||||
.toList()
|
||||
.cast<PangeaProfile>(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:country_picker/country_picker.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/models/language_model.dart';
|
||||
import 'package:fluffychat/pangea/user/models/user_model.dart';
|
||||
import '../../../widgets/matrix.dart';
|
||||
import '../../common/controllers/pangea_controller.dart';
|
||||
import '../models/user_profile_search_model.dart';
|
||||
import '../repo/user_repo.dart';
|
||||
import 'find_partner_view.dart';
|
||||
|
||||
class FindPartner extends StatefulWidget {
|
||||
const FindPartner({super.key});
|
||||
|
||||
@override
|
||||
State<FindPartner> createState() => FindPartnerController();
|
||||
}
|
||||
|
||||
class FindPartnerController extends State<FindPartner> {
|
||||
PangeaController pangeaController = MatrixState.pangeaController;
|
||||
|
||||
bool initialLoad = true;
|
||||
bool loading = false;
|
||||
String currentSearchTerm = "";
|
||||
late LanguageModel targetLanguageSearch;
|
||||
late LanguageModel sourceLanguageSearch;
|
||||
String? countrySearch;
|
||||
String? flagEmoji;
|
||||
|
||||
//PTODO - implement pagination
|
||||
String? nextUrl = "";
|
||||
int nextPage = 1;
|
||||
|
||||
Timer? coolDown;
|
||||
|
||||
final List<PangeaProfile> _userProfilesCache = [];
|
||||
|
||||
final scrollController = ScrollController();
|
||||
String? error;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
targetLanguageSearch = pangeaController.languageController.userL1 ??
|
||||
pangeaController.pLanguageStore.targetOptions[1];
|
||||
sourceLanguageSearch = pangeaController.languageController.userL2 ??
|
||||
pangeaController.pLanguageStore.targetOptions[0];
|
||||
|
||||
scrollController.addListener(() {
|
||||
if (scrollController.position.pixels ==
|
||||
scrollController.position.maxScrollExtent) {
|
||||
searchUserProfiles();
|
||||
}
|
||||
});
|
||||
|
||||
searchUserProfiles().then((_) => setState(() => initialLoad = false));
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
scrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (error != null && error!.isNotEmpty) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(L10n.of(context).oopsSomethingWentWrong),
|
||||
Text(L10n.of(context).errorPleaseRefresh),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
return FindPartnerView(this);
|
||||
}
|
||||
|
||||
List<PangeaProfile> get userProfiles => _userProfilesCache.where((p) {
|
||||
return (p.targetLanguage != null &&
|
||||
targetLanguageSearch.langCode == p.targetLanguage) &&
|
||||
(p.sourceLanguage != null &&
|
||||
sourceLanguageSearch.langCode == p.sourceLanguage) &&
|
||||
(countrySearch == null ||
|
||||
(p.country != null && countrySearch == p.country));
|
||||
}).toList();
|
||||
|
||||
void searchUserProfilesWithCoolDown(String text) {
|
||||
coolDown?.cancel();
|
||||
coolDown = Timer(
|
||||
const Duration(milliseconds: 0),
|
||||
() => searchUserProfiles(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> searchUserProfiles() async {
|
||||
coolDown?.cancel();
|
||||
if (loading || nextUrl == null) return;
|
||||
setState(() => loading = true);
|
||||
|
||||
UserProfileSearchResponse response;
|
||||
try {
|
||||
final String accessToken = pangeaController.userController.accessToken;
|
||||
response = await PUserRepo.searchUserProfiles(
|
||||
accessToken: accessToken,
|
||||
targetLanguage: targetLanguageSearch.langCode,
|
||||
sourceLanguage: sourceLanguageSearch.langCode,
|
||||
country: countrySearch,
|
||||
limit: 15,
|
||||
pageNumber: nextPage.toString(),
|
||||
);
|
||||
} catch (err, s) {
|
||||
error = err.toString();
|
||||
setState(() => loading = false);
|
||||
ErrorHandler.logError(
|
||||
e: err,
|
||||
s: s,
|
||||
data: {
|
||||
"accessToken": pangeaController.userController.accessToken,
|
||||
"targetLanguage": targetLanguageSearch.langCode,
|
||||
"sourceLanguage": sourceLanguageSearch.langCode,
|
||||
"country": countrySearch,
|
||||
"pageNumber": nextPage.toString(),
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
nextUrl = response.next;
|
||||
nextPage++;
|
||||
|
||||
final String? currentUserId = pangeaController.matrixState.client.userID;
|
||||
_userProfilesCache.addAll(
|
||||
response.results.where(
|
||||
(p) =>
|
||||
!_userProfilesCache.any(
|
||||
(element) => p.pangeaUserId == element.pangeaUserId,
|
||||
) &&
|
||||
p.pangeaUserId != currentUserId,
|
||||
),
|
||||
);
|
||||
|
||||
setState(() => loading = false);
|
||||
}
|
||||
|
||||
Future<void> filterUserProfiles({
|
||||
LanguageModel? targetLanguage,
|
||||
LanguageModel? sourceLanguage,
|
||||
Country? country,
|
||||
}) async {
|
||||
if (country != null) {
|
||||
if (country.name != "World Wide") {
|
||||
countrySearch = country.displayNameNoCountryCode;
|
||||
flagEmoji = country.flagEmoji;
|
||||
} else {
|
||||
countrySearch = null;
|
||||
flagEmoji = null;
|
||||
}
|
||||
}
|
||||
if (targetLanguage != null) {
|
||||
targetLanguageSearch = targetLanguage;
|
||||
}
|
||||
if (sourceLanguage != null) {
|
||||
sourceLanguageSearch = sourceLanguage;
|
||||
}
|
||||
nextPage = 1;
|
||||
nextUrl = "";
|
||||
await searchUserProfiles();
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,315 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:country_picker/country_picker.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:matrix/matrix.dart' as matrix;
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/pangea_logo_svg.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/utils/country_display.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/widgets/p_language_dropdown.dart';
|
||||
import 'package:fluffychat/pangea/user/models/user_model.dart';
|
||||
import 'package:fluffychat/pangea/user/widgets/list_placeholder.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import '../../../widgets/profile_bottom_sheet.dart';
|
||||
import 'find_partner.dart';
|
||||
|
||||
class FindPartnerView extends StatelessWidget {
|
||||
final FindPartnerController controller;
|
||||
const FindPartnerView(this.controller, {super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.close_outlined),
|
||||
onPressed: () => context.pop(),
|
||||
),
|
||||
centerTitle: true,
|
||||
title: const PageTitleText(),
|
||||
),
|
||||
body: Center(
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: FluffyThemes.columnWidth * 2,
|
||||
minWidth: FluffyThemes.columnWidth * 2,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
LanguageSelectionRow(
|
||||
controller: controller,
|
||||
isSource: true,
|
||||
),
|
||||
LanguageSelectionRow(
|
||||
controller: controller,
|
||||
isSource: false,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(18),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
L10n.of(context).iWantALanguagePartnerFrom,
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
controller.countrySearch ??
|
||||
L10n.of(context).worldWide,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(15, 0, 15, 0),
|
||||
child: controller.flagEmoji != null
|
||||
? RichText(
|
||||
text: TextSpan(
|
||||
text: controller.flagEmoji,
|
||||
style: const TextStyle(fontSize: 30),
|
||||
),
|
||||
)
|
||||
: const PangeaLogoSvg(width: 30),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.expand_more),
|
||||
onPressed: () => showCountryPicker(
|
||||
showWorldWide: true,
|
||||
context: context,
|
||||
showPhoneCode: false,
|
||||
onSelect: (Country country) {
|
||||
controller.filterUserProfiles(
|
||||
country: country,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
controller.initialLoad
|
||||
? const ExpandedContainer(body: ListPlaceholder())
|
||||
: controller.userProfiles.isNotEmpty
|
||||
? ExpandedContainer(
|
||||
body: ListView.builder(
|
||||
controller: controller.scrollController,
|
||||
itemCount: controller.userProfiles.length + 1,
|
||||
itemBuilder: (context, i) => i !=
|
||||
controller.userProfiles.length
|
||||
? UserProfileEntry(
|
||||
pangeaProfile: controller.userProfiles[i],
|
||||
controller: controller,
|
||||
)
|
||||
: controller.loading
|
||||
? const Center(
|
||||
child: CircularProgressIndicator
|
||||
.adaptive(),
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
)
|
||||
: ExpandedContainer(
|
||||
body: Center(
|
||||
child: Text(L10n.of(context).noResults),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ExpandedContainer extends StatelessWidget {
|
||||
const ExpandedContainer({
|
||||
super.key,
|
||||
required this.body,
|
||||
});
|
||||
|
||||
final Widget body;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Expanded(
|
||||
child: Container(
|
||||
margin: const EdgeInsets.fromLTRB(0, 20, 0, 20),
|
||||
child: body,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ProfileSearchTextField extends StatelessWidget {
|
||||
const ProfileSearchTextField({
|
||||
super.key,
|
||||
required this.controller,
|
||||
});
|
||||
|
||||
final FindPartnerController controller;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextField(
|
||||
autofocus: true,
|
||||
decoration: InputDecoration(
|
||||
hintText: L10n.of(context).searchBy,
|
||||
suffixIconConstraints: const BoxConstraints(
|
||||
maxWidth: 48,
|
||||
maxHeight: 48,
|
||||
minWidth: 48,
|
||||
),
|
||||
suffixIcon: controller.initialLoad
|
||||
? const CircularProgressIndicator.adaptive()
|
||||
: const Icon(Icons.search_outlined),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
),
|
||||
onChanged: controller.searchUserProfilesWithCoolDown,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PageTitleText extends StatelessWidget {
|
||||
const PageTitleText({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FittedBox(
|
||||
child: Text(
|
||||
L10n.of(context).iWantAConversationPartner,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).textTheme.bodyLarge!.color,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
overflow: TextOverflow.clip,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class LanguageSelectionRow extends StatelessWidget {
|
||||
const LanguageSelectionRow({
|
||||
super.key,
|
||||
required this.controller,
|
||||
required this.isSource,
|
||||
});
|
||||
|
||||
final FindPartnerController controller;
|
||||
final bool isSource;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
Flexible(
|
||||
child: ListTile(
|
||||
title: isSource
|
||||
? Text(
|
||||
L10n.of(context).iWantALanguagePartnerWhoSpeaks,
|
||||
style: const TextStyle(fontSize: 16),
|
||||
)
|
||||
: Text(
|
||||
L10n.of(context).iWantALanguagePartnerWhoIsLearning,
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: PLanguageDropdown(
|
||||
languages: isSource
|
||||
? controller.pangeaController.pLanguageStore.baseOptions
|
||||
: controller.pangeaController.pLanguageStore.targetOptions,
|
||||
onChange: (language) {
|
||||
controller.filterUserProfiles(
|
||||
sourceLanguage: isSource ? language : null,
|
||||
targetLanguage: isSource ? null : language,
|
||||
);
|
||||
},
|
||||
isL2List: !isSource,
|
||||
initialLanguage: isSource
|
||||
? controller.sourceLanguageSearch
|
||||
: controller.targetLanguageSearch,
|
||||
decorationText: isSource
|
||||
? L10n.of(context).myBaseLanguage
|
||||
: L10n.of(context).iWantToLearn,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class UserProfileEntry extends StatelessWidget {
|
||||
final PangeaProfile pangeaProfile;
|
||||
final FindPartnerController controller;
|
||||
|
||||
const UserProfileEntry({
|
||||
super.key,
|
||||
required this.pangeaProfile,
|
||||
required this.controller,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
FutureBuilder<matrix.Profile>(
|
||||
future: Matrix.of(context)
|
||||
.client
|
||||
.getProfileFromUserId(pangeaProfile.pangeaUserId),
|
||||
builder: ((context, snapshot) {
|
||||
final matrixProfile = snapshot.data;
|
||||
return ListTile(
|
||||
leading: Avatar(
|
||||
name: matrixProfile == null || matrixProfile.avatarUrl == null
|
||||
? pangeaProfile.pangeaUserId
|
||||
: null,
|
||||
mxContent: matrixProfile?.avatarUrl,
|
||||
),
|
||||
title: Row(
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
//PTODO - get matrix u and show displayName
|
||||
matrixProfile?.displayName ??
|
||||
pangeaProfile.pangeaUserId.replaceAll(
|
||||
":${AppConfig.defaultHomeserver.replaceAll("matrix.", "")}",
|
||||
"",
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
text: CountryDisplayUtil.flagEmoji(pangeaProfile.country),
|
||||
style: const TextStyle(fontSize: 15),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
onTap: () => showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (c) => ProfileBottomSheet(
|
||||
userId: pangeaProfile.pangeaUserId,
|
||||
outerContext: context,
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:http/http.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
import 'package:fluffychat/pangea/common/constants/model_keys.dart';
|
||||
import '../../common/network/requests.dart';
|
||||
import '../../common/network/urls.dart';
|
||||
import '../models/user_profile_search_model.dart';
|
||||
|
||||
class PUserRepo {
|
||||
static Future<UserProfileSearchResponse> searchUserProfiles({
|
||||
// List<String>? interests,
|
||||
String? targetLanguage,
|
||||
String? sourceLanguage,
|
||||
String? country,
|
||||
// String? speaks,
|
||||
String? pageNumber,
|
||||
required String accessToken,
|
||||
required int limit,
|
||||
}) async {
|
||||
final Requests req = Requests(
|
||||
accessToken: accessToken,
|
||||
choreoApiKey: Environment.choreoApiKey,
|
||||
);
|
||||
final Map<String, dynamic> body = {};
|
||||
// if (interests != null) body[ModelKey.userInterests] = interests.toString();
|
||||
if (targetLanguage != null) {
|
||||
body[ModelKey.userTargetLanguage] = targetLanguage;
|
||||
}
|
||||
if (sourceLanguage != null) {
|
||||
body[ModelKey.userSourceLanguage] = sourceLanguage;
|
||||
}
|
||||
if (country != null) body[ModelKey.userCountry] = country;
|
||||
|
||||
final String searchUrl =
|
||||
"${PApiUrls.searchUserProfiles}?limit=$limit${pageNumber != null ? '&page=$pageNumber' : ''}";
|
||||
|
||||
final Response res = await req.post(
|
||||
url: searchUrl,
|
||||
body: body,
|
||||
);
|
||||
|
||||
//PTODO - implement paginiation - make another call with next url
|
||||
|
||||
return UserProfileSearchResponse.fromJson(jsonDecode(res.body));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class ListPlaceholder extends StatelessWidget {
|
||||
static const dummyChatCount = 5;
|
||||
|
||||
const ListPlaceholder({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final titleColor =
|
||||
Theme.of(context).textTheme.bodyLarge!.color!.withAlpha(100);
|
||||
final subtitleColor =
|
||||
Theme.of(context).textTheme.bodyLarge!.color!.withAlpha(50);
|
||||
|
||||
return ListView.builder(
|
||||
itemCount: dummyChatCount,
|
||||
itemBuilder: (context, i) => Opacity(
|
||||
opacity: (dummyChatCount - i) / dummyChatCount,
|
||||
child: Material(
|
||||
child: ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: titleColor,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 1,
|
||||
color: Theme.of(context).textTheme.bodyLarge!.color,
|
||||
),
|
||||
),
|
||||
title: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 14,
|
||||
decoration: BoxDecoration(
|
||||
color: titleColor,
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 36),
|
||||
Container(
|
||||
height: 14,
|
||||
width: 14,
|
||||
decoration: BoxDecoration(
|
||||
color: subtitleColor,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Container(
|
||||
height: 14,
|
||||
width: 14,
|
||||
decoration: BoxDecoration(
|
||||
color: subtitleColor,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: subtitleColor,
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
),
|
||||
height: 12,
|
||||
margin: const EdgeInsets.only(right: 22),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
// // presents choices from vocab_bank_repo
|
||||
// // displays them as emoji choices
|
||||
// // once selection, these words are inserted into the input bar
|
||||
|
||||
// import 'dart:async';
|
||||
|
||||
// import 'package:fluffychat/config/themes.dart';
|
||||
// import 'package:fluffychat/pages/chat/chat.dart';
|
||||
// import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
|
||||
// import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
|
||||
// import 'package:fluffychat/pangea/emojis/emoji_stack.dart';
|
||||
// import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart';
|
||||
// import 'package:fluffychat/pangea/toolbar/reading_assistance_input_row/message_emoji_choice_item.dart';
|
||||
// import 'package:fluffychat/pangea/word_bank/vocab_bank_repo.dart';
|
||||
// import 'package:fluffychat/pangea/word_bank/vocab_request.dart';
|
||||
// import 'package:fluffychat/pangea/word_bank/vocab_response.dart';
|
||||
// import 'package:fluffychat/widgets/matrix.dart';
|
||||
// import 'package:flutter/material.dart';
|
||||
|
||||
// class WritingAssistanceInputRow extends StatefulWidget {
|
||||
// final ChatController controller;
|
||||
|
||||
// const WritingAssistanceInputRow(
|
||||
// this.controller, {
|
||||
// super.key,
|
||||
// });
|
||||
|
||||
// @override
|
||||
// WritingAssistanceInputRowState createState() =>
|
||||
// WritingAssistanceInputRowState();
|
||||
// }
|
||||
|
||||
// class WritingAssistanceInputRowState extends State<WritingAssistanceInputRow> {
|
||||
// List<ConstructIdentifier> suggestions = [];
|
||||
|
||||
// StreamSubscription? _choreoSub;
|
||||
|
||||
// Choreographer get choreographer => widget.controller.choreographer;
|
||||
|
||||
// @override
|
||||
// void initState() {
|
||||
// // Rebuild the widget each time there's an update from choreo
|
||||
// _choreoSub = choreographer.stateListener.stream.listen((_) {
|
||||
// setSuggestions();
|
||||
// });
|
||||
// setSuggestions();
|
||||
// super.initState();
|
||||
// }
|
||||
|
||||
// @override
|
||||
// void dispose() {
|
||||
// _choreoSub?.cancel();
|
||||
// super.dispose();
|
||||
// }
|
||||
|
||||
// Future<void> setSuggestions() async {
|
||||
// final String currentText = choreographer.currentText;
|
||||
|
||||
// final VocabRequest request = VocabRequest(
|
||||
// langCode: MatrixState
|
||||
// .pangeaController.languageController.userL2?.langCodeShort ??
|
||||
// LanguageKeys.defaultLanguage,
|
||||
// level: MatrixState
|
||||
// .pangeaController.userController.profile.userSettings.cefrLevel,
|
||||
// prefix: currentText,
|
||||
// );
|
||||
|
||||
// final VocabResponse response = await VocabRepo.get(request);
|
||||
|
||||
// setState(() {
|
||||
// suggestions = response.vocab;
|
||||
// });
|
||||
// }
|
||||
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return AnimatedContainer(
|
||||
// duration: FluffyThemes.animationDuration,
|
||||
// curve: FluffyThemes.animationCurve,
|
||||
// child: SingleChildScrollView(
|
||||
// scrollDirection: Axis.horizontal,
|
||||
// child: Row(
|
||||
// children: suggestions
|
||||
// .map(
|
||||
// (suggestion) => MessageEmojiChoiceItem(
|
||||
// topContent: EmojiStack(
|
||||
// emoji: suggestion.userSetEmoji,
|
||||
// // suggestion.userSetEmoji ??
|
||||
// // MatrixState
|
||||
// // .pangeaController.getAnalytics.constructListModel
|
||||
// // .getConstructUses(suggestion)
|
||||
// // ?.xpEmoji ??
|
||||
// // AnalyticsConstants.emojiForSeed,
|
||||
// style: const TextStyle(fontSize: 24),
|
||||
// ),
|
||||
// content: suggestion.lemma,
|
||||
// onTap: () {
|
||||
// choreographer.onPredictorSelect(suggestion.lemma);
|
||||
// // setState(() {
|
||||
// // suggestions = [];
|
||||
// // });
|
||||
// },
|
||||
// isSelected: false,
|
||||
// textSize: 16,
|
||||
// greenHighlight: false,
|
||||
// ),
|
||||
// )
|
||||
// .toList(),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
|
@ -5,11 +5,17 @@ import 'package:fluffychat/config/themes.dart';
|
|||
class TwoColumnLayout extends StatelessWidget {
|
||||
final Widget mainView;
|
||||
final Widget sideView;
|
||||
// #Pangea
|
||||
final Color? dividerColor;
|
||||
// Pangea#
|
||||
|
||||
const TwoColumnLayout({
|
||||
super.key,
|
||||
required this.mainView,
|
||||
required this.sideView,
|
||||
// #Pangea
|
||||
this.dividerColor,
|
||||
// Pangea#
|
||||
});
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
@ -27,7 +33,10 @@ class TwoColumnLayout extends StatelessWidget {
|
|||
),
|
||||
Container(
|
||||
width: 1.0,
|
||||
color: theme.dividerColor,
|
||||
// #Pangea
|
||||
// color: theme.dividerColor,
|
||||
color: dividerColor ?? theme.dividerColor,
|
||||
// Pangea#
|
||||
),
|
||||
Expanded(
|
||||
child: ClipRRect(
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import 'package:matrix/matrix.dart';
|
|||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pages/chat_list/navi_rail_item.dart';
|
||||
import 'package:fluffychat/pangea/spaces/utils/space_code.dart';
|
||||
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
|
||||
import 'package:fluffychat/utils/stream_extension.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
|
|
@ -17,11 +16,17 @@ class SpacesNavigationRail extends StatelessWidget {
|
|||
final String? activeSpaceId;
|
||||
final void Function() onGoToChats;
|
||||
final void Function(String) onGoToSpaceId;
|
||||
// #Pangea
|
||||
final void Function()? clearActiveSpace;
|
||||
// Pangea#
|
||||
|
||||
const SpacesNavigationRail({
|
||||
required this.activeSpaceId,
|
||||
required this.onGoToChats,
|
||||
required this.onGoToSpaceId,
|
||||
// #Pangea
|
||||
this.clearActiveSpace,
|
||||
// Pangea#
|
||||
super.key,
|
||||
});
|
||||
|
||||
|
|
@ -35,12 +40,9 @@ class SpacesNavigationRail extends StatelessWidget {
|
|||
.path
|
||||
.startsWith('/rooms/settings');
|
||||
// #Pangea
|
||||
final isHomepage = GoRouter.of(context)
|
||||
.routeInformationProvider
|
||||
.value
|
||||
.uri
|
||||
.path
|
||||
.contains('homepage');
|
||||
final path = GoRouter.of(context).routeInformationProvider.value.uri.path;
|
||||
final isHomepage = path.contains('homepage');
|
||||
final isCommunities = path.contains('communities');
|
||||
final isColumnMode = FluffyThemes.isColumnMode(context);
|
||||
// return StreamBuilder(
|
||||
return Material(
|
||||
|
|
@ -78,18 +80,25 @@ class SpacesNavigationRail extends StatelessWidget {
|
|||
scrollDirection: Axis.vertical,
|
||||
// #Pangea
|
||||
// itemCount: rootSpaces.length + 2,
|
||||
itemCount: rootSpaces.length + 4,
|
||||
itemCount: rootSpaces.length + 3,
|
||||
// Pangea#
|
||||
itemBuilder: (context, i) {
|
||||
// #Pangea
|
||||
if (i == 0) {
|
||||
return NaviRailItem(
|
||||
isSelected: isColumnMode
|
||||
? activeSpaceId == null && !isSettings
|
||||
? activeSpaceId == null &&
|
||||
!isSettings &&
|
||||
!isCommunities
|
||||
: isHomepage,
|
||||
onTap: () => isColumnMode
|
||||
? onGoToChats()
|
||||
: context.go("/rooms/homepage"),
|
||||
onTap: () {
|
||||
if (isColumnMode) {
|
||||
onGoToChats();
|
||||
} else {
|
||||
clearActiveSpace?.call();
|
||||
context.go("/rooms/homepage");
|
||||
}
|
||||
},
|
||||
backgroundColor: Colors.transparent,
|
||||
icon: FutureBuilder<Profile>(
|
||||
future: client.fetchOwnProfile(),
|
||||
|
|
@ -122,7 +131,8 @@ class SpacesNavigationRail extends StatelessWidget {
|
|||
// isSelected: activeSpaceId == null && !isSettings,
|
||||
isSelected: activeSpaceId == null &&
|
||||
!isSettings &&
|
||||
!isHomepage,
|
||||
!isHomepage &&
|
||||
!isCommunities,
|
||||
// Pangea#
|
||||
onTap: onGoToChats,
|
||||
icon: const Padding(
|
||||
|
|
@ -139,28 +149,26 @@ class SpacesNavigationRail extends StatelessWidget {
|
|||
}
|
||||
i--;
|
||||
if (i == rootSpaces.length) {
|
||||
// #Pangea
|
||||
return NaviRailItem(
|
||||
isSelected: false,
|
||||
onTap: () =>
|
||||
SpaceCodeUtil.joinWithSpaceCodeDialog(context),
|
||||
// #Pangea
|
||||
// isSelected: false,
|
||||
// onTap: () => context.go('/rooms/newspace'),
|
||||
// icon: const Padding(
|
||||
// padding: EdgeInsets.all(8.0),
|
||||
// child: Icon(Icons.add),
|
||||
// ),
|
||||
// toolTip: L10n.of(context).createNewSpace,
|
||||
isSelected: isCommunities,
|
||||
onTap: () {
|
||||
clearActiveSpace?.call();
|
||||
context.go('/rooms/communities');
|
||||
},
|
||||
icon: const Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: Icon(Icons.join_right_outlined),
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: Icon(Icons.groups),
|
||||
),
|
||||
toolTip: L10n.of(context).joinByCode,
|
||||
);
|
||||
}
|
||||
if (i == rootSpaces.length + 1) {
|
||||
// Pangea#
|
||||
return NaviRailItem(
|
||||
isSelected: false,
|
||||
onTap: () => context.go('/rooms/newspace'),
|
||||
icon: const Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: Icon(Icons.add),
|
||||
),
|
||||
toolTip: L10n.of(context).createNewSpace,
|
||||
toolTip: L10n.of(context).findYourPeople,
|
||||
// Pangea#
|
||||
);
|
||||
}
|
||||
final space = rootSpaces[i];
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue