fluffychat merge - resolve conflicts

pull/1384/head
ggurdin 1 year ago
commit cca5bb382f
No known key found for this signature in database
GPG Key ID: A01CB41737CBB478

@ -4180,5 +4180,6 @@
"changeTheVisibilityOfChatHistory": "Change the visibility of the chat history",
"changeTheCanonicalRoomAlias": "Change the main public chat address",
"sendRoomNotifications": "Send a @room notifications",
"changeTheDescriptionOfTheGroup": "Change the description of the chat"
"changeTheDescriptionOfTheGroup": "Change the description of the chat",
"chatPermissionsDescription": "Define which power level is necessary for certain actions in this chat. The power levels 0, 50 and 100 are usually representing users, moderators and admins, but any gradation is possible."
}

@ -19,7 +19,7 @@ abstract class AppConfig {
static double fontSizeFactor = 1;
static const Color chatColor = primaryColor;
static Color? colorSchemeSeed = primaryColor;
static const double messageFontSize = 15.75;
static const double messageFontSize = 16.0;
static const bool allowOtherHomeservers = true;
static const bool enableRegistration = true;
// #Pangea

@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/utils/url_launcher.dart';
class ChatAppBarListTile extends StatelessWidget {
@ -11,6 +10,8 @@ class ChatAppBarListTile extends StatelessWidget {
final Widget? trailing;
final void Function()? onTap;
static const double fixedHeight = 40.0;
const ChatAppBarListTile({
super.key,
this.leading,
@ -23,38 +24,40 @@ class ChatAppBarListTile extends StatelessWidget {
Widget build(BuildContext context) {
final leading = this.leading;
final trailing = this.trailing;
final fontSize = AppConfig.messageFontSize * AppConfig.fontSizeFactor;
return InkWell(
onTap: onTap,
child: Row(
children: [
if (leading != null) leading,
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 4.0),
child: Linkify(
text: title,
options: const LinkifyOptions(humanize: false),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Theme.of(context).colorScheme.onSurfaceVariant,
return SizedBox(
height: fixedHeight,
child: InkWell(
onTap: onTap,
child: Row(
children: [
if (leading != null) leading,
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 4.0),
child: Linkify(
text: title,
options: const LinkifyOptions(humanize: false),
maxLines: 1,
overflow: TextOverflow.ellipsis,
fontSize: fontSize,
),
linkStyle: TextStyle(
color: Theme.of(context).colorScheme.onSurfaceVariant,
fontSize: fontSize,
decoration: TextDecoration.underline,
decorationColor:
Theme.of(context).colorScheme.onSurfaceVariant,
style: TextStyle(
color: Theme.of(context).colorScheme.onSurfaceVariant,
overflow: TextOverflow.ellipsis,
fontSize: 14,
),
linkStyle: TextStyle(
color: Theme.of(context).colorScheme.onSurfaceVariant,
fontSize: 14,
decoration: TextDecoration.underline,
decorationColor:
Theme.of(context).colorScheme.onSurfaceVariant,
),
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
),
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
),
),
),
if (trailing != null) trailing,
],
if (trailing != null) trailing,
],
),
),
);
}

@ -178,15 +178,15 @@ class ChatView extends StatelessWidget {
builder: (BuildContext context, snapshot) {
var appbarBottomHeight = 0.0;
if (controller.room.pinnedEventIds.isNotEmpty) {
appbarBottomHeight += 42;
appbarBottomHeight += ChatAppBarListTile.fixedHeight;
}
if (scrollUpBannerEventId != null) {
appbarBottomHeight += 42;
appbarBottomHeight += ChatAppBarListTile.fixedHeight;
}
final tombstoneEvent =
controller.room.getState(EventTypes.RoomTombstone);
if (tombstoneEvent != null) {
appbarBottomHeight += 42;
appbarBottomHeight += ChatAppBarListTile.fixedHeight;
}
return Scaffold(
appBar: AppBar(

@ -557,22 +557,20 @@ class Message extends StatelessWidget {
? const EdgeInsets.symmetric(vertical: 8.0)
: EdgeInsets.zero,
child: Center(
child: Material(
color: displayTime
? Theme.of(context).colorScheme.surface
: Theme.of(context).colorScheme.surface.withOpacity(0.33),
borderRadius:
BorderRadius.circular(AppConfig.borderRadius / 2),
clipBehavior: Clip.antiAlias,
child: Padding(
padding: const EdgeInsets.only(top: 4.0),
child: Text(
event.originServerTs.localizedTime(context),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 12 * AppConfig.fontSizeFactor,
color: Theme.of(context).colorScheme.secondary,
),
child: Padding(
padding: const EdgeInsets.only(top: 4.0),
child: Text(
event.originServerTs.localizedTime(context),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 12 * AppConfig.fontSizeFactor,
color: Theme.of(context).colorScheme.secondary,
shadows: [
Shadow(
color: Theme.of(context).colorScheme.surface,
blurRadius: 3,
),
],
),
),
),

@ -17,10 +17,6 @@ class StateMessage extends StatelessWidget {
child: Center(
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2),
),
child: Text(
event.calcLocalizedBodyFallback(
MatrixLocals(L10n.of(context)!),
@ -29,6 +25,12 @@ class StateMessage extends StatelessWidget {
style: TextStyle(
fontSize: 12 * AppConfig.fontSizeFactor,
decoration: event.redacted ? TextDecoration.lineThrough : null,
shadows: [
Shadow(
color: Theme.of(context).colorScheme.surface,
blurRadius: 3,
),
],
),
),
),

@ -71,8 +71,8 @@ class PinnedEvents extends StatelessWidget {
) ??
L10n.of(context)!.loadingPleaseWait,
leading: IconButton(
splashRadius: 20,
iconSize: 20,
splashRadius: 18,
iconSize: 18,
color: Theme.of(context).colorScheme.onSurfaceVariant,
icon: const Icon(Icons.push_pin),
tooltip: L10n.of(context)!.unpin,

@ -32,10 +32,7 @@ class ChatDetailsView extends StatelessWidget {
@override
Widget build(BuildContext context) {
final room = Matrix.of(context).client.getRoomById(controller.roomId!);
// #Pangea
if (room == null || room.membership == Membership.leave) {
// if (room == null) {
// Pangea#
if (room == null) {
return Scaffold(
appBar: AppBar(
title: Text(L10n.of(context)!.oopsSomethingWentWrong),
@ -62,31 +59,20 @@ class ChatDetailsView extends StatelessWidget {
);
return Scaffold(
appBar: AppBar(
leading:
// #Pangea
!room.isSpace
?
// Pangea#
controller.widget.embeddedCloseButton ??
const Center(child: BackButton())
// #Pangea
: BackButton(
onPressed: () => context.go("/rooms"),
)
// Pangea#
,
leading: controller.widget.embeddedCloseButton ??
const Center(child: BackButton()),
elevation: Theme.of(context).appBarTheme.elevation,
actions: <Widget>[
// #Pangea
// if (room.canonicalAlias.isNotEmpty)
// IconButton(
// tooltip: L10n.of(context)!.share,
// icon: Icon(Icons.adaptive.share_outlined),
// onPressed: () => FluffyShare.share(
// AppConfig.inviteLinkPrefix + room.canonicalAlias,
// context,
// IconButton(
// tooltip: L10n.of(context)!.share,
// icon: Icon(Icons.adaptive.share_outlined),
// onPressed: () => FluffyShare.share(
// AppConfig.inviteLinkPrefix + room.canonicalAlias,
// context,
// ),
// ),
// ),
// Pangea#
if (controller.widget.embeddedCloseButton == null)
ChatSettingsPopupMenu(room, false),
@ -122,32 +108,12 @@ class ChatDetailsView extends StatelessWidget {
padding: const EdgeInsets.all(32.0),
child: Stack(
children: [
Material(
elevation: Theme.of(context)
.appBarTheme
.scrolledUnderElevation ??
4,
shadowColor: Theme.of(context)
.appBarTheme
.shadowColor,
shape: RoundedRectangleBorder(
side: BorderSide(
color: Theme.of(context).dividerColor,
),
borderRadius: BorderRadius.circular(
Avatar.defaultSize * 2.5,
),
),
// #Pangea
// Hero animation is causing weird visual glitch
// Probably not worth keeping
// child: Hero(
// tag: controller.widget
// .embeddedCloseButton !=
// null
// ? 'embedded_content_banner'
// : 'content_banner',
// Pangea#
Hero(
tag: controller
.widget.embeddedCloseButton !=
null
? 'embedded_content_banner'
: 'content_banner',
child: Avatar(
mxContent: room.avatar,
name: displayname,
@ -211,7 +177,7 @@ class ChatDetailsView extends StatelessWidget {
: displayname,
maxLines: 1,
overflow: TextOverflow.ellipsis,
// style: const TextStyle(fontSize: 18),
style: const TextStyle(fontSize: 18),
),
),
TextButton.icon(
@ -243,13 +209,9 @@ class ChatDetailsView extends StatelessWidget {
),
],
),
Divider(
height: 1,
color: Theme.of(context).dividerColor,
),
// if (room.canSendEvent('m.room.name'))
// #Pangea
if (room.isRoomAdmin)
// #Pangea
ClassNameButton(
room: room,
controller: controller,
@ -259,32 +221,18 @@ class ChatDetailsView extends StatelessWidget {
room: room,
controller: controller,
),
// #Pangea
RoomCapacityButton(
room: room,
controller: controller,
),
// Pangea#
// commenting out language settings in spaces for now
// if (room.languageSettings != null && room.isRoomAdmin)
// LanguageSettings(
// roomId: controller.roomId,
// startOpen: false,
// ),
// Commenting out pangea room rules for now
// if (room.pangeaRoomRules != null)
// RoomRulesEditor(
// roomId: controller.roomId,
// startOpen: false,
// ),
// Divider(color: Theme.of(context).dividerColor),
// if (!room.canChangeStateEvent(EventTypes.RoomTopic))
// ListTile(
// title: Text(
// L10n.of(context)!.chatDescription,
// style: TextStyle(
// color: Theme.of(context).colorScheme.secondary,
// color:
// Theme.of(context).colorScheme.secondary,
// fontWeight: FontWeight.bold,
// ),
// ),
@ -294,7 +242,8 @@ class ChatDetailsView extends StatelessWidget {
// padding: const EdgeInsets.all(16.0),
// child: TextButton.icon(
// onPressed: controller.setTopicAction,
// label: Text(L10n.of(context)!.setChatDescription),
// label:
// Text(L10n.of(context)!.setChatDescription),
// icon: const Icon(Icons.edit_outlined),
// style: TextButton.styleFrom(
// backgroundColor: Theme.of(context)
@ -324,20 +273,21 @@ class ChatDetailsView extends StatelessWidget {
// fontStyle: room.topic.isEmpty
// ? FontStyle.italic
// : FontStyle.normal,
// color:
// Theme.of(context).textTheme.bodyMedium!.color,
// decorationColor:
// Theme.of(context).textTheme.bodyMedium!.color,
// color: Theme.of(context)
// .textTheme
// .bodyMedium!
// .color,
// decorationColor: Theme.of(context)
// .textTheme
// .bodyMedium!
// .color,
// ),
// onOpen: (url) =>
// UrlLauncher(context, url.url).launchUrl(),
// ),
// ),
// const SizedBox(height: 16),
// Divider(
// height: 1,
// color: Theme.of(context).dividerColor,
// ),
// Divider(color: Theme.of(context).dividerColor),
// ListTile(
// leading: CircleAvatar(
// backgroundColor:
@ -354,23 +304,25 @@ class ChatDetailsView extends StatelessWidget {
// trailing: const Icon(Icons.chevron_right_outlined),
// ),
// if (!room.isDirectChat)
// ListTile(
// leading: CircleAvatar(
// backgroundColor:
// Theme.of(context).scaffoldBackgroundColor,
// foregroundColor: iconColor,
// child: const Icon(Icons.shield_outlined),
// ),
// title: Text(
// L10n.of(context)!.accessAndVisibility,
// ),
// subtitle: Text(
// L10n.of(context)!.accessAndVisibilityDescription,
// ListTile(
// leading: CircleAvatar(
// backgroundColor:
// Theme.of(context).scaffoldBackgroundColor,
// foregroundColor: iconColor,
// child: const Icon(Icons.shield_outlined),
// ),
// title: Text(
// L10n.of(context)!.accessAndVisibility,
// ),
// subtitle: Text(
// L10n.of(context)!
// .accessAndVisibilityDescription,
// ),
// onTap: () => context
// .push('/rooms/${room.id}/details/access'),
// trailing:
// const Icon(Icons.chevron_right_outlined),
// ),
// onTap: () => context
// .push('/rooms/${room.id}/details/access'),
// trailing: const Icon(Icons.chevron_right_outlined),
// ),
// if (!room.isDirectChat)
if (!room.isDirectChat &&
!room.isSpace &&
@ -399,17 +351,13 @@ class ChatDetailsView extends StatelessWidget {
Icons.edit_attributes_outlined,
),
),
// #Pangea
// trailing: const Icon(Icons.chevron_right_outlined),
// Pangea#
trailing:
const Icon(Icons.chevron_right_outlined),
onTap: () => context.push(
'/rooms/${room.id}/details/permissions',
),
),
Divider(
height: 1,
color: Theme.of(context).dividerColor,
),
Divider(color: Theme.of(context).dividerColor),
// #Pangea
if (room.canInvite &&
!room.isDirectChat &&
@ -486,8 +434,8 @@ class ChatDetailsView extends StatelessWidget {
),
),
onTap: () async {
OkCancelResult confirmed = OkCancelResult.ok;
bool shouldGo = false;
var confirmed = OkCancelResult.ok;
var shouldGo = false;
// archiveSpace has its own popup; only show if not space
if (!room.isSpace) {
confirmed = await showOkCancelAlertDialog(
@ -539,10 +487,10 @@ class ChatDetailsView extends StatelessWidget {
),
),
onTap: () async {
OkCancelResult confirmed = OkCancelResult.ok;
bool shouldGo = false;
var confirmed = OkCancelResult.ok;
var shouldGo = false;
// If user is only admin, room will be archived
final bool onlyAdmin = await room.isOnlyAdmin();
final onlyAdmin = await room.isOnlyAdmin();
// archiveSpace has its own popup; only show if not space
if (!room.isSpace) {
confirmed = await showOkCancelAlertDialog(
@ -649,8 +597,10 @@ class ChatDetailsView extends StatelessWidget {
// radius: Avatar.defaultSize / 2,
// child: const Icon(Icons.add_outlined),
// ),
// trailing: const Icon(Icons.chevron_right_outlined),
// onTap: () => context.go('/rooms/${room.id}/invite'),
// trailing:
// const Icon(Icons.chevron_right_outlined),
// onTap: () =>
// context.go('/rooms/${room.id}/invite'),
// ),
// Pangea#
],

@ -831,9 +831,10 @@ class ChatListController extends State<ChatList>
isDestructiveAction: true,
);
if (confirmed == OkCancelResult.cancel) return;
if (!mounted) {
await showFutureLoadingDialog(context: context, future: room.leave);
}
if (!mounted) return;
await showFutureLoadingDialog(context: context, future: room.leave);
return;
}
}

@ -33,8 +33,8 @@ class NaviRailItem extends StatelessWidget {
return HoverBuilder(
builder: (context, hovered) {
return SizedBox(
height: 64,
width: 64,
height: FluffyThemes.navRailWidth,
width: FluffyThemes.navRailWidth,
child: Stack(
children: [
Positioned(

@ -3,10 +3,12 @@ import 'package:collection/collection.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pages/chat_list/chat_list_item.dart';
import 'package:fluffychat/pages/chat_list/search_title.dart';
import 'package:fluffychat/utils/adaptive_bottom_sheet.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart';
import 'package:fluffychat/utils/stream_extension.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:fluffychat/widgets/public_room_bottom_sheet.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
@ -93,38 +95,23 @@ class _SpaceViewState extends State<SpaceView> {
final client = Matrix.of(context).client;
final space = client.getRoomById(widget.spaceId);
final consent = await showOkCancelAlertDialog(
final joined = await showAdaptiveBottomSheet<bool>(
context: context,
title: item.name ?? item.canonicalAlias ?? L10n.of(context)!.emptyChat,
message: item.topic,
okLabel: L10n.of(context)!.joinRoom,
cancelLabel: L10n.of(context)!.cancel,
);
if (consent != OkCancelResult.ok) return;
if (!mounted) return;
await showFutureLoadingDialog(
context: context,
future: () async {
await client.joinRoom(
item.roomId,
serverName: space?.spaceChildren
.firstWhereOrNull(
(child) => child.roomId == item.roomId,
)
?.via,
);
if (client.getRoomById(item.roomId) == null) {
// Wait for room actually appears in sync
await client.waitForRoomInSync(item.roomId, join: true);
}
},
builder: (_) => PublicRoomBottomSheet(
outerContext: context,
chunk: item,
via: space?.spaceChildren
.firstWhereOrNull(
(child) => child.roomId == item.roomId,
)
?.via,
),
);
if (!mounted) return;
setState(() {
_discoveredChildren.remove(item);
});
if (mounted && joined == true) {
setState(() {
_discoveredChildren.remove(item);
});
}
}
void _onSpaceAction(SpaceActions action) async {

@ -57,6 +57,22 @@ class ChatPermissionsSettingsView extends StatelessWidget {
// Pangea#
return Column(
children: [
ListTile(
leading: const Icon(Icons.info_outlined),
subtitle: Text(
L10n.of(context)!.chatPermissionsDescription,
),
),
Divider(color: Theme.of(context).dividerColor),
ListTile(
title: Text(
L10n.of(context)!.chatPermissions,
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
fontWeight: FontWeight.bold,
),
),
),
Column(
mainAxisSize: MainAxisSize.min,
children: [

@ -80,11 +80,8 @@ class PermissionsListTile extends StatelessWidget {
style: Theme.of(context).textTheme.titleSmall,
),
trailing: Material(
color: color.withAlpha(64),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2),
side: BorderSide(color: color),
),
color: color.withAlpha(32),
borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2),
child: DropdownButton<int>(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2),
@ -94,15 +91,24 @@ class PermissionsListTile extends StatelessWidget {
items: [
DropdownMenuItem(
value: permission < 50 ? permission : 0,
child: Text(L10n.of(context)!.userLevel(permission)),
child: Text(
L10n.of(context)!.userLevel(permission < 50 ? permission : 0),
),
),
DropdownMenuItem(
value: permission < 100 && permission >= 50 ? permission : 50,
child: Text(L10n.of(context)!.moderatorLevel(permission)),
child: Text(
L10n.of(context)!.moderatorLevel(
permission < 100 && permission >= 50 ? permission : 50,
),
),
),
DropdownMenuItem(
value: permission >= 100 ? permission : 100,
child: Text(L10n.of(context)!.adminLevel(permission)),
child: Text(
L10n.of(context)!
.adminLevel(permission >= 100 ? permission : 100),
),
),
DropdownMenuItem(
value: null,

@ -129,10 +129,6 @@ class NewGroupView extends StatelessWidget {
// child: SizedBox(
// width: double.infinity,
// child: ElevatedButton(
// style: ElevatedButton.styleFrom(
// foregroundColor: Theme.of(context).colorScheme.onPrimary,
// backgroundColor: Theme.of(context).colorScheme.primary,
// ),
// onPressed:
// controller.loading ? null : controller.submitAction,
// child: controller.loading

@ -95,10 +95,6 @@ class NewSpaceView extends StatelessWidget {
// child: SizedBox(
// width: double.infinity,
// child: ElevatedButton(
// style: ElevatedButton.styleFrom(
// foregroundColor: Theme.of(context).colorScheme.onPrimary,
// backgroundColor: Theme.of(context).colorScheme.primary,
// ),
// onPressed:
// controller.loading ? null : controller.submitAction,
// child: controller.loading

@ -8,7 +8,7 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart';
import 'package:package_info_plus/package_info_plus.dart'; //adding to check app version
import 'package:package_info_plus/package_info_plus.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'settings.dart';
@ -20,7 +20,7 @@ class SettingsView extends StatelessWidget {
// #Pangea
Future<String> getAppVersion(BuildContext context) async {
final PackageInfo packageInfo = await PackageInfo.fromPlatform();
final packageInfo = await PackageInfo.fromPlatform();
return L10n.of(context)!
.versionText(packageInfo.version, packageInfo.buildNumber);
}
@ -28,9 +28,7 @@ class SettingsView extends StatelessWidget {
@override
Widget build(BuildContext context) {
// #Pangea
// final showChatBackupBanner = controller.showChatBackupBanner;
// Pangea#
final showChatBackupBanner = controller.showChatBackupBanner;
return Scaffold(
appBar: AppBar(
leading: Center(
@ -39,13 +37,6 @@ class SettingsView extends StatelessWidget {
),
),
title: Text(L10n.of(context)!.settings),
actions: [
TextButton.icon(
onPressed: controller.logoutAction,
label: Text(L10n.of(context)!.logout),
icon: const Icon(Icons.logout_outlined),
),
],
),
body: ListTileTheme(
iconColor: Theme.of(context).colorScheme.onSurface,
@ -66,32 +57,17 @@ class SettingsView extends StatelessWidget {
padding: const EdgeInsets.all(32.0),
child: Stack(
children: [
Material(
elevation: Theme.of(context)
.appBarTheme
.scrolledUnderElevation ??
4,
shadowColor:
Theme.of(context).appBarTheme.shadowColor,
shape: RoundedRectangleBorder(
side: BorderSide(
color: Theme.of(context).dividerColor,
),
borderRadius: BorderRadius.circular(
Avatar.defaultSize * 2.5,
),
),
child: Avatar(
mxContent: profile?.avatarUrl,
name: displayname,
size: Avatar.defaultSize * 2.5,
),
Avatar(
mxContent: profile?.avatarUrl,
name: displayname,
size: Avatar.defaultSize * 2.5,
),
if (profile != null)
Positioned(
bottom: 0,
right: 0,
child: FloatingActionButton.small(
elevation: 2,
onPressed: controller.setAvatarAction,
heroTag: null,
child: const Icon(Icons.camera_alt_outlined),
@ -119,7 +95,9 @@ class SettingsView extends StatelessWidget {
displayname,
maxLines: 1,
overflow: TextOverflow.ellipsis,
// style: const TextStyle(fontSize: 18),
style: const TextStyle(
fontSize: 18,
),
),
),
TextButton.icon(
@ -147,10 +125,7 @@ class SettingsView extends StatelessWidget {
},
),
// #Pangea
// Divider(
// height: 1,
// color: Theme.of(context).dividerColor,
// ),
// Divider(color: Theme.of(context).dividerColor),
// if (showChatBackupBanner == null)
// ListTile(
// leading: const Icon(Icons.backup_outlined),
@ -166,7 +141,6 @@ class SettingsView extends StatelessWidget {
// onChanged: controller.firstRunBootstrapAction,
// ),
// Divider(
// height: 1,
// color: Theme.of(context).dividerColor,
// ),
// Pangea#
@ -174,52 +148,40 @@ class SettingsView extends StatelessWidget {
leading: const Icon(Icons.format_paint_outlined),
title: Text(L10n.of(context)!.changeTheme),
onTap: () => context.go('/rooms/settings/style'),
trailing: const Icon(Icons.chevron_right_outlined),
),
ListTile(
leading: const Icon(Icons.notifications_outlined),
title: Text(L10n.of(context)!.notifications),
onTap: () => context.go('/rooms/settings/notifications'),
trailing: const Icon(Icons.chevron_right_outlined),
),
ListTile(
leading: const Icon(Icons.devices_outlined),
title: Text(L10n.of(context)!.devices),
onTap: () => context.go('/rooms/settings/devices'),
trailing: const Icon(Icons.chevron_right_outlined),
),
ListTile(
leading: const Icon(Icons.forum_outlined),
title: Text(L10n.of(context)!.chat),
onTap: () => context.go('/rooms/settings/chat'),
trailing: const Icon(Icons.chevron_right_outlined),
),
// #Pangea
ListTile(
leading: const Icon(Icons.account_circle_outlined),
title: Text(L10n.of(context)!.subscriptionManagement),
onTap: () => context.go('/rooms/settings/subscription'),
trailing: const Icon(
Icons.chevron_right_outlined,
),
),
ListTile(
leading: const Icon(Icons.psychology_outlined),
title: Text(L10n.of(context)!.learningSettings),
onTap: () => context.go('/rooms/settings/learning'),
trailing: const Icon(Icons.chevron_right_outlined),
),
// Pangea#
ListTile(
leading: const Icon(Icons.shield_outlined),
title: Text(L10n.of(context)!.security),
onTap: () => context.go('/rooms/settings/security'),
trailing: const Icon(Icons.chevron_right_outlined),
),
Divider(
height: 1,
color: Theme.of(context).dividerColor,
),
Divider(color: Theme.of(context).dividerColor),
ListTile(
leading: const Icon(Icons.help_outline_outlined),
title: Text(L10n.of(context)!.help),
@ -229,7 +191,7 @@ class SettingsView extends StatelessWidget {
await showFutureLoadingDialog(
context: context,
future: () async {
final String roomId =
final roomId =
await Matrix.of(context).client.startDirectChat(
Environment.supportUserId,
enableEncryption: false,
@ -239,20 +201,17 @@ class SettingsView extends StatelessWidget {
);
},
// Pangea#
trailing: const Icon(Icons.open_in_new_outlined),
),
ListTile(
leading: const Icon(Icons.shield_sharp),
title: Text(L10n.of(context)!.privacy),
onTap: () => launchUrlString(AppConfig.privacyUrl),
trailing: const Icon(Icons.open_in_new_outlined),
),
// #Pangea
// ListTile(
// leading: const Icon(Icons.info_outline_rounded),
// title: Text(L10n.of(context)!.about),
// onTap: () => PlatformInfos.showDialog(context),
// trailing: const Icon(Icons.chevron_right_outlined),
// ),
ListTile(
leading: const Icon(Icons.shield_outlined),
@ -290,6 +249,12 @@ class SettingsView extends StatelessWidget {
title: Text(L10n.of(context)!.connectedToStaging),
),
// Pangea#
Divider(color: Theme.of(context).dividerColor),
ListTile(
leading: const Icon(Icons.logout_outlined),
title: Text(L10n.of(context)!.logout),
onTap: controller.logoutAction,
),
],
),
),

@ -67,7 +67,7 @@ class Settings3PidView extends StatelessWidget {
.withTheseAddressesRecoveryDescription,
),
),
const Divider(height: 1),
const Divider(),
Expanded(
child: ListView.builder(
itemCount: identifier.length,

@ -71,10 +71,7 @@ class SettingsChatView extends StatelessWidget {
defaultValue: AppConfig.swipeRightToLeftToReply,
),
// #Pangea
// Divider(
// height: 1,
// color: Theme.of(context).dividerColor,
// ),
// Divider(color: Theme.of(context).dividerColor),
// ListTile(
// title: Text(
// L10n.of(context)!.customEmojisAndStickers,
@ -93,10 +90,7 @@ class SettingsChatView extends StatelessWidget {
// child: Icon(Icons.chevron_right_outlined),
// ),
// ),
// Divider(
// height: 1,
// color: Theme.of(context).dividerColor,
// ),
// Divider(color: Theme.of(context).dividerColor),
// ListTile(
// title: Text(
// L10n.of(context)!.calls,

@ -117,7 +117,7 @@ class EmotesSettingsView extends StatelessWidget {
onChanged: controller.setIsGloballyActive,
),
if (!controller.readonly || controller.room != null)
const Divider(thickness: 1),
const Divider(),
imageKeys.isEmpty
? Center(
child: Padding(

@ -85,7 +85,6 @@ class SettingsSecurityView extends StatelessWidget {
),
},
Divider(
height: 1,
color: Theme.of(context).dividerColor,
),
ListTile(

@ -136,7 +136,6 @@ class SettingsStyleView extends StatelessWidget {
),
const SizedBox(height: 8),
Divider(
height: 1,
color: Theme.of(context).dividerColor,
),
ListTile(
@ -167,7 +166,6 @@ class SettingsStyleView extends StatelessWidget {
onChanged: controller.switchTheme,
),
Divider(
height: 1,
color: Theme.of(context).dividerColor,
),
ListTile(
@ -192,7 +190,6 @@ class SettingsStyleView extends StatelessWidget {
defaultValue: AppConfig.separateChatTypes,
),
Divider(
height: 1,
color: Theme.of(context).dividerColor,
),
ListTile(

@ -236,6 +236,45 @@ class UserBottomSheetController extends State<UserBottomSheet> {
}
}
bool isSending = false;
Object? sendError;
final TextEditingController sendController = TextEditingController();
void sendAction([_]) async {
final userId = widget.user?.id ?? widget.profile?.userId;
final client = Matrix.of(widget.outerContext).client;
if (userId == null) throw ('user or profile must not be null!');
final input = sendController.text.trim();
if (input.isEmpty) return;
setState(() {
isSending = true;
sendError = null;
});
try {
final roomId = await client.startDirectChat(userId);
if (!mounted) return;
final room = client.getRoomById(roomId);
if (room == null) {
throw ('DM Room found or created but room not found in client');
}
await room.sendTextEvent(input);
setState(() {
isSending = false;
sendController.clear();
});
} catch (e, s) {
Logs().d('Unable to send message', e, s);
setState(() {
isSending = false;
sendError = e;
});
}
}
void knockAccept() async {
final user = widget.user!;
final result = await showFutureLoadingDialog(

@ -2,6 +2,7 @@ import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/utils/bot_name.dart';
import 'package:fluffychat/utils/date_time_extension.dart';
import 'package:fluffychat/utils/fluffy_share.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart';
import 'package:fluffychat/utils/url_launcher.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/presence_builder.dart';
@ -29,6 +30,7 @@ class UserBottomSheetView extends StatelessWidget {
final client = Matrix.of(controller.widget.outerContext).client;
final profileSearchError = controller.widget.profileSearchError;
final dmRoomId = client.getDirectChatFromUserId(userId);
return SafeArea(
child: Scaffold(
appBar: AppBar(
@ -36,78 +38,20 @@ class UserBottomSheetView extends StatelessWidget {
onPressed: Navigator.of(context, rootNavigator: false).pop,
),
centerTitle: false,
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(displayname),
PresenceBuilder(
userId: userId,
client: client,
builder: (context, presence) {
if (presence == null ||
(presence.presence == PresenceType.offline &&
presence.lastActiveTimestamp == null)) {
return const SizedBox.shrink();
}
final dotColor = presence.presence.isOnline
? Colors.green
: presence.presence.isUnavailable
? Colors.orange
: Colors.grey;
final lastActiveTimestamp = presence.lastActiveTimestamp;
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 8,
height: 8,
margin: const EdgeInsets.only(right: 8),
decoration: BoxDecoration(
color: dotColor,
borderRadius: BorderRadius.circular(16),
),
),
if (presence.currentlyActive == true)
Text(
L10n.of(context)!.currentlyActive,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodySmall,
)
else if (lastActiveTimestamp != null)
Text(
L10n.of(context)!.lastActiveAgo(
lastActiveTimestamp.localizedTimeShort(context),
),
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodySmall,
),
],
);
},
),
],
),
actions: [
if (userId != client.userID &&
!client.ignoredUsers.contains(userId)
// #Pangea
&&
userId != BotName.byEnvironment
// Pangea#
)
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: IconButton(
icon: const Icon(Icons.block_outlined),
tooltip: L10n.of(context)!.block,
onPressed: () => controller
.participantAction(UserBottomSheetAction.ignore),
),
),
],
title: Text(displayname),
actions: dmRoomId == null
? null
: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: FloatingActionButton.small(
elevation: 0,
onPressed: () => controller
.participantAction(UserBottomSheetAction.message),
child: const Icon(Icons.chat_outlined),
),
),
],
),
body: StreamBuilder<Object>(
stream: user?.room.client.onSync.stream.where(
@ -174,25 +118,12 @@ class UserBottomSheetView extends StatelessWidget {
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: Material(
elevation: Theme.of(context)
.appBarTheme
.scrolledUnderElevation ??
4,
shadowColor: Theme.of(context).appBarTheme.shadowColor,
shape: RoundedRectangleBorder(
side: BorderSide(
color: Theme.of(context).dividerColor,
),
borderRadius: BorderRadius.circular(
Avatar.defaultSize * 2.5,
),
),
child: Avatar(
mxContent: avatarUrl,
name: displayname,
size: Avatar.defaultSize * 2.5,
),
child: Avatar(
client:
Matrix.of(controller.widget.outerContext).client,
mxContent: avatarUrl,
name: displayname,
size: Avatar.defaultSize * 2.5,
),
),
Expanded(
@ -200,26 +131,6 @@ class UserBottomSheetView extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextButton.icon(
onPressed: () => FluffyShare.share(
'https://matrix.to/#/$userId',
context,
),
icon: Icon(
Icons.adaptive.share_outlined,
size: 16,
),
style: TextButton.styleFrom(
foregroundColor:
Theme.of(context).colorScheme.onSurface,
),
label: Text(
displayname,
maxLines: 1,
overflow: TextOverflow.ellipsis,
// style: const TextStyle(fontSize: 18),
),
),
TextButton.icon(
onPressed: () => FluffyShare.share(
userId,
@ -232,37 +143,72 @@ class UserBottomSheetView extends StatelessWidget {
),
style: TextButton.styleFrom(
foregroundColor:
Theme.of(context).colorScheme.secondary,
Theme.of(context).colorScheme.onSurface,
),
label: Text(
userId,
maxLines: 1,
overflow: TextOverflow.ellipsis,
// style: const TextStyle(fontSize: 12),
),
),
PresenceBuilder(
userId: userId,
client: client,
builder: (context, presence) {
if (presence == null ||
(presence.presence == PresenceType.offline &&
presence.lastActiveTimestamp == null)) {
return const SizedBox.shrink();
}
final dotColor = presence.presence.isOnline
? Colors.green
: presence.presence.isUnavailable
? Colors.orange
: Colors.grey;
final lastActiveTimestamp =
presence.lastActiveTimestamp;
return Row(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(width: 16),
Container(
width: 8,
height: 8,
decoration: BoxDecoration(
color: dotColor,
borderRadius: BorderRadius.circular(16),
),
),
const SizedBox(width: 12),
if (presence.currentlyActive == true)
Text(
L10n.of(context)!.currentlyActive,
overflow: TextOverflow.ellipsis,
style:
Theme.of(context).textTheme.bodySmall,
)
else if (lastActiveTimestamp != null)
Text(
L10n.of(context)!.lastActiveAgo(
lastActiveTimestamp
.localizedTimeShort(context),
),
overflow: TextOverflow.ellipsis,
style:
Theme.of(context).textTheme.bodySmall,
),
],
);
},
),
],
),
),
],
),
if (userId != client.userID)
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 8.0,
),
child: ElevatedButton.icon(
onPressed: () => controller
.participantAction(UserBottomSheetAction.message),
icon: const Icon(Icons.forum_outlined),
label: Text(
controller.widget.user == null
? L10n.of(context)!.startConversation
: L10n.of(context)!.sendAMessage,
),
),
),
PresenceBuilder(
userId: userId,
client: client,
@ -286,6 +232,49 @@ class UserBottomSheetView extends StatelessWidget {
);
},
),
if (userId != client.userID)
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 8.0,
),
child: dmRoomId == null
? ElevatedButton.icon(
onPressed: () => controller.participantAction(
UserBottomSheetAction.message,
),
icon: const Icon(Icons.chat_outlined),
label: Text(L10n.of(context)!.startConversation),
)
: TextField(
controller: controller.sendController,
readOnly: controller.isSending,
onSubmitted: controller.sendAction,
minLines: 1,
maxLines: 1,
textInputAction: TextInputAction.send,
decoration: InputDecoration(
errorText: controller.sendError
?.toLocalizedString(context),
hintText: L10n.of(context)!.sendMessages,
suffix: controller.isSending
? const SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator.adaptive(
strokeWidth: 2,
),
)
: null,
suffixIcon: controller.isSending
? null
: IconButton(
icon: const Icon(Icons.send_outlined),
onPressed: controller.sendAction,
),
),
),
),
if (controller.widget.onMention != null)
ListTile(
leading: const Icon(Icons.alternate_email_outlined),
@ -339,8 +328,8 @@ class UserBottomSheetView extends StatelessWidget {
),
),
),
Divider(color: Theme.of(context).dividerColor),
],
Divider(color: Theme.of(context).dividerColor),
if (user != null && user.canKick)
ListTile(
textColor: Theme.of(context).colorScheme.error,
@ -376,7 +365,7 @@ class UserBottomSheetView extends StatelessWidget {
// textColor: Theme.of(context).colorScheme.onErrorContainer,
// iconColor: Theme.of(context).colorScheme.onErrorContainer,
// title: Text(L10n.of(context)!.reportUser),
// leading: const Icon(Icons.report_outlined),
// leading: const Icon(Icons.gavel_outlined),
// onTap: () => controller
// .participantAction(UserBottomSheetAction.report),
// ),
@ -392,6 +381,21 @@ class UserBottomSheetView extends StatelessWidget {
style: const TextStyle(color: Colors.orange),
),
),
if (userId != client.userID &&
!client.ignoredUsers.contains(userId)
// #Pangea
&&
userId != BotName.byEnvironment
// Pangea#
)
ListTile(
textColor: Theme.of(context).colorScheme.onErrorContainer,
iconColor: Theme.of(context).colorScheme.onErrorContainer,
leading: const Icon(Icons.block_outlined),
title: Text(L10n.of(context)!.block),
onTap: () => controller
.participantAction(UserBottomSheetAction.ignore),
),
],
);
},

@ -8,18 +8,19 @@ Future<T?> showAdaptiveBottomSheet<T>({
required Widget Function(BuildContext) builder,
bool isDismissible = true,
bool isScrollControlled = true,
double maxHeight = 480.0,
double maxHeight = 512,
bool useRootNavigator = true,
}) =>
showModalBottomSheet(
context: context,
builder: builder,
// this sadly is ugly on desktops but otherwise breaks `.of(context)` calls
useRootNavigator: false,
useRootNavigator: useRootNavigator,
isDismissible: isDismissible,
isScrollControlled: isScrollControlled,
constraints: BoxConstraints(
maxHeight: maxHeight,
maxWidth: FluffyThemes.columnWidth * 1.5,
maxWidth: FluffyThemes.columnWidth * 1.25,
),
clipBehavior: Clip.hardEdge,
shape: const RoundedRectangleBorder(

@ -77,6 +77,7 @@ class Avatar extends StatelessWidget {
child: noPic
? textWidget
: MxcImage(
client: client,
key: ValueKey(mxContent.toString()),
cacheKey: '${mxContent}_$size',
uri: mxContent,

@ -1,20 +1,17 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart';
class MaxWidthBody extends StatelessWidget {
final Widget? child;
final Widget child;
final double maxWidth;
final bool withFrame;
final bool withScrolling;
final EdgeInsets? innerPadding;
const MaxWidthBody({
this.child,
required this.child,
this.maxWidth = 600,
this.withFrame = true,
this.withScrolling = true,
this.innerPadding,
super.key,
@ -24,36 +21,35 @@ class MaxWidthBody extends StatelessWidget {
return SafeArea(
child: LayoutBuilder(
builder: (context, constraints) {
final paddingVal = max(0, (constraints.maxWidth - maxWidth) / 2);
final hasPadding = paddingVal > 0;
final padding = EdgeInsets.symmetric(
vertical: hasPadding ? 32 : 0,
horizontal: max(0, (constraints.maxWidth - maxWidth) / 2),
);
final childWithPadding = Padding(
padding: padding,
child: withFrame && hasPadding
? Material(
elevation:
Theme.of(context).appBarTheme.scrolledUnderElevation ??
4,
clipBehavior: Clip.hardEdge,
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
shadowColor: Theme.of(context).appBarTheme.shadowColor,
child: child,
)
: child,
);
if (!withScrolling) {
return Padding(
padding: innerPadding ?? EdgeInsets.zero,
child: childWithPadding,
);
}
const desiredWidth = FluffyThemes.columnWidth * 1.5;
final body = constraints.maxWidth <= desiredWidth
? child
: Container(
alignment: Alignment.topCenter,
padding: const EdgeInsets.all(32),
child: ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: FluffyThemes.columnWidth * 1.5,
),
child: Material(
elevation: Theme.of(context)
.appBarTheme
.scrolledUnderElevation ??
4,
clipBehavior: Clip.hardEdge,
borderRadius:
BorderRadius.circular(AppConfig.borderRadius),
shadowColor: Theme.of(context).appBarTheme.shadowColor,
child: child,
),
),
);
if (!withScrolling) return body;
return SingleChildScrollView(
padding: innerPadding,
physics: const ScrollPhysics(),
child: childWithPadding,
child: body,
);
},
),

@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/config/themes.dart';
class TwoColumnLayout extends StatelessWidget {
final Widget mainView;
final Widget sideView;
@ -20,7 +22,8 @@ class TwoColumnLayout extends StatelessWidget {
Container(
clipBehavior: Clip.antiAlias,
decoration: const BoxDecoration(),
width: 360.0 + (displayNavigationRail ? 64 : 0),
width: FluffyThemes.columnWidth +
(displayNavigationRail ? FluffyThemes.navRailWidth : 0),
child: mainView,
),
Container(

@ -23,6 +23,7 @@ class MxcImage extends StatefulWidget {
final ThumbnailMethod thumbnailMethod;
final Widget Function(BuildContext context)? placeholder;
final String? cacheKey;
final Client? client;
const MxcImage({
this.uri,
@ -38,6 +39,7 @@ class MxcImage extends StatefulWidget {
this.animationCurve = FluffyThemes.animationCurve,
this.thumbnailMethod = ThumbnailMethod.scale,
this.cacheKey,
this.client,
super.key,
});
@ -64,7 +66,7 @@ class _MxcImageState extends State<MxcImage> {
bool? _isCached;
Future<void> _load() async {
final client = Matrix.of(context).client;
final client = widget.client ?? Matrix.of(context).client;
final uri = widget.uri;
final event = widget.event;

@ -10,19 +10,17 @@ import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart';
import '../utils/localized_exception_extension.dart';
class PublicRoomBottomSheet extends StatelessWidget {
final String? roomAlias;
final BuildContext outerContext;
final PublicRoomsChunk? chunk;
final VoidCallback? onRoomJoined;
final List<String>? via;
PublicRoomBottomSheet({
this.roomAlias,
required this.outerContext,
this.chunk,
this.onRoomJoined,
this.via,
super.key,
}) {
assert(roomAlias != null || chunk != null);
@ -39,8 +37,11 @@ class PublicRoomBottomSheet extends StatelessWidget {
return chunk.roomId;
}
final roomId = chunk != null && knock
? await client.knockRoom(chunk.roomId)
: await client.joinRoom(roomAlias ?? chunk!.roomId);
? await client.knockRoom(chunk.roomId, serverName: via)
: await client.joinRoom(
roomAlias ?? chunk!.roomId,
serverName: via,
);
if (!knock && client.getRoomById(roomId) == null) {
await client.waitForRoomInSync(roomId);
@ -58,7 +59,7 @@ class PublicRoomBottomSheet extends StatelessWidget {
return;
}
if (result.error == null) {
Navigator.of(context).pop();
Navigator.of(context).pop<bool>(true);
// don't open the room if the joined room is a space
if (chunk?.roomType != 'm.space' &&
!client.getRoomById(result.result!)!.isSpace) {
@ -70,17 +71,17 @@ class PublicRoomBottomSheet extends StatelessWidget {
bool _testRoom(PublicRoomsChunk r) => r.canonicalAlias == roomAlias;
Future<PublicRoomsChunk> _search(BuildContext context) async {
Future<PublicRoomsChunk> _search() async {
final chunk = this.chunk;
if (chunk != null) return chunk;
final query = await Matrix.of(context).client.queryPublicRooms(
final query = await Matrix.of(outerContext).client.queryPublicRooms(
server: roomAlias!.domain,
filter: PublicRoomQueryFilter(
genericSearchTerm: roomAlias,
),
);
if (!query.chunk.any(_testRoom)) {
throw (L10n.of(context)!.noRoomsFound);
throw (L10n.of(outerContext)!.noRoomsFound);
}
return query.chunk.firstWhere(_testRoom);
}
@ -88,6 +89,7 @@ class PublicRoomBottomSheet extends StatelessWidget {
@override
Widget build(BuildContext context) {
final roomAlias = this.roomAlias ?? chunk?.canonicalAlias;
final roomLink = roomAlias ?? chunk?.roomId;
return SafeArea(
child: Scaffold(
appBar: AppBar(
@ -114,41 +116,84 @@ class PublicRoomBottomSheet extends StatelessWidget {
],
),
body: FutureBuilder<PublicRoomsChunk>(
future: _search(context),
future: _search(),
builder: (context, snapshot) {
final profile = snapshot.data;
return ListView(
padding: EdgeInsets.zero,
children: [
if (profile == null)
Container(
height: 156,
alignment: Alignment.center,
color: Theme.of(context).secondaryHeaderColor,
child: snapshot.hasError
? Text(snapshot.error!.toLocalizedString(context))
: const CircularProgressIndicator.adaptive(
strokeWidth: 2,
),
)
else
Center(
child: Padding(
Row(
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: Avatar(
mxContent: profile.avatarUrl,
name: profile.name ?? roomAlias,
size: Avatar.defaultSize * 3,
child: profile == null
? const Center(
child: CircularProgressIndicator.adaptive(),
)
: Avatar(
client: Matrix.of(outerContext).client,
mxContent: profile.avatarUrl,
name: profile.name ?? roomAlias,
size: Avatar.defaultSize * 3,
),
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextButton.icon(
onPressed: roomLink != null
? () => FluffyShare.share(
roomLink,
context,
copyOnly: true,
)
: null,
icon: const Icon(
Icons.copy_outlined,
size: 14,
),
style: TextButton.styleFrom(
foregroundColor:
Theme.of(context).colorScheme.onSurface,
),
label: Text(
roomLink ?? '...',
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
TextButton.icon(
onPressed: () {},
icon: const Icon(
Icons.groups_3_outlined,
size: 14,
),
style: TextButton.styleFrom(
foregroundColor:
Theme.of(context).colorScheme.onSurface,
),
label: Text(
L10n.of(context)!.countParticipants(
profile?.numJoinedMembers ?? 0,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
),
),
),
],
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: ElevatedButton.icon(
onPressed: () => _joinRoom(context),
label: Text(
chunk?.joinRule == 'knock' &&
Matrix.of(context)
Matrix.of(outerContext)
.client
.getRoomById(chunk!.roomId) ==
null
@ -157,36 +202,10 @@ class PublicRoomBottomSheet extends StatelessWidget {
? L10n.of(context)!.joinSpace
: L10n.of(context)!.joinRoom,
),
icon: const Icon(Icons.login_outlined),
icon: const Icon(Icons.navigate_next),
),
),
const SizedBox(height: 16),
ListTile(
title: Text(
profile?.name ??
roomAlias?.localpart ??
chunk?.roomId.localpart ??
L10n.of(context)!.chat,
),
subtitle: Text(
'${L10n.of(context)!.participant}: ${profile?.numJoinedMembers ?? 0}',
),
trailing: const Icon(Icons.account_box_outlined),
),
if (roomAlias != null)
ListTile(
title: Text(L10n.of(context)!.publicLink),
subtitle: SelectableText(roomAlias),
contentPadding:
const EdgeInsets.symmetric(horizontal: 16.0),
trailing: IconButton(
icon: const Icon(Icons.copy_outlined),
onPressed: () => FluffyShare.share(
roomAlias,
context,
),
),
),
if (profile?.topic?.isNotEmpty ?? false)
ListTile(
subtitle: SelectableLinkify(

Loading…
Cancel
Save