feat: Refactor code structure and optimize performance across multiple modules
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
const String allWorkingAreas = r'''
|
||||
query allWorkingAreas {
|
||||
working_areas {
|
||||
id
|
||||
address
|
||||
}
|
||||
}
|
||||
''';
|
||||
|
||||
const String staffWorkingAreas = r'''
|
||||
query staffAreas {
|
||||
me {
|
||||
id
|
||||
working_areas {
|
||||
id
|
||||
address
|
||||
}
|
||||
}
|
||||
}
|
||||
''';
|
||||
|
||||
const syncWorkingAreas = r'''
|
||||
mutation sync($areas: [ID!]!) {
|
||||
sync_staff_working_areas(areas: $areas) {
|
||||
}
|
||||
}
|
||||
''';
|
||||
@@ -0,0 +1,9 @@
|
||||
import 'package:krow/core/data/models/staff/workin_area.dart';
|
||||
|
||||
abstract class WorkingAreaRepository {
|
||||
Stream<List<WorkingArea>> getAllWorkingAreas();
|
||||
|
||||
Stream<List<WorkingArea>> getStaffWorkingAreas();
|
||||
|
||||
Future<void> saveWorkingArea(List<String> ids);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import 'package:graphql_flutter/graphql_flutter.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:krow/core/application/clients/api/api_client.dart';
|
||||
import 'package:krow/core/data/models/staff/workin_area.dart';
|
||||
import 'package:krow/features/profile/working_area/data/gql.dart';
|
||||
|
||||
@injectable
|
||||
class WorkingAreasApiProvider {
|
||||
final ApiClient _apiClient;
|
||||
|
||||
WorkingAreasApiProvider(this._apiClient);
|
||||
|
||||
Stream<List<WorkingArea>> getAllWorkingAreas() async* {
|
||||
await for (var result
|
||||
in _apiClient.queryWithCache(schema: allWorkingAreas)) {
|
||||
if (result == null || result.data == null) continue;
|
||||
|
||||
if (result.hasException) {
|
||||
throw Exception(result.exception.toString());
|
||||
}
|
||||
if (result.data?['working_areas'] == null) continue;
|
||||
|
||||
yield result.data!['working_areas'].map<WorkingArea>((e) {
|
||||
return WorkingArea.fromJson(e);
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
|
||||
Stream<List<WorkingArea>> getStaffWorkingAreas() async* {
|
||||
await for (var result
|
||||
in _apiClient.queryWithCache(schema: staffWorkingAreas)) {
|
||||
if (result == null || result.data == null) {
|
||||
continue;
|
||||
}
|
||||
if (result.hasException) {
|
||||
if (result.exception is OperationException) continue;
|
||||
throw Exception(result.exception.toString());
|
||||
}
|
||||
|
||||
if (result.data?['me']?['working_areas'] == null) continue;
|
||||
|
||||
yield result.data!['me']['working_areas'].map<WorkingArea>((e) {
|
||||
return WorkingArea.fromJson(e);
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> putWorkingAreas(List<String> workingAreas) async {
|
||||
final Map<String, dynamic> variables = {
|
||||
'areas': workingAreas,
|
||||
};
|
||||
var result =
|
||||
await _apiClient.mutate(schema: syncWorkingAreas, body: variables);
|
||||
|
||||
if (result.hasException) throw Exception(result.exception.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:krow/core/application/di/injectable.dart';
|
||||
import 'package:krow/core/data/models/staff/workin_area.dart';
|
||||
import 'package:krow/core/data/enums/state_status.dart';
|
||||
import 'package:krow/features/profile/working_area/data/working_area_repository.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
|
||||
part 'working_area_event.dart';
|
||||
part 'working_area_state.dart';
|
||||
|
||||
class WorkingAreaBloc extends Bloc<WorkingAreaEvent, WorkingAreaState> {
|
||||
WorkingAreaBloc() : super(const WorkingAreaState()) {
|
||||
on<InitializeWorkingAreaEvent>(_onInitialize);
|
||||
on<CheckAreaEvent>(_onCheckArea);
|
||||
on<SubmitWorkingAreaEvent>(_onSubmit);
|
||||
}
|
||||
|
||||
void _onInitialize(
|
||||
InitializeWorkingAreaEvent event, Emitter<WorkingAreaState> emit) async {
|
||||
emit(state.copyWith(status: StateStatus.loading));
|
||||
|
||||
|
||||
var staffWorkingAreasStream =
|
||||
getIt<WorkingAreaRepository>().getStaffWorkingAreas();
|
||||
var allWorkingAreasStream =
|
||||
getIt<WorkingAreaRepository>().getAllWorkingAreas();
|
||||
|
||||
await for (var areas in CombineLatestStream.combine2(
|
||||
allWorkingAreasStream,
|
||||
staffWorkingAreasStream,
|
||||
(List<WorkingArea> allAreas, List<WorkingArea> staffAreas) {
|
||||
return allAreas.map((area) {
|
||||
var selected = staffAreas.any((assignedArea) => assignedArea.id == area.id);
|
||||
return AreaItemState(id: area.id, name: area.address, selected: selected);
|
||||
}).toList();
|
||||
},
|
||||
)) {
|
||||
emit(state.copyWith(status: StateStatus.idle, areas: areas));
|
||||
}
|
||||
}
|
||||
|
||||
void _onCheckArea(CheckAreaEvent event, Emitter<WorkingAreaState> emit) {
|
||||
var areas = state.areas.map((e) {
|
||||
if (e.id == event.id) {
|
||||
return e.copyWith(selected: event.selected);
|
||||
}
|
||||
return e;
|
||||
}).toList();
|
||||
emit(state.copyWith(areas: areas));
|
||||
}
|
||||
|
||||
void _onSubmit(
|
||||
SubmitWorkingAreaEvent event, Emitter<WorkingAreaState> emit) async {
|
||||
emit(state.copyWith(status: StateStatus.loading));
|
||||
var ids = state.areas
|
||||
.where((element) => element.selected)
|
||||
.map((e) => e.id)
|
||||
.toList();
|
||||
await getIt<WorkingAreaRepository>().saveWorkingArea(ids);
|
||||
emit(state.copyWith(status: StateStatus.success));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
part of 'working_area_bloc.dart';
|
||||
|
||||
@immutable
|
||||
sealed class WorkingAreaEvent {}
|
||||
|
||||
class InitializeWorkingAreaEvent extends WorkingAreaEvent {
|
||||
InitializeWorkingAreaEvent();
|
||||
}
|
||||
|
||||
class CheckAreaEvent extends WorkingAreaEvent {
|
||||
final String id;
|
||||
final bool selected;
|
||||
|
||||
CheckAreaEvent({required this.id, required this.selected});
|
||||
}
|
||||
|
||||
class SubmitWorkingAreaEvent extends WorkingAreaEvent {
|
||||
SubmitWorkingAreaEvent();
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
part of 'working_area_bloc.dart';
|
||||
|
||||
@immutable
|
||||
class WorkingAreaState {
|
||||
final StateStatus status;
|
||||
final List<AreaItemState> areas;
|
||||
|
||||
const WorkingAreaState({
|
||||
this.areas = const [],
|
||||
this.status = StateStatus.idle,
|
||||
});
|
||||
|
||||
WorkingAreaState copyWith({
|
||||
List<AreaItemState>? areas,
|
||||
StateStatus? status,
|
||||
}) {
|
||||
return WorkingAreaState(
|
||||
areas: areas ?? this.areas,
|
||||
status: status ?? this.status,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AreaItemState {
|
||||
final String id;
|
||||
final String name;
|
||||
final bool selected;
|
||||
|
||||
AreaItemState({required this.id, required this.name, this.selected = false});
|
||||
|
||||
AreaItemState copyWith({
|
||||
bool? selected,
|
||||
}) {
|
||||
return AreaItemState(
|
||||
id: id,
|
||||
name: name,
|
||||
selected: selected ?? this.selected,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:krow/core/data/models/staff/workin_area.dart';
|
||||
import 'package:krow/features/profile/working_area/data/working_area_repository.dart';
|
||||
import 'package:krow/features/profile/working_area/data/working_areas_api_provider.dart';
|
||||
|
||||
@Singleton(as: WorkingAreaRepository)
|
||||
class WorkingAreaRepositoryImpl implements WorkingAreaRepository {
|
||||
final WorkingAreasApiProvider _apiProvider;
|
||||
|
||||
WorkingAreaRepositoryImpl({required WorkingAreasApiProvider apiProvider})
|
||||
: _apiProvider = apiProvider;
|
||||
|
||||
@override
|
||||
Stream<List<WorkingArea>> getAllWorkingAreas() {
|
||||
return _apiProvider.getAllWorkingAreas();
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<List<WorkingArea>> getStaffWorkingAreas() {
|
||||
return _apiProvider.getStaffWorkingAreas();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> saveWorkingArea(List<String> ids) {
|
||||
return _apiProvider.putWorkingAreas(ids);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:krow/core/application/routing/routes.gr.dart';
|
||||
import 'package:krow/core/data/enums/state_status.dart';
|
||||
import 'package:krow/core/presentation/styles/kw_text_styles.dart';
|
||||
import 'package:krow/core/presentation/styles/theme.dart';
|
||||
import 'package:krow/core/presentation/widgets/scroll_layout_helper.dart';
|
||||
import 'package:krow/core/presentation/widgets/ui_kit/check_box.dart';
|
||||
import 'package:krow/core/presentation/widgets/ui_kit/check_box_card.dart';
|
||||
import 'package:krow/core/presentation/widgets/ui_kit/kw_app_bar.dart';
|
||||
import 'package:krow/core/presentation/widgets/ui_kit/kw_button.dart';
|
||||
import 'package:krow/core/presentation/widgets/ui_kit/kw_loading_overlay.dart';
|
||||
import 'package:krow/features/profile/working_area/domain/bloc/working_area_bloc.dart';
|
||||
|
||||
@RoutePage()
|
||||
class WorkingAreaScreen extends StatelessWidget implements AutoRouteWrapper {
|
||||
final bool isInEditMode;
|
||||
|
||||
const WorkingAreaScreen({super.key, this.isInEditMode = true});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: KwAppBar(
|
||||
showNotification: isInEditMode,
|
||||
titleText: 'working_area'.tr(),
|
||||
),
|
||||
body: BlocConsumer<WorkingAreaBloc, WorkingAreaState>(
|
||||
listenWhen: (previous, current) => previous.status != current.status,
|
||||
listener: (context, state) {
|
||||
if (state.status == StateStatus.success) {
|
||||
if (isInEditMode) {
|
||||
Navigator.pop(context);
|
||||
} else {
|
||||
context.router.push(
|
||||
RoleRoute(isInEditMode: false),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
builder: (context, state) {
|
||||
return KwLoadingOverlay(
|
||||
shouldShowLoading: state.status == StateStatus.loading,
|
||||
child: ScrollLayoutHelper(
|
||||
padding: isInEditMode? const EdgeInsets.symmetric(horizontal: 16):const EdgeInsets.all(16),
|
||||
upperWidget: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
if (isInEditMode)
|
||||
Text(
|
||||
'select_nearby'.tr(),
|
||||
style: AppTextStyles.bodySmallReg
|
||||
.copyWith(color: AppColors.blackGray),
|
||||
),
|
||||
if (!isInEditMode) ...[
|
||||
const Gap(20),
|
||||
Text(
|
||||
'where_u_can_work'.tr(),
|
||||
style: AppTextStyles.headingH1,
|
||||
),
|
||||
Text(
|
||||
'define_the_zone'.tr(),
|
||||
style: AppTextStyles.bodyMediumReg
|
||||
.copyWith(color: AppColors.blackGray),
|
||||
),
|
||||
],
|
||||
const Gap(24),
|
||||
...state.areas.map((item) => CheckBoxCard(
|
||||
checkBoxStyle: CheckBoxStyle.black,
|
||||
padding: const EdgeInsets.only(bottom: 8.0),
|
||||
isChecked: item.selected,
|
||||
title: item.name,
|
||||
onTap: () {
|
||||
context.read<WorkingAreaBloc>().add(CheckAreaEvent(
|
||||
id: item.id, selected: !item.selected));
|
||||
},
|
||||
)),
|
||||
const Gap(24),
|
||||
],
|
||||
),
|
||||
lowerWidget: KwButton.primary(
|
||||
disabled: state.areas.every((element) => !element.selected),
|
||||
label: isInEditMode ? 'save_changes'.tr() : 'save_and_continue'.tr(),
|
||||
onPressed: () {
|
||||
context
|
||||
.read<WorkingAreaBloc>()
|
||||
.add(SubmitWorkingAreaEvent());
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget wrappedRoute(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => WorkingAreaBloc()..add(InitializeWorkingAreaEvent()),
|
||||
child: this,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user