1137 room visibility changes (#1139)

* only show visbility toggles for parent spaces. update copy and toggle icon.

* when a space is added to another space, reset its join rules / visibility to defaults

* before adding space to space, give visibility warning
This commit is contained in:
ggurdin 2024-12-04 11:06:27 -05:00 committed by GitHub
parent e40dc33c0c
commit 09c88b6e28
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 141 additions and 132 deletions

View file

@ -4519,5 +4519,8 @@
"notificationsOn": "Notifications on",
"notificationsOff": "Notifications off",
"spaceCanBeFoundViaSearch": "Space can be found via search",
"chatCanBeFoundViaSearch": "Chat can be found via search"
"chatCanBeFoundViaSearch": "Chat can be found via search",
"requireCodeToJoin": "Require code to join",
"canFindInSearch": "Can find in search",
"addSubspaceWarning": "Once you add this, it will not appear in public search results, and it will be visible to all members of the parent space."
}

View file

@ -924,6 +924,15 @@ class ChatListController extends State<ChatList>
.toList(),
);
if (space == null) return;
// #Pangea
if (room.isSpace) {
final resp = await showOkCancelAlertDialog(
context: context,
title: L10n.of(context)!.addSubspaceWarning,
);
if (resp == OkCancelResult.cancel) return;
}
// Pangea#
await showFutureLoadingDialog(
context: context,
// #Pangea
@ -949,10 +958,10 @@ class ChatListController extends State<ChatList>
}
// #Pangea
// Remove a room from a space. Often, the user will have permission to set
// the SpaceChild event for the parent space, but not the SpaceParent event.
// This would cause a permissions error, but the child will still be removed
// via the SpaceChild event. If that's the case, silence the error.
/// Remove a room from a space. Often, the user will have permission to set
/// the SpaceChild event for the parent space, but not the SpaceParent event.
/// This would cause a permissions error, but the child will still be removed
/// via the SpaceChild event. If that's the case, silence the error.
Future<void> removeSpaceChild(Room space, String roomId) async {
try {
await space.removeSpaceChild(roomId);

View file

@ -70,34 +70,34 @@ class NewGroupView extends StatelessWidget {
),
),
const SizedBox(height: 16),
SwitchListTile.adaptive(
contentPadding: const EdgeInsets.symmetric(horizontal: 32),
secondary: const Icon(Icons.public_outlined),
title: Text(L10n.of(context)!.groupIsPublic),
value: controller.publicGroup,
onChanged: controller.loading ? null : controller.setPublicGroup,
),
AnimatedSize(
duration: FluffyThemes.animationDuration,
child: controller.publicGroup
? SwitchListTile.adaptive(
contentPadding:
const EdgeInsets.symmetric(horizontal: 32),
secondary: const Icon(Icons.search_outlined),
// #Pangea
// title: Text(L10n.of(context)!.groupCanBeFoundViaSearch),
title: Text(L10n.of(context)!.chatCanBeFoundViaSearch),
// Pangea#
value: controller.groupCanBeFound,
onChanged: controller.loading
? null
: controller.setGroupCanBeFound,
)
: const SizedBox.shrink(),
),
// #Pangea
// SwitchListTile.adaptive(
// contentPadding: const EdgeInsets.symmetric(horizontal: 32),
// secondary: const Icon(Icons.public_outlined),
// title: Text(L10n.of(context)!.groupIsPublic),
// value: controller.publicGroup,
// onChanged: controller.loading ? null : controller.setPublicGroup,
// ),
// AnimatedSize(
// duration: FluffyThemes.animationDuration,
// child: controller.publicGroup
// ? SwitchListTile.adaptive(
// contentPadding:
// const EdgeInsets.symmetric(horizontal: 32),
// secondary: const Icon(Icons.search_outlined),
// // #Pangea
// // title: Text(L10n.of(context)!.groupCanBeFoundViaSearch),
// title: Text(L10n.of(context)!.chatCanBeFoundViaSearch),
// // Pangea#
// value: controller.groupCanBeFound,
// onChanged: controller.loading
// ? null
// : controller.setGroupCanBeFound,
// )
// : const SizedBox.shrink(),
// ),
// SwitchListTile.adaptive(
// contentPadding: const EdgeInsets.symmetric(horizontal: 32),
// secondary: Icon(
// Icons.lock_outlined,
// color: theme.colorScheme.onSurface,

View file

@ -27,10 +27,13 @@ class NewSpace extends StatefulWidget {
class NewSpaceController extends State<NewSpace> {
TextEditingController nameController = TextEditingController();
TextEditingController topicController = TextEditingController();
bool publicGroup = false;
// #Pangea
bool spaceCanBeFound = true;
// bool publicGroup = false;
bool requiredCodeToJoin = false;
bool spaceCanBeFound = false;
// Pangea#
bool loading = false;
String? nameError;
String? topicError;
@ -55,10 +58,13 @@ class NewSpaceController extends State<NewSpace> {
void setSpaceCanBeFound(bool b) => setState(() => spaceCanBeFound = b);
// Pangea#
void setPublicGroup(bool b) => setState(() => publicGroup = b);
// #Pangea
// void setPublicGroup(bool b) => setState(() => publicGroup = b);
void setRequireCode(bool b) => setState(() => requiredCodeToJoin = b);
// Pangea#
// #Pangea
List<StateEvent> initialState(String joinCode, bool publicGroup) {
List<StateEvent> initialState(String joinCode) {
return [
StateEvent(
type: EventTypes.RoomPowerLevels,
@ -77,9 +83,9 @@ class NewSpaceController extends State<NewSpace> {
StateEvent(
type: sdk.EventTypes.RoomJoinRules,
content: {
ModelKey.joinRule: publicGroup
? sdk.JoinRules.public.toString().replaceAll('JoinRules.', '')
: sdk.JoinRules.invite.toString().replaceAll('JoinRules.', ''),
ModelKey.joinRule: requiredCodeToJoin
? sdk.JoinRules.knock.toString().replaceAll('JoinRules.', '')
: sdk.JoinRules.public.toString().replaceAll('JoinRules.', ''),
ModelKey.accessCode: joinCode,
},
),
@ -109,24 +115,25 @@ class NewSpaceController extends State<NewSpace> {
// Pangea#
final spaceId = await client.createRoom(
preset: publicGroup
? sdk.CreateRoomPreset.publicChat
: sdk.CreateRoomPreset.privateChat,
// #Pangea
visibility: publicGroup && spaceCanBeFound
? sdk.Visibility.public
: sdk.Visibility.private,
// preset: publicGroup
// ? sdk.CreateRoomPreset.publicChat
// : sdk.CreateRoomPreset.privateChat,
visibility:
spaceCanBeFound ? sdk.Visibility.public : sdk.Visibility.private,
// Pangea#
creationContent: {'type': RoomCreationTypes.mSpace},
roomAliasName: publicGroup
? nameController.text.trim().toLowerCase().replaceAll(' ', '_')
: null,
// #Pangea
// roomAliasName: publicGroup
// ? nameController.text.trim().toLowerCase().replaceAll(' ', '_')
// : null,
// Pangea#
name: nameController.text.trim(),
topic: topicController.text.isEmpty ? null : topicController.text,
powerLevelContentOverride: {'events_default': 100},
initialState: [
// #Pangea
...initialState(joinCode, publicGroup),
...initialState(joinCode),
// Pangea#
if (avatar != null)
sdk.StateEvent(

View file

@ -58,18 +58,20 @@ class NewSpaceView extends StatelessWidget {
const SizedBox(height: 16),
SwitchListTile.adaptive(
contentPadding: const EdgeInsets.symmetric(horizontal: 32),
title: Text(L10n.of(context)!.spaceIsPublic),
value: controller.publicGroup,
onChanged: controller.setPublicGroup,
// #Pangea
// title: Text(L10n.of(context)!.spaceIsPublic),
title: Text(L10n.of(context)!.requireCodeToJoin),
// Pangea#
value: controller.requiredCodeToJoin,
onChanged: controller.setRequireCode,
),
// #Pangea
if (controller.publicGroup)
SwitchListTile.adaptive(
contentPadding: const EdgeInsets.symmetric(horizontal: 32),
title: Text(L10n.of(context)!.spaceCanBeFoundViaSearch),
value: controller.spaceCanBeFound,
onChanged: controller.setSpaceCanBeFound,
),
SwitchListTile.adaptive(
contentPadding: const EdgeInsets.symmetric(horizontal: 32),
title: Text(L10n.of(context)!.canFindInSearch),
value: controller.spaceCanBeFound,
onChanged: controller.setSpaceCanBeFound,
),
// Pangea#
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 32),

View file

@ -27,6 +27,7 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
// import markdown.dart
import 'package:html_unescape/html_unescape.dart';
import 'package:matrix/matrix.dart' as matrix;
import 'package:matrix/matrix.dart';
import 'package:matrix/src/utils/markdown.dart';
import 'package:sentry_flutter/sentry_flutter.dart';

View file

@ -109,25 +109,35 @@ extension ChildrenAndParentsRoomExtension on Room {
}
/// Wrapper around call to setSpaceChild with added functionality
/// to prevent adding one room to multiple spaces
/// to prevent adding one room to multiple spaces, and resets the
/// subspace's JoinRules and Visibility to defaults.
Future<void> _pangeaSetSpaceChild(
String roomId, {
bool? suggested,
}) async {
final Room? child = client.getRoomById(roomId);
if (child != null) {
final List<Room> spaceParents = child.pangeaSpaceParents;
for (final Room parent in spaceParents) {
try {
await parent.removeSpaceChild(roomId);
} catch (e) {
ErrorHandler.logError(
e: e,
m: 'Failed to remove child from parent',
);
}
if (child == null) return;
final List<Room> spaceParents = child.pangeaSpaceParents;
for (final Room parent in spaceParents) {
try {
await parent.removeSpaceChild(roomId);
} catch (e) {
ErrorHandler.logError(
e: e,
m: 'Failed to remove child from parent',
);
}
}
try {
await setSpaceChild(roomId, suggested: suggested);
await child.setJoinRules(JoinRules.public);
await child.client.setRoomVisibilityOnDirectory(
roomId,
visibility: matrix.Visibility.private,
);
} catch (err, stack) {
ErrorHandler.logError(e: err, s: stack);
}
}

View file

@ -313,14 +313,18 @@ class PangeaChatDetailsView extends StatelessWidget {
),
if (isGroupChat && room.isRoomAdmin)
Divider(color: theme.dividerColor, height: 1),
if (room.isRoomAdmin)
if (room.isRoomAdmin &&
room.isSpace &&
room.spaceParents.isEmpty)
VisibilityToggle(
room: room,
setVisibility: controller.setVisibility,
setJoinRules: controller.setJoinRules,
iconColor: iconColor,
),
if (room.isRoomAdmin)
if (room.isRoomAdmin &&
room.isSpace &&
room.spaceParents.isEmpty)
Divider(color: theme.dividerColor, height: 1),
RoomCapacityButton(
room: room,

View file

@ -1,5 +1,4 @@
import 'package:fluffychat/pages/chat_list/space_view.dart';
import 'package:fluffychat/pangea/widgets/chat/visibility_toggle.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart' as matrix;
@ -23,17 +22,6 @@ class AddRoomDialogState extends State<AddRoomDialog> {
final TextEditingController _roomDescriptionController =
TextEditingController();
matrix.Visibility visibility = matrix.Visibility.public;
JoinRules joinRules = JoinRules.public;
Future<void> _setJoinRules(JoinRules newJoinRules) async {
setState(() => joinRules = newJoinRules);
}
Future<void> _setVisibility(matrix.Visibility newVisibility) async {
setState(() => visibility = newVisibility);
}
@override
void dispose() {
_roomNameController.dispose();
@ -98,13 +86,6 @@ class AddRoomDialogState extends State<AddRoomDialog> {
],
),
),
VisibilityToggle(
setJoinRules: _setJoinRules,
setVisibility: _setVisibility,
spaceMode: widget.roomType == AddRoomType.subspace,
visibility: visibility,
joinRules: joinRules,
),
Padding(
padding: const EdgeInsets.all(20),
child: Row(
@ -121,13 +102,12 @@ class AddRoomDialogState extends State<AddRoomDialog> {
onPressed: () async {
final isValid = _formKey.currentState!.validate();
if (!isValid) return;
Navigator.of(context).pop(
RoomResponse(
roomName: _roomNameController.text,
roomDescription: _roomDescriptionController.text,
joinRules: joinRules,
visibility: visibility,
joinRules: JoinRules.public,
visibility: matrix.Visibility.private,
),
);
},

View file

@ -23,7 +23,7 @@ class VisibilityToggle extends StatelessWidget {
this.iconColor,
this.spaceMode = false,
this.visibility = matrix.Visibility.private,
this.joinRules = JoinRules.invite,
this.joinRules = JoinRules.knock,
this.showSearchToggle = true,
super.key,
});
@ -40,9 +40,7 @@ class VisibilityToggle extends StatelessWidget {
SwitchListTile.adaptive(
activeColor: AppConfig.activeToggleColor,
title: Text(
room?.isSpace ?? spaceMode
? L10n.of(context)!.spaceIsPublic
: L10n.of(context)!.groupIsPublic,
L10n.of(context)!.requireCodeToJoin,
style: TextStyle(
color: Theme.of(context).colorScheme.secondary,
fontWeight: FontWeight.bold,
@ -51,45 +49,40 @@ class VisibilityToggle extends StatelessWidget {
secondary: CircleAvatar(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
foregroundColor: iconColor,
child: const Icon(Icons.public_outlined),
child: const Icon(Icons.key_outlined),
),
value: _isPublic,
value: !_isPublic,
onChanged: (value) =>
setJoinRules(value ? JoinRules.public : JoinRules.invite),
setJoinRules(value ? JoinRules.knock : JoinRules.public),
),
if (_isPublic && showSearchToggle)
FutureBuilder(
future: room != null
? Matrix.of(context)
.client
.getRoomVisibilityOnDirectory(room!.id)
: null,
builder: (context, snapshot) {
return SwitchListTile.adaptive(
activeColor: AppConfig.activeToggleColor,
title: Text(
room?.isSpace ?? spaceMode
? L10n.of(context)!.spaceCanBeFoundViaSearch
: L10n.of(context)!.chatCanBeFoundViaSearch,
style: TextStyle(
color: Theme.of(context).colorScheme.secondary,
fontWeight: FontWeight.bold,
),
FutureBuilder(
future: room != null
? Matrix.of(context).client.getRoomVisibilityOnDirectory(room!.id)
: null,
builder: (context, snapshot) {
return SwitchListTile.adaptive(
activeColor: AppConfig.activeToggleColor,
title: Text(
L10n.of(context)!.canFindInSearch,
style: TextStyle(
color: Theme.of(context).colorScheme.secondary,
fontWeight: FontWeight.bold,
),
secondary: CircleAvatar(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
foregroundColor: iconColor,
child: const Icon(Icons.search_outlined),
),
value: room != null
? snapshot.data == matrix.Visibility.public
: visibility == matrix.Visibility.public,
onChanged: (value) => setVisibility(
value ? matrix.Visibility.public : matrix.Visibility.private,
),
);
},
),
),
secondary: CircleAvatar(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
foregroundColor: iconColor,
child: const Icon(Icons.search_outlined),
),
value: room != null
? snapshot.data == matrix.Visibility.public
: visibility == matrix.Visibility.public,
onChanged: (value) => setVisibility(
value ? matrix.Visibility.public : matrix.Visibility.private,
),
);
},
),
],
);
}