diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index e39ea8e0c..9135c4788 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -4870,7 +4870,7 @@ "areYouLikeMe": "Are you like me?", "tryAgainLater": "Too many attempts made. Please try again in 5 minutes.", "enterSpaceCode": "Enter space code", - "shareSpaceLink": "Share link to space", + "shareSpaceLink": "Share link", "byUsingPangeaChat": "By using Pangea Chat, I agree to the ", "details": "Details", "languageLevelPreA1Desc": "I have never learned or used the language.", @@ -4912,5 +4912,14 @@ "forms": "Forms", "exampleMessages": "Example messages", "timesUsedIndependently": "Times used independently", - "timesUsedWithAssistance": "Times used with assistance" + "timesUsedWithAssistance": "Times used with assistance", + "goToSpaceButton": "Go to space", + "shareInviteCode": "Share invite code: {code}", + "@shareInviteCode": { + "placeholders": { + "code": { + "type": "String" + } + } + } } diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index dd51089fe..23e213d09 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -888,10 +888,13 @@ class ChatController extends State pangeaEditingEvent = previousEdit; } - GoogleAnalytics.sendMessage( - room.id, - room.classCode(context), - ); + final spaceCode = room.classCode(context); + if (spaceCode != null) { + GoogleAnalytics.sendMessage( + room.id, + spaceCode, + ); + } if (msgEventId == null) { ErrorHandler.logError( diff --git a/lib/pages/invitation_selection/invitation_selection.dart b/lib/pages/invitation_selection/invitation_selection.dart index b43096ecb..f786ef6cc 100644 --- a/lib/pages/invitation_selection/invitation_selection.dart +++ b/lib/pages/invitation_selection/invitation_selection.dart @@ -35,6 +35,36 @@ class InvitationSelectionController extends State { String? get roomId => widget.roomId; // #Pangea + final viewportKey = GlobalKey(); + + final participantListItemHeight = 72.0; + final goToChatButtonHeight = 50.0; + final shareButtonsHeight = 150.0; + final padding = 16.0 * 2; + final fixedParticipantHeight = 72.0; + + double? viewportHeight; + double get availableHeight => + (viewportHeight ?? 0) - + goToChatButtonHeight - + shareButtonsHeight - + padding; + + bool showShareButtons(int numParticipants) => + (fixedParticipantHeight * numParticipants) < availableHeight; + + @override + initState() { + WidgetsBinding.instance.addPostFrameCallback((_) { + final context = viewportKey.currentContext; + if (context == null) return; + final renderBox = context.findRenderObject() as RenderBox; + final size = renderBox.size; + setState(() => viewportHeight = size.height); + }); + super.initState(); + } + List? get participants { final room = Matrix.of(context).client.getRoomById(roomId!); return room?.getParticipants(); diff --git a/lib/pages/invitation_selection/invitation_selection_view.dart b/lib/pages/invitation_selection/invitation_selection_view.dart index a1cfc8422..b3c54d3e0 100644 --- a/lib/pages/invitation_selection/invitation_selection_view.dart +++ b/lib/pages/invitation_selection/invitation_selection_view.dart @@ -3,7 +3,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:cached_network_image/cached_network_image.dart'; -import 'package:collection/collection.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:go_router/go_router.dart'; import 'package:matrix/matrix.dart'; @@ -14,11 +13,11 @@ import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/invitation_selection/invitation_selection.dart'; import 'package:fluffychat/pangea/analytics_misc/level_display_name.dart'; import 'package:fluffychat/pangea/chat_settings/constants/room_settings_constants.dart'; -import 'package:fluffychat/pangea/chat_settings/widgets/refer_friends_dialog.dart'; +import 'package:fluffychat/pangea/chat_settings/widgets/space_invite_buttons.dart'; import 'package:fluffychat/pangea/common/config/environment.dart'; -import 'package:fluffychat/pangea/common/widgets/full_width_dialog.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/pangea/spaces/constants/space_constants.dart'; +import 'package:fluffychat/utils/stream_extension.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/layouts/max_width_body.dart'; import 'package:fluffychat/widgets/matrix.dart'; @@ -53,346 +52,247 @@ class InvitationSelectionView extends StatelessWidget { leading: const Center(child: BackButton()), titleSpacing: 0, title: Text(L10n.of(context).inviteContact), + // #Pangea + actions: [ + if (room.classCode(context) != null) + PopupMenuButton( + icon: const Icon(Icons.share_outlined), + onSelected: (value) async { + final spaceCode = room.classCode(context)!; + String toCopy = spaceCode; + if (value == 0) { + final String initialUrl = + kIsWeb ? html.window.origin! : Environment.frontendURL; + toCopy = + "$initialUrl/#/join_with_link?${SpaceConstants.classCode}=${room.classCode(context)}"; + } + + await Clipboard.setData(ClipboardData(text: toCopy)); + ScaffoldMessenger.of( + context, + ).showSnackBar( + SnackBar( + content: Text( + L10n.of(context).copiedToClipboard, + ), + ), + ); + }, + itemBuilder: (BuildContext context) => >[ + PopupMenuItem( + value: 0, + child: ListTile( + leading: const Icon(Icons.share_outlined), + title: Text(L10n.of(context).shareSpaceLink), + contentPadding: const EdgeInsets.all(0), + ), + ), + PopupMenuItem( + value: 1, + child: ListTile( + leading: const Icon(Icons.share_outlined), + title: Text( + L10n.of(context) + .shareInviteCode(room.classCode(context)!), + ), + contentPadding: const EdgeInsets.all(0), + ), + ), + ], + ), + ], + // Pangea# ), body: MaxWidthBody( innerPadding: const EdgeInsets.symmetric(vertical: 8), // #Pangea withScrolling: false, // Pangea# - child: Column( + child: Stack( + alignment: Alignment.bottomCenter, children: [ - // #Pangea Padding( padding: const EdgeInsets.all(16.0), - child: InkWell( - customBorder: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(99), - ), - onTap: () async { - await Clipboard.setData( - ClipboardData(text: room.classCode(context)), - ); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(L10n.of(context).copiedToClipboard)), - ); - }, - child: Container( - padding: const EdgeInsets.symmetric( - vertical: 12.0, - horizontal: 16.0, - ), - decoration: BoxDecoration( - color: theme.colorScheme.secondaryContainer, - borderRadius: BorderRadius.circular(99), - ), - child: Row( - spacing: 16.0, - children: [ - const Icon( - Icons.copy_outlined, - size: 20.0, - ), - Text( - "${L10n.of(context).copyClassCode}: ${room.classCode(context)}", - style: TextStyle( - color: theme.colorScheme.onPrimaryContainer, - fontWeight: FontWeight.normal, - fontSize: 16.0, - ), - ), - ], + child: SizedBox( + width: 450, + child: CachedNetworkImage( + imageUrl: + "${AppConfig.assetsBaseURL}/${RoomSettingsConstants.referFriendAsset}", + errorWidget: (context, url, error) => const SizedBox(), + placeholder: (context, url) => const Center( + child: CircularProgressIndicator.adaptive(), ), ), ), ), - Padding( - padding: const EdgeInsets.only( - bottom: 16.0, - left: 16.0, - right: 16.0, - ), - child: InkWell( - customBorder: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(99), - ), - onTap: () async { - final String initialUrl = - kIsWeb ? html.window.origin! : Environment.frontendURL; - final link = - "$initialUrl/#/join_with_link?${SpaceConstants.classCode}=${room.classCode(context)}"; - await Clipboard.setData(ClipboardData(text: link)); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(L10n.of(context).copiedToClipboard)), - ); - }, - child: Container( - padding: const EdgeInsets.symmetric( - vertical: 12.0, - horizontal: 16.0, - ), - decoration: BoxDecoration( - color: theme.colorScheme.secondaryContainer, - borderRadius: BorderRadius.circular(99), - ), - child: Row( - spacing: 16.0, - children: [ - const Icon( - Icons.copy_outlined, - size: 20.0, - ), - Text( - L10n.of(context).copyClassLink, - style: TextStyle( - color: theme.colorScheme.onPrimaryContainer, - fontWeight: FontWeight.normal, - fontSize: 16.0, - ), - ), - ], - ), - ), - ), - ), - // Pangea# - Padding( - // #Pangea - // padding: const EdgeInsets.all(16.0), - padding: const EdgeInsets.only( - bottom: 16.0, - left: 16.0, - right: 16.0, - ), - // Pangea# - child: TextField( - textInputAction: TextInputAction.search, - decoration: InputDecoration( - filled: true, - fillColor: theme.colorScheme.secondaryContainer, - border: OutlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(99), - ), - hintStyle: TextStyle( - color: theme.colorScheme.onPrimaryContainer, - fontWeight: FontWeight.normal, - ), + Column( + children: [ + Padding( // #Pangea - hintText: L10n.of(context).inviteStudentByUserName, - // hintText: L10n.of(context).inviteContactToGroup(groupName), + // padding: const EdgeInsets.all(16.0), + padding: const EdgeInsets.only( + bottom: 16.0, + left: 16.0, + right: 16.0, + ), // Pangea# - prefixIcon: controller.loading - ? const Padding( - padding: EdgeInsets.symmetric( - vertical: 10.0, - horizontal: 12, - ), - child: SizedBox.square( - dimension: 24, - child: CircularProgressIndicator.adaptive( - strokeWidth: 2, - ), - ), - ) - : const Icon(Icons.search_outlined), - ), - onChanged: controller.searchUserWithCoolDown, - ), - ), - // #Pangea - // StreamBuilder( - Expanded( - child: StreamBuilder( - // Pangea# - stream: room.client.onRoomState.stream - .where((update) => update.roomId == room.id), - builder: (context, snapshot) { - final participants = - room.getParticipants().map((user) => user.id).toSet(); - return controller.foundProfiles.isNotEmpty - ? ListView.builder( - // #Pangea - // physics: const NeverScrollableScrollPhysics(), - // shrinkWrap: true, - // Pangea# - itemCount: controller.foundProfiles.length, - itemBuilder: (BuildContext context, int i) => - _InviteContactListTile( - profile: controller.foundProfiles[i], - isMember: participants - .contains(controller.foundProfiles[i].userId), - onTap: () => controller.inviteAction( - context, - controller.foundProfiles[i].userId, - controller.foundProfiles[i].displayName ?? - controller - .foundProfiles[i].userId.localpart ?? - L10n.of(context).user, - ), - ), - ) - : FutureBuilder>( - future: controller.getContacts(context), - builder: (BuildContext context, snapshot) { - if (!snapshot.hasData) { - return const Center( + child: TextField( + textInputAction: TextInputAction.search, + decoration: InputDecoration( + filled: true, + fillColor: theme.colorScheme.secondaryContainer, + border: OutlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(99), + ), + hintStyle: TextStyle( + color: theme.colorScheme.onPrimaryContainer, + fontWeight: FontWeight.normal, + ), + // #Pangea + hintText: L10n.of(context).inviteStudentByUserName, + // hintText: L10n.of(context).inviteContactToGroup(groupName), + // Pangea# + prefixIcon: controller.loading + ? const Padding( + padding: EdgeInsets.symmetric( + vertical: 10.0, + horizontal: 12, + ), + child: SizedBox.square( + dimension: 24, child: CircularProgressIndicator.adaptive( strokeWidth: 2, ), - ); - } - final contacts = snapshot.data!; - return ListView.builder( + ), + ) + : const Icon(Icons.search_outlined), + ), + onChanged: controller.searchUserWithCoolDown, + ), + ), + // #Pangea + // StreamBuilder( + Expanded( + key: controller.viewportKey, + child: StreamBuilder( + // stream: room.client.onRoomState.stream + // .where((update) => update.roomId == room.id), + stream: room.client.onRoomState.stream + .where((update) => update.roomId == room.id) + .rateLimit(const Duration(seconds: 1)), + // Pangea# + builder: (context, snapshot) { + final participants = + room.getParticipants().map((user) => user.id).toSet(); + return controller.foundProfiles.isNotEmpty + ? ListView.builder( // #Pangea // physics: const NeverScrollableScrollPhysics(), // shrinkWrap: true, - // itemCount: contacts.length, - // itemBuilder: (BuildContext context, int i) => - // _InviteContactListTile( - itemCount: contacts.length + 1, - itemBuilder: (BuildContext context, int i) { - if (i == contacts.length) { - return room.isSpace - ? const SizedBox() - : Center( - child: Padding( - padding: const EdgeInsets.all(16.0), - child: SizedBox( - width: 450, - child: CachedNetworkImage( - imageUrl: - "${AppConfig.assetsBaseURL}/${RoomSettingsConstants.referFriendAsset}", - errorWidget: - (context, url, error) => - const SizedBox(), - placeholder: (context, url) => - const Center( - child: - CircularProgressIndicator - .adaptive(), - ), - ), - ), - ), - ); + // Pangea# + itemCount: controller.foundProfiles.length, + itemBuilder: (BuildContext context, int i) => + _InviteContactListTile( + profile: controller.foundProfiles[i], + isMember: participants.contains( + controller.foundProfiles[i].userId, + ), + onTap: () => controller.inviteAction( + context, + controller.foundProfiles[i].userId, + controller.foundProfiles[i].displayName ?? + controller + .foundProfiles[i].userId.localpart ?? + L10n.of(context).user, + ), + ), + ) + : FutureBuilder>( + future: controller.getContacts(context), + builder: (BuildContext context, snapshot) { + if (!snapshot.hasData) { + return const Center( + child: CircularProgressIndicator.adaptive( + strokeWidth: 2, + ), + ); } - return _InviteContactListTile( - // Pangea# - user: contacts[i], - profile: Profile( - avatarUrl: contacts[i].avatarUrl, - displayName: contacts[i].displayName ?? - contacts[i].id.localpart ?? - L10n.of(context).user, - userId: contacts[i].id, - ), - isMember: - participants.contains(contacts[i].id), - onTap: () => controller.inviteAction( - context, - contacts[i].id, - contacts[i].displayName ?? - contacts[i].id.localpart ?? - L10n.of(context).user, - ), + final contacts = snapshot.data!; + return ListView.builder( // #Pangea - roomPowerLevel: controller.participants - ?.firstWhereOrNull( - (element) => - element.id == contacts[i].id, - ) - ?.powerLevel, - membership: controller.participants - ?.firstWhereOrNull( - (element) => - element.id == contacts[i].id, - ) - ?.membership, - // Pangea# + // physics: const NeverScrollableScrollPhysics(), + // shrinkWrap: true, + // itemCount: contacts.length, + // itemBuilder: (BuildContext context, int i) => + // _InviteContactListTile( + itemCount: contacts.length + 1, + itemBuilder: (BuildContext context, int i) { + if (i == contacts.length) { + final showButtons = controller + .showShareButtons(contacts.length); + return AnimatedOpacity( + duration: + FluffyThemes.animationDuration, + opacity: showButtons ? 1.0 : 0.0, + child: SpaceInviteButtons(room: room), + ); + } + + return _InviteContactListTile( + // Pangea# + user: contacts[i], + profile: Profile( + avatarUrl: contacts[i].avatarUrl, + displayName: contacts[i].displayName ?? + contacts[i].id.localpart ?? + L10n.of(context).user, + userId: contacts[i].id, + ), + isMember: + participants.contains(contacts[i].id), + onTap: () => controller.inviteAction( + context, + contacts[i].id, + contacts[i].displayName ?? + contacts[i].id.localpart ?? + L10n.of(context).user, + ), + ); + }, ); }, ); - }, - ); - }, - ), + }, + ), + ), + ], ), - // #Pangea - if (!room.isSpace) - Padding( - padding: EdgeInsets.only( - left: 8.0, - right: 8.0, - top: 16.0, - bottom: FluffyThemes.isColumnMode(context) ? 0 : 16.0, + Padding( + padding: const EdgeInsets.all(16.0), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: theme.colorScheme.primaryContainer, + elevation: 5.0, ), child: Row( - spacing: 8.0, + mainAxisAlignment: MainAxisAlignment.center, children: [ - Expanded( - child: ElevatedButton( - onPressed: () => showDialog( - context: context, - builder: (context) => FullWidthDialog( - dialogContent: ReferFriendsDialog(room: room), - maxWidth: 600.0, - maxHeight: 800.0, - ), - ), - style: ElevatedButton.styleFrom( - backgroundColor: AppConfig.gold, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - spacing: 12.0, - children: [ - Icon( - Icons.redeem_outlined, - color: Theme.of(context).brightness == - Brightness.light - ? DefaultTextStyle.of(context).style.color - : Theme.of(context).colorScheme.surface, - ), - Text( - L10n.of(context).referFriends, - style: TextStyle( - color: Theme.of(context).brightness == - Brightness.light - ? null - : Theme.of(context).colorScheme.surface, - fontWeight: FontWeight.bold, - ), - ), - ], - ), - ), - ), - Expanded( - child: ElevatedButton( - onPressed: () => context.go("/rooms/${room.id}"), - style: ElevatedButton.styleFrom(), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - spacing: 12.0, - children: [ - Icon( - Icons.chat_outlined, - color: DefaultTextStyle.of(context).style.color, - ), - Text( - L10n.of(context).goToChat, - style: const TextStyle( - fontWeight: FontWeight.bold, - ), - ), - ], - ), + Text( + room.isSpace + ? L10n.of(context).goToSpaceButton + : L10n.of(context).goToChat, + style: TextStyle( + color: theme.colorScheme.onPrimaryContainer, ), ), ], ), + onPressed: () => room.isSpace + ? context.push("/rooms/${room.id}/details") + : context.go("/rooms/${room.id}"), ), - // Pangea# + ), ], ), ), @@ -405,48 +305,19 @@ class _InviteContactListTile extends StatelessWidget { final User? user; final bool isMember; final void Function() onTap; - // #Pangea - final int? roomPowerLevel; - final Membership? membership; - // Pangea# const _InviteContactListTile({ required this.profile, this.user, required this.isMember, required this.onTap, - // #Pangea - this.roomPowerLevel, - this.membership, - // Pangea# }); @override Widget build(BuildContext context) { // #Pangea - String? permissionCopy() { - if (roomPowerLevel == null) { - return null; - } - - return roomPowerLevel! >= 100 - ? L10n.of(context).admin - : roomPowerLevel! >= 50 - ? L10n.of(context).moderator - : null; - } - - String? membershipCopy() => switch (membership) { - Membership.ban => L10n.of(context).banned, - Membership.invite => L10n.of(context).invited, - Membership.join => null, - Membership.knock => L10n.of(context).knocking, - Membership.leave => L10n.of(context).leftTheChat, - null => null, - }; + // final theme = Theme.of(context); // Pangea# - - final theme = Theme.of(context); final l10n = L10n.of(context); return ListTile( @@ -464,10 +335,8 @@ class _InviteContactListTile extends StatelessWidget { maxLines: 1, overflow: TextOverflow.ellipsis, ), - subtitle: - // #Pangea - LevelDisplayName(userId: profile.userId), - // Text( + // #Pangea + // subtitle: Text( // profile.userId, // maxLines: 1, // overflow: TextOverflow.ellipsis, @@ -475,60 +344,13 @@ class _InviteContactListTile extends StatelessWidget { // color: theme.colorScheme.secondary, // ), // ), - // trailing: TextButton.icon( - // onPressed: isMember ? null : onTap, - // label: Text(isMember ? l10n.participant : l10n.invite), - // icon: Icon(isMember ? Icons.check : Icons.add), - // ), - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - if (membershipCopy() != null) - Container( - padding: const EdgeInsets.all(4), - margin: const EdgeInsets.symmetric(horizontal: 8), - decoration: BoxDecoration( - color: theme.secondaryHeaderColor, - borderRadius: BorderRadius.circular(8), - ), - child: Text( - membershipCopy()!, - style: theme.textTheme.labelSmall, - ), - ) - else if (permissionCopy() != null) - Container( - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 6, - ), - margin: const EdgeInsets.only(right: 8), - decoration: BoxDecoration( - color: roomPowerLevel! >= 100 - ? theme.colorScheme.tertiary - : theme.colorScheme.tertiaryContainer, - borderRadius: BorderRadius.circular( - AppConfig.borderRadius, - ), - ), - child: Text( - permissionCopy()!, - style: theme.textTheme.labelSmall?.copyWith( - color: roomPowerLevel! >= 100 - ? theme.colorScheme.onTertiary - : theme.colorScheme.onTertiaryContainer, - ), - ), - ) - else if (!isMember || roomPowerLevel == null || roomPowerLevel! < 50) - TextButton.icon( - onPressed: isMember ? null : onTap, - label: Text(isMember ? l10n.participant : l10n.invite), - icon: Icon(isMember ? Icons.check : Icons.add), - ), - ], - ), + subtitle: LevelDisplayName(userId: profile.userId), // Pangea# + trailing: TextButton.icon( + onPressed: isMember ? null : onTap, + label: Text(isMember ? l10n.participant : l10n.invite), + icon: Icon(isMember ? Icons.check : Icons.add), + ), ); } } diff --git a/lib/pages/new_group/new_group.dart b/lib/pages/new_group/new_group.dart index b2df139b0..59536160c 100644 --- a/lib/pages/new_group/new_group.dart +++ b/lib/pages/new_group/new_group.dart @@ -236,7 +236,10 @@ class NewGroupController extends State { room = client.getRoomById(spaceId); } if (room == null) return; - GoogleAnalytics.createClass(room.name, room.classCode(context)); + final spaceCode = room.classCode(context); + if (spaceCode != null) { + GoogleAnalytics.createClass(room.name, spaceCode); + } // if a timeout happened, don't redirect to the space if (error != null) return; diff --git a/lib/pangea/chat_settings/widgets/class_invitation_buttons.dart b/lib/pangea/chat_settings/widgets/class_invitation_buttons.dart index 8f2d6ad03..cb25ddab7 100644 --- a/lib/pangea/chat_settings/widgets/class_invitation_buttons.dart +++ b/lib/pangea/chat_settings/widgets/class_invitation_buttons.dart @@ -48,31 +48,35 @@ class ClassInvitationButtons extends StatelessWidget { }, ); - final copyCodeListTile = ListTile( - title: Text( - "${L10n.of(context).copyClassCode}: ${room.classCode(context)}", - style: TextStyle( - color: Theme.of(context).colorScheme.secondary, - fontWeight: FontWeight.bold, + final spaceCode = room.classCode(context); + Widget? copyCodeListTile; + if (spaceCode != null) { + copyCodeListTile = ListTile( + title: Text( + "${L10n.of(context).copyClassCode}: $spaceCode", + style: TextStyle( + color: Theme.of(context).colorScheme.secondary, + fontWeight: FontWeight.bold, + ), ), - ), - subtitle: Text(L10n.of(context).copyClassCodeDesc), - leading: CircleAvatar( - backgroundColor: Theme.of(context).scaffoldBackgroundColor, - foregroundColor: Theme.of(context).textTheme.bodyLarge!.color, - child: const Icon( - Icons.copy, + subtitle: Text(L10n.of(context).copyClassCodeDesc), + leading: CircleAvatar( + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + foregroundColor: Theme.of(context).textTheme.bodyLarge!.color, + child: const Icon( + Icons.copy, + ), ), - ), - onTap: () async { - //PTODO-Lala: Standarize toast - //PTODO - explore using Fluffyshare for this - await Clipboard.setData(ClipboardData(text: room.classCode(context))); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(L10n.of(context).copiedToClipboard)), - ); - }, - ); + onTap: () async { + //PTODO-Lala: Standarize toast + //PTODO - explore using Fluffyshare for this + await Clipboard.setData(ClipboardData(text: spaceCode)); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(L10n.of(context).copiedToClipboard)), + ); + }, + ); + } // final inviateWithEmailListTile = ListTile( // enabled: false, @@ -118,7 +122,7 @@ class ClassInvitationButtons extends StatelessWidget { children: [ // inviteStudentByUserNameTile, copyClassLinkListTile, - copyCodeListTile, + if (copyCodeListTile != null) copyCodeListTile, // inviateWithEmailListTile, // addFromGoogleClassooomListTile, ], diff --git a/lib/pangea/chat_settings/widgets/space_invite_buttons.dart b/lib/pangea/chat_settings/widgets/space_invite_buttons.dart new file mode 100644 index 000000000..001e4c24b --- /dev/null +++ b/lib/pangea/chat_settings/widgets/space_invite_buttons.dart @@ -0,0 +1,137 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:matrix/matrix.dart'; +import 'package:universal_html/html.dart' as html; + +import 'package:fluffychat/pangea/common/config/environment.dart'; +import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; +import 'package:fluffychat/pangea/spaces/constants/space_constants.dart'; + +class SpaceInviteButtons extends StatefulWidget { + final Room room; + // final ScrollController scrollController; + const SpaceInviteButtons({ + super.key, + required this.room, + // required this.scrollController, + }); + + @override + SpaceInviteButtonsController createState() => SpaceInviteButtonsController(); +} + +class SpaceInviteButtonsController extends State { + // bool get isVisible { + // final context = (widget.key as GlobalKey).currentContext; + // if (context == null) return false; + + // final renderBox = context.findRenderObject() as RenderBox; + // final position = renderBox.localToGlobal(Offset.zero); + + // final size = renderBox.size; + // final screenHeight = MediaQuery.of(context).size.height; + + // debugPrint("position: $position, size: $size, screenHeight: $screenHeight"); + + // // Check if any part of the widget is within the visible range + // return position.dy + size.height > 0 && position.dy < screenHeight; + // } + + @override + void initState() { + // WidgetsBinding.instance.addPostFrameCallback( + // (_) => debugPrint("isVisible: $isVisible"), + // ); + super.initState(); + } + + @override + Widget build(BuildContext context) { + final spaceCode = widget.room.classCode(context); + if (spaceCode == null) { + return const SizedBox.shrink(); + } + + return SizedBox( + height: 150.0, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.only( + top: 16.0, + right: 16.0, + left: 16.0, + ), + child: ElevatedButton( + child: Row( + spacing: 8.0, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon( + Icons.share_outlined, + ), + Text(L10n.of(context).shareSpaceLink), + ], + ), + onPressed: () async { + final String initialUrl = + kIsWeb ? html.window.origin! : Environment.frontendURL; + final link = + "$initialUrl/#/join_with_link?${SpaceConstants.classCode}=$spaceCode"; + await Clipboard.setData( + ClipboardData( + text: link, + ), + ); + ScaffoldMessenger.of( + context, + ).showSnackBar( + SnackBar( + content: Text( + L10n.of(context).copiedToClipboard, + ), + ), + ); + }, + ), + ), + Padding( + padding: const EdgeInsets.only( + top: 16.0, + right: 16.0, + left: 16.0, + ), + child: ElevatedButton( + child: Row( + spacing: 8.0, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon( + Icons.share_outlined, + ), + Text(L10n.of(context).shareInviteCode(spaceCode)), + ], + ), + onPressed: () async { + await Clipboard.setData(ClipboardData(text: spaceCode)); + ScaffoldMessenger.of( + context, + ).showSnackBar( + SnackBar( + content: Text( + L10n.of(context).copiedToClipboard, + ), + ), + ); + }, + ), + ), + ], + ), + ); + } +} diff --git a/lib/pangea/extensions/room_space_settings_extension.dart b/lib/pangea/extensions/room_space_settings_extension.dart index 620ed3a29..37551150a 100644 --- a/lib/pangea/extensions/room_space_settings_extension.dart +++ b/lib/pangea/extensions/room_space_settings_extension.dart @@ -1,14 +1,14 @@ part of "pangea_room_extension.dart"; extension SpaceRoomExtension on Room { - String classCode(BuildContext context) { + String? classCode(BuildContext context) { if (!isSpace) { for (final Room potentialClassRoom in pangeaSpaceParents) { if (potentialClassRoom.isSpace) { return SpaceRoomExtension(potentialClassRoom).classCode(context); } } - return L10n.of(context).notInClass; + return null; } final roomJoinRules = getState(EventTypes.RoomJoinRules, ""); if (roomJoinRules != null) { @@ -17,7 +17,7 @@ extension SpaceRoomExtension on Room { return accessCode; } } - return L10n.of(context).noClassCode; + return null; } void checkClass() {