mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-11-26 11:04:56 +00:00
Refactor SpaceManagementPage to use StatefulWidget and initialize CommunitiesBloc in initState. Update CommunityStructureHeader to handle community updates and improve state management in CommunitiesTreeSelectionBloc with new event for community state updates.
This commit is contained in:
@ -16,21 +16,37 @@ import 'package:syncrow_web/services/api/http_service.dart';
|
|||||||
import 'package:syncrow_web/utils/theme/responsive_text_theme.dart';
|
import 'package:syncrow_web/utils/theme/responsive_text_theme.dart';
|
||||||
import 'package:syncrow_web/web_layout/web_scaffold.dart';
|
import 'package:syncrow_web/web_layout/web_scaffold.dart';
|
||||||
|
|
||||||
class SpaceManagementPage extends StatelessWidget {
|
class SpaceManagementPage extends StatefulWidget {
|
||||||
const SpaceManagementPage({super.key});
|
const SpaceManagementPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SpaceManagementPage> createState() => _SpaceManagementPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SpaceManagementPageState extends State<SpaceManagementPage> {
|
||||||
|
late final CommunitiesBloc communitiesBloc;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
communitiesBloc = CommunitiesBloc(
|
||||||
|
communitiesService: DebouncedCommunitiesService(
|
||||||
|
RemoteCommunitiesService(HTTPService()),
|
||||||
|
),
|
||||||
|
)..add(const LoadCommunities(LoadCommunitiesParam()));
|
||||||
|
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MultiBlocProvider(
|
return MultiBlocProvider(
|
||||||
providers: [
|
providers: [
|
||||||
|
BlocProvider.value(value: communitiesBloc),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) => CommunitiesBloc(
|
create: (context) => CommunitiesTreeSelectionBloc(
|
||||||
communitiesService: DebouncedCommunitiesService(
|
communitiesBloc: communitiesBloc,
|
||||||
RemoteCommunitiesService(HTTPService()),
|
),
|
||||||
),
|
|
||||||
)..add(const LoadCommunities(LoadCommunitiesParam())),
|
|
||||||
),
|
),
|
||||||
BlocProvider(create: (context) => CommunitiesTreeSelectionBloc()),
|
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) => SpaceDetailsBloc(
|
create: (context) => SpaceDetailsBloc(
|
||||||
UniqueSubspacesDecorator(
|
UniqueSubspacesDecorator(
|
||||||
|
|||||||
@ -3,7 +3,10 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/main_module/shared/helpers/space_management_community_dialog_helper.dart';
|
import 'package:syncrow_web/pages/space_management_v2/main_module/shared/helpers/space_management_community_dialog_helper.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/community_structure_header_action_buttons.dart';
|
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/community_structure_header_action_buttons.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/bloc/communities_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/helpers/space_details_dialog_helper.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/helpers/space_details_dialog_helper.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
@ -11,6 +14,26 @@ import 'package:syncrow_web/utils/constants/assets.dart';
|
|||||||
class CommunityStructureHeader extends StatelessWidget {
|
class CommunityStructureHeader extends StatelessWidget {
|
||||||
const CommunityStructureHeader({super.key});
|
const CommunityStructureHeader({super.key});
|
||||||
|
|
||||||
|
List<SpaceModel> _updateRecursive(
|
||||||
|
List<SpaceModel> spaces,
|
||||||
|
SpaceDetailsModel updatedSpace,
|
||||||
|
) {
|
||||||
|
return spaces.map((space) {
|
||||||
|
if (space.uuid == updatedSpace.uuid) {
|
||||||
|
return space.copyWith(
|
||||||
|
spaceName: updatedSpace.spaceName,
|
||||||
|
icon: updatedSpace.icon,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (space.children.isNotEmpty) {
|
||||||
|
return space.copyWith(
|
||||||
|
children: _updateRecursive(space.children, updatedSpace),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return space;
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
@ -99,6 +122,19 @@ class CommunityStructureHeader extends StatelessWidget {
|
|||||||
context,
|
context,
|
||||||
spaceModel: selectedSpace!,
|
spaceModel: selectedSpace!,
|
||||||
communityUuid: selectedCommunity.uuid,
|
communityUuid: selectedCommunity.uuid,
|
||||||
|
onSuccess: (updatedSpaceDetails) {
|
||||||
|
final communitiesBloc = context.read<CommunitiesBloc>();
|
||||||
|
final updatedSpaces = _updateRecursive(
|
||||||
|
selectedCommunity.spaces,
|
||||||
|
updatedSpaceDetails,
|
||||||
|
);
|
||||||
|
|
||||||
|
final community = selectedCommunity.copyWith(
|
||||||
|
spaces: updatedSpaces,
|
||||||
|
);
|
||||||
|
|
||||||
|
communitiesBloc.add(CommunitiesUpdateCommunity(community));
|
||||||
|
},
|
||||||
),
|
),
|
||||||
selectedSpace: selectedSpace,
|
selectedSpace: selectedSpace,
|
||||||
),
|
),
|
||||||
|
|||||||
@ -46,6 +46,25 @@ class SpaceModel extends Equatable {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SpaceModel copyWith({
|
||||||
|
String? uuid,
|
||||||
|
DateTime? createdAt,
|
||||||
|
DateTime? updatedAt,
|
||||||
|
String? spaceName,
|
||||||
|
String? icon,
|
||||||
|
List<SpaceModel>? children,
|
||||||
|
}) {
|
||||||
|
return SpaceModel(
|
||||||
|
uuid: uuid ?? this.uuid,
|
||||||
|
createdAt: createdAt ?? this.createdAt,
|
||||||
|
updatedAt: updatedAt ?? this.updatedAt,
|
||||||
|
spaceName: spaceName ?? this.spaceName,
|
||||||
|
icon: icon ?? this.icon,
|
||||||
|
children: children ?? this.children,
|
||||||
|
parent: parent,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [uuid, spaceName, icon, children];
|
List<Object?> get props => [uuid, spaceName, icon, children];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +1,39 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/bloc/communities_bloc.dart';
|
||||||
|
|
||||||
part 'communities_tree_selection_event.dart';
|
part 'communities_tree_selection_event.dart';
|
||||||
part 'communities_tree_selection_state.dart';
|
part 'communities_tree_selection_state.dart';
|
||||||
|
|
||||||
class CommunitiesTreeSelectionBloc
|
class CommunitiesTreeSelectionBloc
|
||||||
extends Bloc<CommunitiesTreeSelectionEvent, CommunitiesTreeSelectionState> {
|
extends Bloc<CommunitiesTreeSelectionEvent, CommunitiesTreeSelectionState> {
|
||||||
CommunitiesTreeSelectionBloc() : super(const CommunitiesTreeSelectionState()) {
|
CommunitiesTreeSelectionBloc({
|
||||||
|
required CommunitiesBloc communitiesBloc,
|
||||||
|
}) : _communitiesBloc = communitiesBloc,
|
||||||
|
super(const CommunitiesTreeSelectionState()) {
|
||||||
on<SelectCommunityEvent>(_onSelectCommunity);
|
on<SelectCommunityEvent>(_onSelectCommunity);
|
||||||
on<SelectSpaceEvent>(_onSelectSpace);
|
on<SelectSpaceEvent>(_onSelectSpace);
|
||||||
on<ClearCommunitiesTreeSelectionEvent>(_onClearSelection);
|
on<ClearCommunitiesTreeSelectionEvent>(_onClearSelection);
|
||||||
|
on<_CommunitiesStateUpdated>(_onCommunitiesStateUpdated);
|
||||||
|
|
||||||
|
_communitiesSubscription = _communitiesBloc.stream.listen((communitiesState) {
|
||||||
|
if (state.selectedCommunity != null) {
|
||||||
|
add(_CommunitiesStateUpdated(communitiesState));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
final CommunitiesBloc _communitiesBloc;
|
||||||
|
late final StreamSubscription<CommunitiesState> _communitiesSubscription;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() {
|
||||||
|
_communitiesSubscription.cancel();
|
||||||
|
return super.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onSelectCommunity(
|
void _onSelectCommunity(
|
||||||
@ -44,4 +66,59 @@ class CommunitiesTreeSelectionBloc
|
|||||||
) {
|
) {
|
||||||
emit(const CommunitiesTreeSelectionState());
|
emit(const CommunitiesTreeSelectionState());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onCommunitiesStateUpdated(
|
||||||
|
_CommunitiesStateUpdated event,
|
||||||
|
Emitter<CommunitiesTreeSelectionState> emit,
|
||||||
|
) {
|
||||||
|
if (state.selectedCommunity == null) return;
|
||||||
|
|
||||||
|
final communities = event.communitiesState.communities;
|
||||||
|
try {
|
||||||
|
final updatedCommunity = communities.firstWhere(
|
||||||
|
(c) => c.uuid == state.selectedCommunity!.uuid,
|
||||||
|
);
|
||||||
|
|
||||||
|
var updatedSelectedSpace = state.selectedSpace;
|
||||||
|
if (state.selectedSpace != null) {
|
||||||
|
updatedSelectedSpace = _findSpaceInCommunity(
|
||||||
|
updatedCommunity,
|
||||||
|
state.selectedSpace!.uuid,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
selectedCommunity: updatedCommunity,
|
||||||
|
selectedSpace: updatedSelectedSpace,
|
||||||
|
clearSelectedSpace: updatedSelectedSpace == null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
add(const ClearCommunitiesTreeSelectionEvent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SpaceModel? _findSpaceInCommunity(CommunityModel community, String spaceUuid) {
|
||||||
|
try {
|
||||||
|
return _findSpaceRecursive(community.spaces, spaceUuid);
|
||||||
|
} catch (_) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SpaceModel _findSpaceRecursive(List<SpaceModel> spaces, String spaceUuid) {
|
||||||
|
for (final space in spaces) {
|
||||||
|
if (space.uuid == spaceUuid) {
|
||||||
|
return space;
|
||||||
|
}
|
||||||
|
if (space.children.isNotEmpty) {
|
||||||
|
try {
|
||||||
|
return _findSpaceRecursive(space.children, spaceUuid);
|
||||||
|
} catch (_) {
|
||||||
|
// not found in this branch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw Exception('Space not found');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,3 +29,12 @@ final class ClearCommunitiesTreeSelectionEvent
|
|||||||
extends CommunitiesTreeSelectionEvent {
|
extends CommunitiesTreeSelectionEvent {
|
||||||
const ClearCommunitiesTreeSelectionEvent();
|
const ClearCommunitiesTreeSelectionEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final class _CommunitiesStateUpdated extends CommunitiesTreeSelectionEvent {
|
||||||
|
const _CommunitiesStateUpdated(this.communitiesState);
|
||||||
|
|
||||||
|
final CommunitiesState communitiesState;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [communitiesState];
|
||||||
|
}
|
||||||
|
|||||||
@ -12,18 +12,14 @@ final class CommunitiesTreeSelectionState extends Equatable {
|
|||||||
CommunitiesTreeSelectionState copyWith({
|
CommunitiesTreeSelectionState copyWith({
|
||||||
CommunityModel? selectedCommunity,
|
CommunityModel? selectedCommunity,
|
||||||
SpaceModel? selectedSpace,
|
SpaceModel? selectedSpace,
|
||||||
List<CommunityModel>? expandedCommunities,
|
bool clearSelectedSpace = false,
|
||||||
List<SpaceModel>? expandedSpaces,
|
|
||||||
}) {
|
}) {
|
||||||
return CommunitiesTreeSelectionState(
|
return CommunitiesTreeSelectionState(
|
||||||
selectedCommunity: selectedCommunity ?? this.selectedCommunity,
|
selectedCommunity: selectedCommunity ?? this.selectedCommunity,
|
||||||
selectedSpace: selectedSpace ?? this.selectedSpace,
|
selectedSpace: clearSelectedSpace ? null : selectedSpace ?? this.selectedSpace,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => [selectedCommunity, selectedSpace];
|
||||||
selectedCommunity,
|
}
|
||||||
selectedSpace,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|||||||
@ -42,6 +42,7 @@ abstract final class SpaceDetailsDialogHelper {
|
|||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
required SpaceModel spaceModel,
|
required SpaceModel spaceModel,
|
||||||
required String communityUuid,
|
required String communityUuid,
|
||||||
|
required void Function(SpaceDetailsModel updatedSpaceDetails)? onSuccess,
|
||||||
}) {
|
}) {
|
||||||
showDialog<void>(
|
showDialog<void>(
|
||||||
context: context,
|
context: context,
|
||||||
@ -60,7 +61,11 @@ abstract final class SpaceDetailsDialogHelper {
|
|||||||
],
|
],
|
||||||
child: Builder(
|
child: Builder(
|
||||||
builder: (context) => BlocListener<UpdateSpaceBloc, UpdateSpaceState>(
|
builder: (context) => BlocListener<UpdateSpaceBloc, UpdateSpaceState>(
|
||||||
listener: _updateListener,
|
listener: (context, state) => _updateListener(
|
||||||
|
context,
|
||||||
|
state,
|
||||||
|
onSuccess,
|
||||||
|
),
|
||||||
child: SpaceDetailsDialog(
|
child: SpaceDetailsDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: const SelectableText('Edit Space'),
|
title: const SelectableText('Edit Space'),
|
||||||
@ -81,17 +86,28 @@ abstract final class SpaceDetailsDialogHelper {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _updateListener(BuildContext context, UpdateSpaceState state) {
|
static void _updateListener(
|
||||||
|
BuildContext context,
|
||||||
|
UpdateSpaceState state,
|
||||||
|
void Function(SpaceDetailsModel updatedSpaceDetails)? onSuccess,
|
||||||
|
) {
|
||||||
return switch (state) {
|
return switch (state) {
|
||||||
UpdateSpaceInitial() => null,
|
UpdateSpaceInitial() => null,
|
||||||
UpdateSpaceLoading() => _onLoading(context),
|
UpdateSpaceLoading() => _onLoading(context),
|
||||||
UpdateSpaceSuccess(:final space) => _onUpdateSuccess(context, space),
|
UpdateSpaceSuccess(:final space) =>
|
||||||
|
_onUpdateSuccess(context, space, onSuccess),
|
||||||
UpdateSpaceFailure(:final errorMessage) => _onError(context, errorMessage),
|
UpdateSpaceFailure(:final errorMessage) => _onError(context, errorMessage),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _onUpdateSuccess(BuildContext context, SpaceDetailsModel space) {
|
static void _onUpdateSuccess(
|
||||||
|
BuildContext context,
|
||||||
|
SpaceDetailsModel space,
|
||||||
|
void Function(SpaceDetailsModel updatedSpaceDetails)? onSuccess,
|
||||||
|
) {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
onSuccess?.call(space);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _onLoading(BuildContext context) {
|
static void _onLoading(BuildContext context) {
|
||||||
|
|||||||
Reference in New Issue
Block a user