mirror of
https://github.com/krille-chan/fluffychat
synced 2024-09-10 13:05:11 +00:00
feat: Search for public spaces
This commit is contained in:
parent
ef249c6f6e
commit
715e615e3a
5 changed files with 110 additions and 59 deletions
|
@ -2385,5 +2385,8 @@
|
||||||
"newPassword": "New password",
|
"newPassword": "New password",
|
||||||
"pleaseChooseAStrongPassword": "Please choose a strong password",
|
"pleaseChooseAStrongPassword": "Please choose a strong password",
|
||||||
"passwordsDoNotMatch": "Passwords do not match",
|
"passwordsDoNotMatch": "Passwords do not match",
|
||||||
"passwordIsWrong": "Your entered password is wrong"
|
"passwordIsWrong": "Your entered password is wrong",
|
||||||
|
"publicLink": "Public link",
|
||||||
|
"joinSpace": "Join space",
|
||||||
|
"publicSpaces": "Public spaces"
|
||||||
}
|
}
|
|
@ -235,8 +235,10 @@ class ChatDetailsView extends StatelessWidget {
|
||||||
? L10n.of(context)!.noChatDescriptionYet
|
? L10n.of(context)!.noChatDescriptionYet
|
||||||
: room.topic,
|
: room.topic,
|
||||||
options: const LinkifyOptions(humanize: false),
|
options: const LinkifyOptions(humanize: false),
|
||||||
linkStyle:
|
linkStyle: const TextStyle(
|
||||||
const TextStyle(color: Colors.blueAccent),
|
color: Colors.blueAccent,
|
||||||
|
decorationColor: Colors.blueAccent,
|
||||||
|
),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontStyle: room.topic.isEmpty
|
fontStyle: room.topic.isEmpty
|
||||||
|
|
|
@ -28,7 +28,12 @@ class ChatListViewBody extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final roomSearchResult = controller.roomSearchResult;
|
final publicRooms = controller.roomSearchResult?.chunk
|
||||||
|
.where((room) => room.roomType != 'm.space')
|
||||||
|
.toList();
|
||||||
|
final publicSpaces = controller.roomSearchResult?.chunk
|
||||||
|
.where((room) => room.roomType == 'm.space')
|
||||||
|
.toList();
|
||||||
final userSearchResult = controller.userSearchResult;
|
final userSearchResult = controller.userSearchResult;
|
||||||
final client = Matrix.of(context).client;
|
final client = Matrix.of(context).client;
|
||||||
const dummyChatCount = 4;
|
const dummyChatCount = 4;
|
||||||
|
@ -83,39 +88,12 @@ class ChatListViewBody extends StatelessWidget {
|
||||||
title: L10n.of(context)!.publicRooms,
|
title: L10n.of(context)!.publicRooms,
|
||||||
icon: const Icon(Icons.explore_outlined),
|
icon: const Icon(Icons.explore_outlined),
|
||||||
),
|
),
|
||||||
AnimatedContainer(
|
PublicRoomsHorizontalList(publicRooms: publicRooms),
|
||||||
clipBehavior: Clip.hardEdge,
|
SearchTitle(
|
||||||
decoration: const BoxDecoration(),
|
title: L10n.of(context)!.publicSpaces,
|
||||||
height: roomSearchResult == null ||
|
icon: const Icon(Icons.workspaces_outlined),
|
||||||
roomSearchResult.chunk.isEmpty
|
|
||||||
? 0
|
|
||||||
: 106,
|
|
||||||
duration: FluffyThemes.animationDuration,
|
|
||||||
curve: FluffyThemes.animationCurve,
|
|
||||||
child: roomSearchResult == null
|
|
||||||
? null
|
|
||||||
: ListView.builder(
|
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
itemCount: roomSearchResult.chunk.length,
|
|
||||||
itemBuilder: (context, i) => _SearchItem(
|
|
||||||
title: roomSearchResult.chunk[i].name ??
|
|
||||||
roomSearchResult.chunk[i].canonicalAlias
|
|
||||||
?.localpart ??
|
|
||||||
L10n.of(context)!.group,
|
|
||||||
avatar: roomSearchResult.chunk[i].avatarUrl,
|
|
||||||
onPressed: () => showAdaptiveBottomSheet(
|
|
||||||
context: context,
|
|
||||||
builder: (c) => PublicRoomBottomSheet(
|
|
||||||
roomAlias: roomSearchResult
|
|
||||||
.chunk[i].canonicalAlias ??
|
|
||||||
roomSearchResult.chunk[i].roomId,
|
|
||||||
outerContext: context,
|
|
||||||
chunk: roomSearchResult.chunk[i],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
PublicRoomsHorizontalList(publicRooms: publicSpaces),
|
||||||
SearchTitle(
|
SearchTitle(
|
||||||
title: L10n.of(context)!.users,
|
title: L10n.of(context)!.users,
|
||||||
icon: const Icon(Icons.group_outlined),
|
icon: const Icon(Icons.group_outlined),
|
||||||
|
@ -294,6 +272,48 @@ class ChatListViewBody extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PublicRoomsHorizontalList extends StatelessWidget {
|
||||||
|
const PublicRoomsHorizontalList({
|
||||||
|
super.key,
|
||||||
|
required this.publicRooms,
|
||||||
|
});
|
||||||
|
|
||||||
|
final List<PublicRoomsChunk>? publicRooms;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final publicRooms = this.publicRooms;
|
||||||
|
return AnimatedContainer(
|
||||||
|
clipBehavior: Clip.hardEdge,
|
||||||
|
decoration: const BoxDecoration(),
|
||||||
|
height: publicRooms == null || publicRooms.isEmpty ? 0 : 106,
|
||||||
|
duration: FluffyThemes.animationDuration,
|
||||||
|
curve: FluffyThemes.animationCurve,
|
||||||
|
child: publicRooms == null
|
||||||
|
? null
|
||||||
|
: ListView.builder(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemCount: publicRooms.length,
|
||||||
|
itemBuilder: (context, i) => _SearchItem(
|
||||||
|
title: publicRooms[i].name ??
|
||||||
|
publicRooms[i].canonicalAlias?.localpart ??
|
||||||
|
L10n.of(context)!.group,
|
||||||
|
avatar: publicRooms[i].avatarUrl,
|
||||||
|
onPressed: () => showAdaptiveBottomSheet(
|
||||||
|
context: context,
|
||||||
|
builder: (c) => PublicRoomBottomSheet(
|
||||||
|
roomAlias:
|
||||||
|
publicRooms[i].canonicalAlias ?? publicRooms[i].roomId,
|
||||||
|
outerContext: context,
|
||||||
|
chunk: publicRooms[i],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _SearchItem extends StatelessWidget {
|
class _SearchItem extends StatelessWidget {
|
||||||
final String title;
|
final String title;
|
||||||
final Uri? avatar;
|
final Uri? avatar;
|
||||||
|
|
|
@ -211,7 +211,10 @@ class UserBottomSheetView extends StatelessWidget {
|
||||||
text: status,
|
text: status,
|
||||||
style: const TextStyle(fontSize: 16),
|
style: const TextStyle(fontSize: 16),
|
||||||
options: const LinkifyOptions(humanize: false),
|
options: const LinkifyOptions(humanize: false),
|
||||||
linkStyle: const TextStyle(color: Colors.blueAccent),
|
linkStyle: const TextStyle(
|
||||||
|
color: Colors.blueAccent,
|
||||||
|
decorationColor: Colors.blueAccent,
|
||||||
|
),
|
||||||
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
|
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,6 +6,7 @@ import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
|
import 'package:fluffychat/utils/fluffy_share.dart';
|
||||||
import 'package:fluffychat/utils/url_launcher.dart';
|
import 'package:fluffychat/utils/url_launcher.dart';
|
||||||
import 'package:fluffychat/widgets/avatar.dart';
|
import 'package:fluffychat/widgets/avatar.dart';
|
||||||
import 'package:fluffychat/widgets/matrix.dart';
|
import 'package:fluffychat/widgets/matrix.dart';
|
||||||
|
@ -41,9 +42,7 @@ class PublicRoomBottomSheet extends StatelessWidget {
|
||||||
: await client.joinRoom(roomAlias ?? chunk!.roomId);
|
: await client.joinRoom(roomAlias ?? chunk!.roomId);
|
||||||
|
|
||||||
if (client.getRoomById(roomId) == null) {
|
if (client.getRoomById(roomId) == null) {
|
||||||
await client.onSync.stream.firstWhere(
|
await client.waitForRoomInSync(roomId);
|
||||||
(sync) => sync.rooms?.join?.containsKey(roomId) ?? false,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return roomId;
|
return roomId;
|
||||||
},
|
},
|
||||||
|
@ -51,7 +50,8 @@ class PublicRoomBottomSheet extends StatelessWidget {
|
||||||
if (result.error == null) {
|
if (result.error == null) {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
// don't open the room if the joined room is a space
|
// don't open the room if the joined room is a space
|
||||||
if (!client.getRoomById(result.result!)!.isSpace) {
|
if (chunk?.roomType != 'm.space' &&
|
||||||
|
!client.getRoomById(result.result!)!.isSpace) {
|
||||||
outerContext.go('/rooms/${result.result!}');
|
outerContext.go('/rooms/${result.result!}');
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -77,12 +77,12 @@ class PublicRoomBottomSheet extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final roomAlias = this.roomAlias;
|
final roomAlias = this.roomAlias ?? chunk?.canonicalAlias;
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(
|
title: Text(
|
||||||
roomAlias ?? chunk!.name ?? chunk!.roomId,
|
chunk!.name ?? roomAlias ?? chunk!.roomId,
|
||||||
overflow: TextOverflow.fade,
|
overflow: TextOverflow.fade,
|
||||||
),
|
),
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
|
@ -92,11 +92,13 @@ class PublicRoomBottomSheet extends StatelessWidget {
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||||
child: OutlinedButton.icon(
|
child: IconButton(
|
||||||
onPressed: () => _joinRoom(context),
|
icon: Icon(Icons.adaptive.share_outlined),
|
||||||
label: Text(L10n.of(context)!.joinRoom),
|
onPressed: () => FluffyShare.share(
|
||||||
icon: const Icon(Icons.login_outlined),
|
'https://matrix.to/#/${roomAlias ?? chunk?.roomId}',
|
||||||
|
context,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -131,29 +133,50 @@ class PublicRoomBottomSheet extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||||
|
child: ElevatedButton.icon(
|
||||||
|
onPressed: () => _joinRoom(context),
|
||||||
|
label: Text(
|
||||||
|
chunk?.roomType == 'm.space'
|
||||||
|
? L10n.of(context)!.joinSpace
|
||||||
|
: L10n.of(context)!.joinRoom,
|
||||||
|
),
|
||||||
|
icon: const Icon(Icons.login_outlined),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(
|
title: Text(
|
||||||
profile?.name ??
|
profile?.name ?? chunk!.roomId.localpart ?? '',
|
||||||
roomAlias?.localpart ??
|
|
||||||
chunk!.roomId.localpart ??
|
|
||||||
'',
|
|
||||||
),
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
'${L10n.of(context)!.participant}: ${profile?.numJoinedMembers ?? 0}',
|
'${L10n.of(context)!.participant}: ${profile?.numJoinedMembers ?? 0}',
|
||||||
),
|
),
|
||||||
trailing: const Icon(Icons.account_box_outlined),
|
trailing: const Icon(Icons.account_box_outlined),
|
||||||
),
|
),
|
||||||
if (profile?.topic?.isNotEmpty ?? false)
|
if (roomAlias != null)
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(
|
title: Text(L10n.of(context)!.publicLink),
|
||||||
L10n.of(context)!.chatDescription,
|
subtitle: SelectableText(roomAlias),
|
||||||
style: TextStyle(
|
contentPadding:
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
const EdgeInsets.symmetric(horizontal: 16.0),
|
||||||
|
trailing: IconButton(
|
||||||
|
icon: const Icon(Icons.copy_outlined),
|
||||||
|
onPressed: () => FluffyShare.share(
|
||||||
|
roomAlias,
|
||||||
|
context,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
subtitle: Linkify(
|
),
|
||||||
|
if (profile?.topic?.isNotEmpty ?? false)
|
||||||
|
ListTile(
|
||||||
|
subtitle: SelectableLinkify(
|
||||||
text: profile!.topic!,
|
text: profile!.topic!,
|
||||||
linkStyle: const TextStyle(color: Colors.blueAccent),
|
linkStyle: const TextStyle(
|
||||||
|
color: Colors.blueAccent,
|
||||||
|
decorationColor: Colors.blueAccent,
|
||||||
|
),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
color: Theme.of(context).textTheme.bodyMedium!.color,
|
color: Theme.of(context).textTheme.bodyMedium!.color,
|
||||||
|
|
Loading…
Reference in a new issue