fix: on login, don't show invite dialog for previously knocked spaces (#5880)

* fix: on login, don't show invite dialog for previously knocked spaces

* await onTap space

* another fix
This commit is contained in:
ggurdin 2026-03-04 11:14:25 -05:00 committed by GitHub
parent 4bcf3737f8
commit d5ae87abf7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 143 additions and 129 deletions

View file

@ -604,7 +604,7 @@ class ChatListController extends State<ChatList>
).client.rooms.where((r) => r.isSpace && r.membership == Membership.invite);
for (final space in invitedSpaces) {
await showInviteDialog(space, context);
await SpaceTapUtil.onTap(context, space);
}
}
@ -641,9 +641,7 @@ class ChatListController extends State<ChatList>
final roomCode = room.classCode?.toLowerCase();
final cachedCode = SpaceCodeRepo.recentCode?.toLowerCase();
if (cachedCode == roomCode) continue;
// Show invite popup
chatListHandleSpaceTap(context, room);
await SpaceTapUtil.onTap(context, room);
}
}
}

View file

@ -3,142 +3,63 @@ import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/chat_list/widgets/room_invite_dialog.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/pangea/join_codes/knock_room_extension.dart';
import 'package:fluffychat/pangea/join_codes/space_code_repo.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
import 'package:fluffychat/widgets/matrix.dart';
import '../../common/utils/error_handler.dart';
Future<void> showInviteDialog(Room room, BuildContext context) async {
if (room.membership != Membership.invite) return;
final theme = Theme.of(context);
final action = await showAdaptiveDialog<CourseInviteAction>(
barrierDismissible: true,
context: context,
builder: (context) => AlertDialog.adaptive(
title: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 256),
child: Center(
child: Text(
L10n.of(context).youreInvited,
textAlign: TextAlign.center,
),
),
),
content: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 256, maxHeight: 256),
child: Text(
room.isSpace
? L10n.of(
context,
).invitedToSpace(room.name, room.creatorId ?? "???")
: L10n.of(
context,
).invitedToChat(room.name, room.creatorId ?? "???"),
textAlign: TextAlign.center,
),
),
actions: [
AdaptiveDialogAction(
onPressed: () =>
Navigator.of(context).pop(CourseInviteAction.decline),
bigButtons: true,
child: Text(
L10n.of(context).decline,
style: TextStyle(color: theme.colorScheme.error),
),
),
AdaptiveDialogAction(
onPressed: () => Navigator.of(context).pop(CourseInviteAction.accept),
bigButtons: true,
child: Text(L10n.of(context).accept),
),
],
),
);
switch (action) {
case null:
return;
case CourseInviteAction.accept:
break;
case CourseInviteAction.decline:
await room.leave();
return;
class SpaceTapUtil {
static Future<void> onTap(BuildContext context, Room space) async {
switch (space.membership) {
case Membership.join:
context.go("/rooms/spaces/${space.id}/details");
return;
case Membership.leave:
await _autoJoin(context, space);
return;
case Membership.invite:
await _onInviteTap(context, space);
return;
case Membership.ban:
case Membership.knock:
context.go("/rooms/spaces/${space.id}/details");
ErrorHandler.logError(
m: 'should not show space with membership ${space.membership}',
data: space.toJson(),
);
}
}
final joinResult = await showFutureLoadingDialog(
context: context,
future: () async {
await room.joinKnockedRoom();
},
exceptionContext: ExceptionContext.joinRoom,
);
if (joinResult.error != null) return;
static Future<void> _autoJoin(BuildContext context, Room space) async {
final resp = await showFutureLoadingDialog(
context: context,
future: space.joinKnockedRoom,
);
if (room.membership != Membership.join) {
await room.client.waitForRoomInSync(room.id, join: true);
}
context.go(
room.isSpace ? "/rooms/spaces/${room.id}/details" : "/rooms/${room.id}",
);
}
// ignore: curly_braces_in_flow_control_structures
void chatListHandleSpaceTap(BuildContext context, Room space) {
void setActiveSpaceAndCloseChat() {
if (resp.isError) return;
context.go("/rooms/spaces/${space.id}/details");
}
void autoJoin(Room space) {
showFutureLoadingDialog(
context: context,
future: () async {
await space.joinKnockedRoom();
setActiveSpaceAndCloseChat();
},
);
}
static Future<void> _onInviteTap(BuildContext context, Room space) async {
final justInputtedCode = SpaceCodeRepo.recentCode;
final spaceCode = space.classCode;
if (spaceCode != null && justInputtedCode == spaceCode) {
return;
}
switch (space.membership) {
case Membership.join:
setActiveSpaceAndCloseChat();
break;
case Membership.invite:
//if space is a child of a space you're in, automatically join
//else confirm you want to join
//can we tell whether space or chat?
final rooms = Matrix.of(context).client.rooms.where(
(element) => element.isSpace && element.membership == Membership.join,
);
final justInputtedCode = SpaceCodeRepo.recentCode;
if (rooms.any((s) => s.spaceChildren.any((c) => c.roomId == space.id))) {
autoJoin(space);
} else if (justInputtedCode != null &&
justInputtedCode == space.classCode) {
// do nothing
} else if (space.hasKnocked) {
autoJoin(space);
} else {
showInviteDialog(space, context);
}
break;
case Membership.leave:
autoJoin(space);
break;
default:
setActiveSpaceAndCloseChat();
ErrorHandler.logError(
m: 'should not show space with membership ${space.membership}',
data: space.toJson(),
);
break;
final rooms = Matrix.of(context).client.rooms.where(
(element) => element.isSpace && element.membership == Membership.join,
);
final isSpaceChild = rooms.any(
(s) => s.spaceChildren.any((c) => c.roomId == space.id),
);
isSpaceChild || space.hasKnocked
? await _autoJoin(context, space)
: await RoomInviteDialog.show(context, space);
}
}
enum CourseInviteAction { accept, decline }

View file

@ -0,0 +1,95 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/pangea/join_codes/knock_room_extension.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
enum CourseInviteAction { accept, decline }
class RoomInviteDialog extends StatelessWidget {
final Room room;
const RoomInviteDialog({super.key, required this.room});
static Future<void> show(BuildContext context, Room room) async {
final resp = await showDialog<CourseInviteAction>(
context: context,
builder: (context) => RoomInviteDialog(room: room),
);
switch (resp) {
case CourseInviteAction.accept:
final joinResult = await showFutureLoadingDialog(
context: context,
future: room.joinKnockedRoom,
exceptionContext: ExceptionContext.joinRoom,
);
if (joinResult.isError) return;
if (room.membership != Membership.join) {
await room.client.waitForRoomInSync(room.id, join: true);
}
context.go(
room.isSpace
? "/rooms/spaces/${room.id}/details"
: "/rooms/${room.id}",
);
return;
case CourseInviteAction.decline:
await room.leave();
return;
case null:
return;
}
}
@override
Widget build(BuildContext context) {
return AlertDialog.adaptive(
title: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 256),
child: Center(
child: Text(
L10n.of(context).youreInvited,
textAlign: TextAlign.center,
),
),
),
content: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 256, maxHeight: 256),
child: Text(
room.isSpace
? L10n.of(
context,
).invitedToSpace(room.name, room.creatorId ?? "???")
: L10n.of(
context,
).invitedToChat(room.name, room.creatorId ?? "???"),
textAlign: TextAlign.center,
),
),
actions: [
AdaptiveDialogAction(
onPressed: () =>
Navigator.of(context).pop(CourseInviteAction.decline),
bigButtons: true,
child: Text(
L10n.of(context).decline,
style: TextStyle(color: Theme.of(context).colorScheme.error),
),
),
AdaptiveDialogAction(
onPressed: () => Navigator.of(context).pop(CourseInviteAction.accept),
bigButtons: true,
child: Text(L10n.of(context).accept),
),
],
);
}
}

View file

@ -233,11 +233,11 @@ class SpacesNavigationRail extends StatelessWidget {
backgroundColor: Colors.transparent,
borderRadius: BorderRadius.circular(0),
// onTap: () => onGoToSpaceId(allSpaces[i].id),
onTap: () {
onTap: () async {
collapse();
final room = client.getRoomById(allSpaces[i].id);
if (room != null) {
chatListHandleSpaceTap(context, room);
await SpaceTapUtil.onTap(context, room);
} else {
context.go(
"/rooms/spaces/${allSpaces[i].id}/details",