Files
Client/lib/routing/app_routes.dart
T
2026-05-08 20:12:40 +02:00

202 lines
6.4 KiB
Dart

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
import '../api/marianumcloud/talk/room/get_room_response.dart';
import '../main.dart';
import '../model/account_data.dart';
import '../state/app/modules/app_modules.dart';
import '../state/app/modules/chat/bloc/chat_bloc.dart';
import '../state/app/modules/chat_list/bloc/chat_list_bloc.dart';
import '../state/app/modules/marianum_message/bloc/marianum_message_state.dart';
import '../view/pages/files/files.dart';
import '../view/pages/marianum_message/marianum_message_view.dart';
import '../view/pages/more/feedback/feedback_dialog.dart';
import '../view/pages/more/roomplan/roomplan.dart';
import '../view/pages/more/share/qr_share_view.dart';
import '../view/pages/settings/modules_settings_page.dart';
import '../view/pages/settings/settings.dart';
import '../view/pages/talk/chat_view.dart';
import '../view/pages/talk/details/message_reactions.dart';
import '../view/pages/talk/talk_navigator.dart';
import '../view/pages/timetable/custom_events/custom_events_view.dart';
import '../widget/debug/cache_view.dart';
import '../widget/file_viewer.dart';
import '../widget/user_avatar.dart';
/// Single entry point for full-page navigations. Dialogs and bottom sheets
/// stay at the call sites; only full-page pushes go through here.
class AppRoutes {
AppRoutes._();
/// Set by [openChatByToken] (e.g. from a tapped notification) and consumed
/// by `ChatList` once the matching room is loaded.
static final ValueNotifier<String?> pendingChatToken = ValueNotifier(null);
static void openFolder(BuildContext context, List<String> path) {
pushScreen(context, withNavBar: false, screen: Files(path: path));
}
static void openFileViewer(
BuildContext context,
String localPath, {
bool openExternal = false,
}) {
pushScreen(
context,
withNavBar: false,
screen: FileViewer(path: localPath, openExternal: openExternal),
);
}
static void openCustomEvents(BuildContext context) {
pushScreen(context, withNavBar: false, screen: const CustomEventsView());
}
static void openMarianumMessage(
BuildContext context,
String basePath,
MarianumMessage message,
) {
pushScreen(
context,
withNavBar: false,
screen: MessageView(basePath: basePath, message: message),
);
}
static void openQrShare(BuildContext context) {
pushScreen(context, withNavBar: false, screen: const QrShareView());
}
static void openSettings(BuildContext context) {
pushScreen(context, withNavBar: false, screen: const Settings());
}
static void openModulesSettings(BuildContext context) {
pushScreen(context, withNavBar: false, screen: const ModulesSettingsPage());
}
static void openFeedback(BuildContext context) {
pushScreen(context, withNavBar: false, screen: const FeedbackDialog());
}
static void openCacheView(BuildContext context) {
pushScreen(context, withNavBar: false, screen: const CacheView());
}
static void openRoomplan(BuildContext context) {
pushScreen(context, withNavBar: false, screen: const Roomplan());
}
static void openMessageReactions(
BuildContext context,
String token,
int messageId,
) {
pushScreen(
context,
withNavBar: false,
screen: MessageReactions(token: token, messageId: messageId),
);
}
static void openChatView(
BuildContext context, {
required GetRoomResponseObject room,
required String selfId,
required UserAvatar avatar,
bool overrideToSingleSubScreen = true,
}) {
TalkNavigator.pushSplitView(
context,
ChatView(room: room, selfId: selfId, avatar: avatar),
overrideToSingleSubScreen: overrideToSingleSubScreen,
);
context.read<ChatBloc>().setToken(room.token);
}
/// Schedules a chat to be opened in the Talk tab once the room is loaded.
/// Use when only the token is known (e.g. from a tapped notification).
static void openChatByToken(BuildContext context, String token) {
pendingChatToken.value = token;
goToTalkTab(context);
try {
context.read<ChatListBloc>().refresh();
} catch (e) {
if (kDebugMode) {
debugPrint('openChatByToken: ChatListBloc refresh failed: $e');
}
}
}
/// Resolves a pending chat token (set via [openChatByToken]). Returns null
/// if the room or account is not ready yet — callers should retry when
/// [pendingChatToken] or the bloc state change.
static ResolvedPendingChat? resolvePendingChat(BuildContext context) {
final token = pendingChatToken.value;
if (token == null) return null;
if (!AccountData().isPopulated()) return null;
final rooms = context.read<ChatListBloc>().state.data?.rooms;
final room = _findRoomByToken(rooms, token);
if (room == null) return null;
final isGroup = room.type != GetRoomResponseObjectConversationType.oneToOne;
final avatar = UserAvatar(
id: isGroup ? room.token : room.name,
isGroup: isGroup,
);
return ResolvedPendingChat(
room: room,
selfId: AccountData().getUsername(),
avatar: avatar,
);
}
static GetRoomResponseObject? _findRoomByToken(
GetRoomResponse? rooms,
String token,
) {
if (rooms == null) return null;
for (final room in rooms.data) {
if (room.token == token) return room;
}
return null;
}
/// Pushes a module from the "Mehr" tab list. Modules already in the bottom
/// bar are switched to via [goToTab] instead.
static void openModule(BuildContext context, AppModule module) {
pushScreen(context, withNavBar: false, screen: module.create());
}
/// Switches the bottom navigation to [module]. Returns false when the
/// module is not currently in the bottom bar.
static bool goToTab(BuildContext context, Modules module) {
final index = AppModule.getBottomBarModules(
context,
).map((e) => e.module).toList().indexOf(module);
if (index == -1) return false;
Main.bottomNavigator.jumpToTab(index);
return true;
}
static void goToTalkTab(BuildContext context) {
goToTab(context, Modules.talk);
}
}
class ResolvedPendingChat {
final GetRoomResponseObject room;
final String selfId;
final UserAvatar avatar;
const ResolvedPendingChat({
required this.room,
required this.selfId,
required this.avatar,
});
}