Merge remote-tracking branch 'origin/408-feature-implement-paidunpaid-breaks---client-app-frontend-development' into staff_recurring_permanent_order
This commit is contained in:
@@ -1129,15 +1129,18 @@
|
|||||||
"title": "Privacy & Security",
|
"title": "Privacy & Security",
|
||||||
"privacy_section": "Privacy",
|
"privacy_section": "Privacy",
|
||||||
"legal_section": "Legal",
|
"legal_section": "Legal",
|
||||||
"location_sharing": {
|
"profile_visibility": {
|
||||||
"title": "Location Sharing",
|
"title": "Profile Visibility",
|
||||||
"subtitle": "Share location during shifts"
|
"subtitle": "Let clients see your profile"
|
||||||
},
|
},
|
||||||
"terms_of_service": {
|
"terms_of_service": {
|
||||||
"title": "Terms of Service"
|
"title": "Terms of Service"
|
||||||
},
|
},
|
||||||
"privacy_policy": {
|
"privacy_policy": {
|
||||||
"title": "Privacy Policy"
|
"title": "Privacy Policy"
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"profile_visibility_updated": "Profile visibility updated successfully!"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"success": {
|
"success": {
|
||||||
|
|||||||
@@ -1129,15 +1129,18 @@
|
|||||||
"title": "Privacidad y Seguridad",
|
"title": "Privacidad y Seguridad",
|
||||||
"privacy_section": "Privacidad",
|
"privacy_section": "Privacidad",
|
||||||
"legal_section": "Legal",
|
"legal_section": "Legal",
|
||||||
"location_sharing": {
|
"profile_visibility": {
|
||||||
"title": "Compartir Ubicación",
|
"title": "Visibilidad del Perfil",
|
||||||
"subtitle": "Compartir ubicación durante turnos"
|
"subtitle": "Deja que los clientes vean tu perfil"
|
||||||
},
|
},
|
||||||
"terms_of_service": {
|
"terms_of_service": {
|
||||||
"title": "Términos de Servicio"
|
"title": "Términos de Servicio"
|
||||||
},
|
},
|
||||||
"privacy_policy": {
|
"privacy_policy": {
|
||||||
"title": "Política de Privacidad"
|
"title": "Política de Privacidad"
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"profile_visibility_updated": "¡Visibilidad del perfil actualizada exitosamente!"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"success": {
|
"success": {
|
||||||
|
|||||||
@@ -1,79 +1,92 @@
|
|||||||
|
import 'package:firebase_data_connect/firebase_data_connect.dart' as fdc;
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:krow_data_connect/krow_data_connect.dart';
|
import 'package:krow_data_connect/krow_data_connect.dart';
|
||||||
|
|
||||||
import '../../domain/entities/privacy_settings_entity.dart';
|
|
||||||
import '../../domain/repositories/privacy_settings_repository_interface.dart';
|
import '../../domain/repositories/privacy_settings_repository_interface.dart';
|
||||||
|
|
||||||
/// Data layer implementation of privacy settings repository
|
/// Data layer implementation of privacy settings repository
|
||||||
///
|
///
|
||||||
/// Handles all backend communication for privacy settings,
|
/// Handles all backend communication for privacy settings via Data Connect,
|
||||||
/// using DataConnectService for automatic auth and token refresh,
|
|
||||||
/// and loads legal documents from app assets
|
/// and loads legal documents from app assets
|
||||||
class PrivacySettingsRepositoryImpl
|
class PrivacySettingsRepositoryImpl
|
||||||
implements PrivacySettingsRepositoryInterface {
|
implements PrivacySettingsRepositoryInterface {
|
||||||
final DataConnectService _service;
|
|
||||||
|
|
||||||
PrivacySettingsRepositoryImpl(this._service);
|
PrivacySettingsRepositoryImpl(this._service);
|
||||||
|
|
||||||
|
final DataConnectService _service;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<PrivacySettingsEntity> getPrivacySettings() async {
|
Future<bool> getProfileVisibility() async {
|
||||||
return _service.run<PrivacySettingsEntity>(
|
return _service.run<bool>(() async {
|
||||||
() async {
|
// Get current user ID
|
||||||
// TODO: Call Data Connect query to fetch privacy settings
|
final String staffId = await _service.getStaffId();
|
||||||
// For now, return default settings
|
|
||||||
return PrivacySettingsEntity(
|
// Call Data Connect query: getStaffProfileVisibility
|
||||||
locationSharing: true,
|
final fdc.QueryResult<
|
||||||
updatedAt: DateTime.now(),
|
GetStaffProfileVisibilityData,
|
||||||
);
|
GetStaffProfileVisibilityVariables
|
||||||
},
|
>
|
||||||
);
|
response = await _service.connector
|
||||||
|
.getStaffProfileVisibility(staffId: staffId)
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
// Return the profile visibility status from the first result
|
||||||
|
if (response.data.staff != null) {
|
||||||
|
return response.data.staff?.isProfileVisible ?? true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to visible if no staff record found
|
||||||
|
return true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<PrivacySettingsEntity> updateLocationSharing(bool enabled) async {
|
Future<bool> updateProfileVisibility(bool isVisible) async {
|
||||||
return _service.run<PrivacySettingsEntity>(
|
return _service.run<bool>(() async {
|
||||||
() async {
|
// Get staff ID for the current user
|
||||||
// TODO: Call Data Connect mutation to update location sharing preference
|
final String staffId = await _service.getStaffId();
|
||||||
// For now, return updated settings
|
|
||||||
return PrivacySettingsEntity(
|
// Call Data Connect mutation: UpdateStaffProfileVisibility
|
||||||
locationSharing: enabled,
|
await _service.connector
|
||||||
updatedAt: DateTime.now(),
|
.updateStaffProfileVisibility(
|
||||||
);
|
id: staffId,
|
||||||
},
|
isProfileVisible: isVisible,
|
||||||
);
|
)
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
// Return the requested visibility state
|
||||||
|
return isVisible;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> getTermsOfService() async {
|
Future<String> getTermsOfService() async {
|
||||||
return _service.run<String>(
|
return _service.run<String>(() async {
|
||||||
() async {
|
try {
|
||||||
try {
|
// Load from package asset path
|
||||||
// Load from package asset path
|
return await rootBundle.loadString(
|
||||||
return await rootBundle.loadString(
|
'packages/staff_privacy_security/lib/src/assets/legal/terms_of_service.txt',
|
||||||
'packages/staff_privacy_security/lib/src/assets/legal/terms_of_service.txt',
|
);
|
||||||
);
|
} catch (e) {
|
||||||
} catch (e) {
|
// Final fallback if asset not found
|
||||||
// Final fallback if asset not found
|
print('Error loading terms of service: $e');
|
||||||
return 'Terms of Service - Content unavailable. Please contact support@krow.com';
|
return 'Terms of Service - Content unavailable. Please contact support@krow.com';
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> getPrivacyPolicy() async {
|
Future<String> getPrivacyPolicy() async {
|
||||||
return _service.run<String>(
|
return _service.run<String>(() async {
|
||||||
() async {
|
try {
|
||||||
try {
|
// Load from package asset path
|
||||||
// Load from package asset path
|
return await rootBundle.loadString(
|
||||||
return await rootBundle.loadString(
|
'packages/staff_privacy_security/lib/src/assets/legal/privacy_policy.txt',
|
||||||
'packages/staff_privacy_security/lib/src/assets/legal/privacy_policy.txt',
|
);
|
||||||
);
|
} catch (e) {
|
||||||
} catch (e) {
|
// Final fallback if asset not found
|
||||||
// Final fallback if asset not found
|
print('Error loading privacy policy: $e');
|
||||||
return 'Privacy Policy - Content unavailable. Please contact privacy@krow.com';
|
return 'Privacy Policy - Content unavailable. Please contact privacy@krow.com';
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import '../entities/privacy_settings_entity.dart';
|
|
||||||
|
|
||||||
/// Interface for privacy settings repository operations
|
/// Interface for privacy settings repository operations
|
||||||
abstract class PrivacySettingsRepositoryInterface {
|
abstract class PrivacySettingsRepositoryInterface {
|
||||||
/// Fetch the current user's privacy settings
|
/// Fetch the current staff member's profile visibility setting
|
||||||
Future<PrivacySettingsEntity> getPrivacySettings();
|
Future<bool> getProfileVisibility();
|
||||||
|
|
||||||
/// Update location sharing preference
|
/// Update profile visibility preference
|
||||||
///
|
///
|
||||||
/// Returns the updated privacy settings
|
/// Returns the updated profile visibility status
|
||||||
Future<PrivacySettingsEntity> updateLocationSharing(bool enabled);
|
Future<bool> updateProfileVisibility(bool isVisible);
|
||||||
|
|
||||||
/// Fetch terms of service content
|
/// Fetch terms of service content
|
||||||
Future<String> getTermsOfService();
|
Future<String> getTermsOfService();
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
import '../entities/privacy_settings_entity.dart';
|
|
||||||
import '../repositories/privacy_settings_repository_interface.dart';
|
|
||||||
|
|
||||||
/// Use case to retrieve the current user's privacy settings
|
|
||||||
class GetPrivacySettingsUseCase {
|
|
||||||
final PrivacySettingsRepositoryInterface _repository;
|
|
||||||
|
|
||||||
GetPrivacySettingsUseCase(this._repository);
|
|
||||||
|
|
||||||
/// Execute the use case to get privacy settings
|
|
||||||
Future<PrivacySettingsEntity> call() async {
|
|
||||||
try {
|
|
||||||
return await _repository.getPrivacySettings();
|
|
||||||
} catch (e) {
|
|
||||||
// Return default settings on error
|
|
||||||
return PrivacySettingsEntity(
|
|
||||||
locationSharing: true,
|
|
||||||
updatedAt: DateTime.now(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import '../repositories/privacy_settings_repository_interface.dart';
|
||||||
|
|
||||||
|
/// Use case to retrieve the current staff member's profile visibility setting
|
||||||
|
class GetProfileVisibilityUseCase {
|
||||||
|
final PrivacySettingsRepositoryInterface _repository;
|
||||||
|
|
||||||
|
GetProfileVisibilityUseCase(this._repository);
|
||||||
|
|
||||||
|
/// Execute the use case to get profile visibility status
|
||||||
|
/// Returns true if profile is visible, false if hidden
|
||||||
|
Future<bool> call() async {
|
||||||
|
try {
|
||||||
|
return await _repository.getProfileVisibility();
|
||||||
|
} catch (e) {
|
||||||
|
// Return default (visible) on error
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
|
||||||
|
|
||||||
import '../entities/privacy_settings_entity.dart';
|
|
||||||
import '../repositories/privacy_settings_repository_interface.dart';
|
|
||||||
|
|
||||||
/// Parameters for updating location sharing
|
|
||||||
class UpdateLocationSharingParams extends Equatable {
|
|
||||||
/// Whether to enable or disable location sharing
|
|
||||||
final bool enabled;
|
|
||||||
|
|
||||||
const UpdateLocationSharingParams({required this.enabled});
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [enabled];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Use case to update location sharing preference
|
|
||||||
class UpdateLocationSharingUseCase {
|
|
||||||
final PrivacySettingsRepositoryInterface _repository;
|
|
||||||
|
|
||||||
UpdateLocationSharingUseCase(this._repository);
|
|
||||||
|
|
||||||
/// Execute the use case to update location sharing
|
|
||||||
Future<PrivacySettingsEntity> call(UpdateLocationSharingParams params) async {
|
|
||||||
try {
|
|
||||||
return await _repository.updateLocationSharing(params.enabled);
|
|
||||||
} catch (e) {
|
|
||||||
// Return current settings on error
|
|
||||||
return PrivacySettingsEntity(
|
|
||||||
locationSharing: params.enabled,
|
|
||||||
updatedAt: DateTime.now(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
|
||||||
|
import '../repositories/privacy_settings_repository_interface.dart';
|
||||||
|
|
||||||
|
/// Parameters for updating profile visibility
|
||||||
|
class UpdateProfileVisibilityParams extends Equatable {
|
||||||
|
/// Whether to show (true) or hide (false) the profile
|
||||||
|
final bool isVisible;
|
||||||
|
|
||||||
|
const UpdateProfileVisibilityParams({required this.isVisible});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => <Object?>[isVisible];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Use case to update profile visibility setting
|
||||||
|
class UpdateProfileVisibilityUseCase {
|
||||||
|
final PrivacySettingsRepositoryInterface _repository;
|
||||||
|
|
||||||
|
UpdateProfileVisibilityUseCase(this._repository);
|
||||||
|
|
||||||
|
/// Execute the use case to update profile visibility
|
||||||
|
/// Returns the updated visibility status
|
||||||
|
Future<bool> call(UpdateProfileVisibilityParams params) async {
|
||||||
|
try {
|
||||||
|
return await _repository.updateProfileVisibility(params.isVisible);
|
||||||
|
} catch (e) {
|
||||||
|
// Return the requested state on error (optimistic)
|
||||||
|
return params.isVisible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
|
|
||||||
import '../../domain/entities/privacy_settings_entity.dart';
|
import '../../domain/usecases/get_profile_visibility_usecase.dart';
|
||||||
import '../../domain/repositories/privacy_settings_repository_interface.dart';
|
import '../../domain/usecases/update_profile_visibility_usecase.dart';
|
||||||
import '../../domain/usecases/get_privacy_settings_usecase.dart';
|
|
||||||
import '../../domain/usecases/update_location_sharing_usecase.dart';
|
|
||||||
import '../../domain/usecases/get_terms_usecase.dart';
|
import '../../domain/usecases/get_terms_usecase.dart';
|
||||||
import '../../domain/usecases/get_privacy_policy_usecase.dart';
|
import '../../domain/usecases/get_privacy_policy_usecase.dart';
|
||||||
|
|
||||||
@@ -14,72 +12,75 @@ part 'privacy_security_state.dart';
|
|||||||
/// BLoC managing privacy and security settings state
|
/// BLoC managing privacy and security settings state
|
||||||
class PrivacySecurityBloc
|
class PrivacySecurityBloc
|
||||||
extends Bloc<PrivacySecurityEvent, PrivacySecurityState> {
|
extends Bloc<PrivacySecurityEvent, PrivacySecurityState> {
|
||||||
final GetPrivacySettingsUseCase _getPrivacySettingsUseCase;
|
final GetProfileVisibilityUseCase _getProfileVisibilityUseCase;
|
||||||
final UpdateLocationSharingUseCase _updateLocationSharingUseCase;
|
final UpdateProfileVisibilityUseCase _updateProfileVisibilityUseCase;
|
||||||
final GetTermsUseCase _getTermsUseCase;
|
final GetTermsUseCase _getTermsUseCase;
|
||||||
final GetPrivacyPolicyUseCase _getPrivacyPolicyUseCase;
|
final GetPrivacyPolicyUseCase _getPrivacyPolicyUseCase;
|
||||||
|
|
||||||
PrivacySecurityBloc({
|
PrivacySecurityBloc({
|
||||||
required GetPrivacySettingsUseCase getPrivacySettingsUseCase,
|
required GetProfileVisibilityUseCase getProfileVisibilityUseCase,
|
||||||
required UpdateLocationSharingUseCase updateLocationSharingUseCase,
|
required UpdateProfileVisibilityUseCase updateProfileVisibilityUseCase,
|
||||||
required GetTermsUseCase getTermsUseCase,
|
required GetTermsUseCase getTermsUseCase,
|
||||||
required GetPrivacyPolicyUseCase getPrivacyPolicyUseCase,
|
required GetPrivacyPolicyUseCase getPrivacyPolicyUseCase,
|
||||||
}) : _getPrivacySettingsUseCase = getPrivacySettingsUseCase,
|
}) : _getProfileVisibilityUseCase = getProfileVisibilityUseCase,
|
||||||
_updateLocationSharingUseCase = updateLocationSharingUseCase,
|
_updateProfileVisibilityUseCase = updateProfileVisibilityUseCase,
|
||||||
_getTermsUseCase = getTermsUseCase,
|
_getTermsUseCase = getTermsUseCase,
|
||||||
_getPrivacyPolicyUseCase = getPrivacyPolicyUseCase,
|
_getPrivacyPolicyUseCase = getPrivacyPolicyUseCase,
|
||||||
super(const PrivacySecurityState()) {
|
super(const PrivacySecurityState()) {
|
||||||
on<FetchPrivacySettingsEvent>(_onFetchPrivacySettings);
|
on<FetchProfileVisibilityEvent>(_onFetchProfileVisibility);
|
||||||
on<UpdateLocationSharingEvent>(_onUpdateLocationSharing);
|
on<UpdateProfileVisibilityEvent>(_onUpdateProfileVisibility);
|
||||||
on<FetchTermsEvent>(_onFetchTerms);
|
on<FetchTermsEvent>(_onFetchTerms);
|
||||||
on<FetchPrivacyPolicyEvent>(_onFetchPrivacyPolicy);
|
on<FetchPrivacyPolicyEvent>(_onFetchPrivacyPolicy);
|
||||||
|
on<ClearProfileVisibilityUpdatedEvent>(_onClearProfileVisibilityUpdated);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onFetchPrivacySettings(
|
Future<void> _onFetchProfileVisibility(
|
||||||
FetchPrivacySettingsEvent event,
|
FetchProfileVisibilityEvent event,
|
||||||
Emitter<PrivacySecurityState> emit,
|
Emitter<PrivacySecurityState> emit,
|
||||||
) async {
|
) async {
|
||||||
emit(state.copyWith(isLoading: true, error: null));
|
emit(state.copyWith(isLoading: true, error: null));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final settings = await _getPrivacySettingsUseCase.call();
|
final bool isVisible = await _getProfileVisibilityUseCase.call();
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
privacySettings: settings,
|
isProfileVisible: isVisible,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
error: 'Failed to fetch privacy settings',
|
error: 'Failed to fetch profile visibility',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onUpdateLocationSharing(
|
Future<void> _onUpdateProfileVisibility(
|
||||||
UpdateLocationSharingEvent event,
|
UpdateProfileVisibilityEvent event,
|
||||||
Emitter<PrivacySecurityState> emit,
|
Emitter<PrivacySecurityState> emit,
|
||||||
) async {
|
) async {
|
||||||
emit(state.copyWith(isUpdating: true, error: null));
|
emit(state.copyWith(isUpdating: true, error: null, profileVisibilityUpdated: false));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final settings = await _updateLocationSharingUseCase.call(
|
final bool isVisible = await _updateProfileVisibilityUseCase.call(
|
||||||
UpdateLocationSharingParams(enabled: event.enabled),
|
UpdateProfileVisibilityParams(isVisible: event.isVisible),
|
||||||
);
|
);
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
isUpdating: false,
|
isUpdating: false,
|
||||||
privacySettings: settings,
|
isProfileVisible: isVisible,
|
||||||
|
profileVisibilityUpdated: true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
isUpdating: false,
|
isUpdating: false,
|
||||||
error: 'Failed to update location sharing',
|
error: 'Failed to update profile visibility',
|
||||||
|
profileVisibilityUpdated: false,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -92,7 +93,7 @@ class PrivacySecurityBloc
|
|||||||
emit(state.copyWith(isLoadingTerms: true, error: null));
|
emit(state.copyWith(isLoadingTerms: true, error: null));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final content = await _getTermsUseCase.call();
|
final String content = await _getTermsUseCase.call();
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
isLoadingTerms: false,
|
isLoadingTerms: false,
|
||||||
@@ -116,7 +117,7 @@ class PrivacySecurityBloc
|
|||||||
emit(state.copyWith(isLoadingPrivacyPolicy: true, error: null));
|
emit(state.copyWith(isLoadingPrivacyPolicy: true, error: null));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final content = await _getPrivacyPolicyUseCase.call();
|
final String content = await _getPrivacyPolicyUseCase.call();
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
isLoadingPrivacyPolicy: false,
|
isLoadingPrivacyPolicy: false,
|
||||||
@@ -132,4 +133,11 @@ class PrivacySecurityBloc
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onClearProfileVisibilityUpdated(
|
||||||
|
ClearProfileVisibilityUpdatedEvent event,
|
||||||
|
Emitter<PrivacySecurityState> emit,
|
||||||
|
) {
|
||||||
|
emit(state.copyWith(profileVisibilityUpdated: false));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,23 +5,23 @@ abstract class PrivacySecurityEvent extends Equatable {
|
|||||||
const PrivacySecurityEvent();
|
const PrivacySecurityEvent();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [];
|
List<Object?> get props => <Object?>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Event to fetch current privacy settings
|
/// Event to fetch current profile visibility setting
|
||||||
class FetchPrivacySettingsEvent extends PrivacySecurityEvent {
|
class FetchProfileVisibilityEvent extends PrivacySecurityEvent {
|
||||||
const FetchPrivacySettingsEvent();
|
const FetchProfileVisibilityEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Event to update location sharing preference
|
/// Event to update profile visibility
|
||||||
class UpdateLocationSharingEvent extends PrivacySecurityEvent {
|
class UpdateProfileVisibilityEvent extends PrivacySecurityEvent {
|
||||||
/// Whether to enable or disable location sharing
|
/// Whether to show (true) or hide (false) the profile
|
||||||
final bool enabled;
|
final bool isVisible;
|
||||||
|
|
||||||
const UpdateLocationSharingEvent({required this.enabled});
|
const UpdateProfileVisibilityEvent({required this.isVisible});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [enabled];
|
List<Object?> get props => <Object?>[isVisible];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Event to fetch terms of service
|
/// Event to fetch terms of service
|
||||||
@@ -33,3 +33,8 @@ class FetchTermsEvent extends PrivacySecurityEvent {
|
|||||||
class FetchPrivacyPolicyEvent extends PrivacySecurityEvent {
|
class FetchPrivacyPolicyEvent extends PrivacySecurityEvent {
|
||||||
const FetchPrivacyPolicyEvent();
|
const FetchPrivacyPolicyEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Event to clear the profile visibility updated flag after showing snackbar
|
||||||
|
class ClearProfileVisibilityUpdatedEvent extends PrivacySecurityEvent {
|
||||||
|
const ClearProfileVisibilityUpdatedEvent();
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,15 +2,18 @@ part of 'privacy_security_bloc.dart';
|
|||||||
|
|
||||||
/// State for privacy security BLoC
|
/// State for privacy security BLoC
|
||||||
class PrivacySecurityState extends Equatable {
|
class PrivacySecurityState extends Equatable {
|
||||||
/// Current privacy settings
|
/// Current profile visibility setting (true = visible, false = hidden)
|
||||||
final PrivacySettingsEntity? privacySettings;
|
final bool isProfileVisible;
|
||||||
|
|
||||||
/// Whether settings are currently loading
|
/// Whether profile visibility is currently loading
|
||||||
final bool isLoading;
|
final bool isLoading;
|
||||||
|
|
||||||
/// Whether settings are currently being updated
|
/// Whether profile visibility is currently being updated
|
||||||
final bool isUpdating;
|
final bool isUpdating;
|
||||||
|
|
||||||
|
/// Whether the profile visibility was just successfully updated
|
||||||
|
final bool profileVisibilityUpdated;
|
||||||
|
|
||||||
/// Terms of service content
|
/// Terms of service content
|
||||||
final String? termsContent;
|
final String? termsContent;
|
||||||
|
|
||||||
@@ -27,9 +30,10 @@ class PrivacySecurityState extends Equatable {
|
|||||||
final String? error;
|
final String? error;
|
||||||
|
|
||||||
const PrivacySecurityState({
|
const PrivacySecurityState({
|
||||||
this.privacySettings,
|
this.isProfileVisible = true,
|
||||||
this.isLoading = false,
|
this.isLoading = false,
|
||||||
this.isUpdating = false,
|
this.isUpdating = false,
|
||||||
|
this.profileVisibilityUpdated = false,
|
||||||
this.termsContent,
|
this.termsContent,
|
||||||
this.isLoadingTerms = false,
|
this.isLoadingTerms = false,
|
||||||
this.privacyPolicyContent,
|
this.privacyPolicyContent,
|
||||||
@@ -39,9 +43,10 @@ class PrivacySecurityState extends Equatable {
|
|||||||
|
|
||||||
/// Create a copy with optional field overrides
|
/// Create a copy with optional field overrides
|
||||||
PrivacySecurityState copyWith({
|
PrivacySecurityState copyWith({
|
||||||
PrivacySettingsEntity? privacySettings,
|
bool? isProfileVisible,
|
||||||
bool? isLoading,
|
bool? isLoading,
|
||||||
bool? isUpdating,
|
bool? isUpdating,
|
||||||
|
bool? profileVisibilityUpdated,
|
||||||
String? termsContent,
|
String? termsContent,
|
||||||
bool? isLoadingTerms,
|
bool? isLoadingTerms,
|
||||||
String? privacyPolicyContent,
|
String? privacyPolicyContent,
|
||||||
@@ -49,9 +54,10 @@ class PrivacySecurityState extends Equatable {
|
|||||||
String? error,
|
String? error,
|
||||||
}) {
|
}) {
|
||||||
return PrivacySecurityState(
|
return PrivacySecurityState(
|
||||||
privacySettings: privacySettings ?? this.privacySettings,
|
isProfileVisible: isProfileVisible ?? this.isProfileVisible,
|
||||||
isLoading: isLoading ?? this.isLoading,
|
isLoading: isLoading ?? this.isLoading,
|
||||||
isUpdating: isUpdating ?? this.isUpdating,
|
isUpdating: isUpdating ?? this.isUpdating,
|
||||||
|
profileVisibilityUpdated: profileVisibilityUpdated ?? this.profileVisibilityUpdated,
|
||||||
termsContent: termsContent ?? this.termsContent,
|
termsContent: termsContent ?? this.termsContent,
|
||||||
isLoadingTerms: isLoadingTerms ?? this.isLoadingTerms,
|
isLoadingTerms: isLoadingTerms ?? this.isLoadingTerms,
|
||||||
privacyPolicyContent: privacyPolicyContent ?? this.privacyPolicyContent,
|
privacyPolicyContent: privacyPolicyContent ?? this.privacyPolicyContent,
|
||||||
@@ -62,10 +68,11 @@ class PrivacySecurityState extends Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => <Object?>[
|
||||||
privacySettings,
|
isProfileVisible,
|
||||||
isLoading,
|
isLoading,
|
||||||
isUpdating,
|
isUpdating,
|
||||||
|
profileVisibilityUpdated,
|
||||||
termsContent,
|
termsContent,
|
||||||
isLoadingTerms,
|
isLoadingTerms,
|
||||||
privacyPolicyContent,
|
privacyPolicyContent,
|
||||||
@@ -73,3 +80,4 @@ class PrivacySecurityState extends Equatable {
|
|||||||
error,
|
error,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class PrivacySecurityPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
body: BlocProvider<PrivacySecurityBloc>.value(
|
body: BlocProvider<PrivacySecurityBloc>.value(
|
||||||
value: Modular.get<PrivacySecurityBloc>()
|
value: Modular.get<PrivacySecurityBloc>()
|
||||||
..add(const FetchPrivacySettingsEvent()),
|
..add(const FetchProfileVisibilityEvent()),
|
||||||
child: BlocBuilder<PrivacySecurityBloc, PrivacySecurityState>(
|
child: BlocBuilder<PrivacySecurityBloc, PrivacySecurityState>(
|
||||||
builder: (BuildContext context, PrivacySecurityState state) {
|
builder: (BuildContext context, PrivacySecurityState state) {
|
||||||
if (state.isLoading) {
|
if (state.isLoading) {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class LegalSectionWidget extends StatelessWidget {
|
|||||||
// Legal Section Header
|
// Legal Section Header
|
||||||
SettingsSectionHeader(
|
SettingsSectionHeader(
|
||||||
title: t.staff_privacy_security.legal_section,
|
title: t.staff_privacy_security.legal_section,
|
||||||
icon: Icons.shield,
|
icon: UiIcons.shield,
|
||||||
),
|
),
|
||||||
|
|
||||||
Container(
|
Container(
|
||||||
|
|||||||
@@ -7,48 +7,64 @@ import '../../blocs/privacy_security_bloc.dart';
|
|||||||
import '../settings_section_header_widget.dart';
|
import '../settings_section_header_widget.dart';
|
||||||
import '../settings_switch_tile_widget.dart';
|
import '../settings_switch_tile_widget.dart';
|
||||||
|
|
||||||
/// Widget displaying privacy settings including location sharing preference
|
/// Widget displaying privacy settings including profile visibility preference
|
||||||
class PrivacySectionWidget extends StatelessWidget {
|
class PrivacySectionWidget extends StatelessWidget {
|
||||||
const PrivacySectionWidget({super.key});
|
const PrivacySectionWidget({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<PrivacySecurityBloc, PrivacySecurityState>(
|
return BlocListener<PrivacySecurityBloc, PrivacySecurityState>(
|
||||||
builder: (BuildContext context, PrivacySecurityState state) {
|
listener: (BuildContext context, PrivacySecurityState state) {
|
||||||
return Column(
|
// Show success message when profile visibility update just completed
|
||||||
children: <Widget>[
|
if (state.profileVisibilityUpdated && state.error == null) {
|
||||||
// Privacy Section Header
|
UiSnackbar.show(
|
||||||
SettingsSectionHeader(
|
context,
|
||||||
title: t.staff_privacy_security.privacy_section,
|
message: t.staff_privacy_security.success.profile_visibility_updated,
|
||||||
icon: UiIcons.eye,
|
type: UiSnackbarType.success,
|
||||||
),
|
);
|
||||||
const SizedBox(height: 12.0),
|
// Clear the flag after showing the snackbar
|
||||||
Container(
|
context.read<PrivacySecurityBloc>().add(
|
||||||
decoration: BoxDecoration(
|
const ClearProfileVisibilityUpdatedEvent(),
|
||||||
color: Colors.white,
|
);
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
}
|
||||||
border: Border.all(
|
},
|
||||||
color: UiColors.border,
|
child: BlocBuilder<PrivacySecurityBloc, PrivacySecurityState>(
|
||||||
|
builder: (BuildContext context, PrivacySecurityState state) {
|
||||||
|
return Column(
|
||||||
|
children: <Widget>[
|
||||||
|
// Privacy Section Header
|
||||||
|
SettingsSectionHeader(
|
||||||
|
title: t.staff_privacy_security.privacy_section,
|
||||||
|
icon: UiIcons.eye,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12.0),
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
|
border: Border.all(
|
||||||
|
color: UiColors.border,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
SettingsSwitchTile(
|
||||||
|
title: t.staff_privacy_security.profile_visibility.title,
|
||||||
|
subtitle: t.staff_privacy_security.profile_visibility.subtitle,
|
||||||
|
value: state.isProfileVisible,
|
||||||
|
onChanged: (bool value) {
|
||||||
|
BlocProvider.of<PrivacySecurityBloc>(context).add(
|
||||||
|
UpdateProfileVisibilityEvent(isVisible: value),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Column(
|
],
|
||||||
children: <Widget>[
|
);
|
||||||
SettingsSwitchTile(
|
},
|
||||||
title: t.staff_privacy_security.location_sharing.title,
|
),
|
||||||
subtitle: t.staff_privacy_security.location_sharing.subtitle,
|
|
||||||
value: state.privacySettings?.locationSharing ?? false,
|
|
||||||
onChanged: (bool value) {
|
|
||||||
BlocProvider.of<PrivacySecurityBloc>(context).add(
|
|
||||||
UpdateLocationSharingEvent(enabled: value),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import 'package:krow_data_connect/krow_data_connect.dart';
|
|||||||
import 'data/repositories_impl/privacy_settings_repository_impl.dart';
|
import 'data/repositories_impl/privacy_settings_repository_impl.dart';
|
||||||
import 'domain/repositories/privacy_settings_repository_interface.dart';
|
import 'domain/repositories/privacy_settings_repository_interface.dart';
|
||||||
import 'domain/usecases/get_privacy_policy_usecase.dart';
|
import 'domain/usecases/get_privacy_policy_usecase.dart';
|
||||||
import 'domain/usecases/get_privacy_settings_usecase.dart';
|
import 'domain/usecases/get_profile_visibility_usecase.dart';
|
||||||
import 'domain/usecases/get_terms_usecase.dart';
|
import 'domain/usecases/get_terms_usecase.dart';
|
||||||
import 'domain/usecases/update_location_sharing_usecase.dart';
|
import 'domain/usecases/update_profile_visibility_usecase.dart';
|
||||||
import 'presentation/blocs/legal/privacy_policy_cubit.dart';
|
import 'presentation/blocs/legal/privacy_policy_cubit.dart';
|
||||||
import 'presentation/blocs/legal/terms_cubit.dart';
|
import 'presentation/blocs/legal/terms_cubit.dart';
|
||||||
import 'presentation/blocs/privacy_security_bloc.dart';
|
import 'presentation/blocs/privacy_security_bloc.dart';
|
||||||
@@ -33,12 +33,12 @@ class PrivacySecurityModule extends Module {
|
|||||||
|
|
||||||
// Use Cases
|
// Use Cases
|
||||||
i.addSingleton(
|
i.addSingleton(
|
||||||
() => GetPrivacySettingsUseCase(
|
() => GetProfileVisibilityUseCase(
|
||||||
i<PrivacySettingsRepositoryInterface>(),
|
i<PrivacySettingsRepositoryInterface>(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
i.addSingleton(
|
i.addSingleton(
|
||||||
() => UpdateLocationSharingUseCase(
|
() => UpdateProfileVisibilityUseCase(
|
||||||
i<PrivacySettingsRepositoryInterface>(),
|
i<PrivacySettingsRepositoryInterface>(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -56,8 +56,8 @@ class PrivacySecurityModule extends Module {
|
|||||||
// BLoC
|
// BLoC
|
||||||
i.add(
|
i.add(
|
||||||
() => PrivacySecurityBloc(
|
() => PrivacySecurityBloc(
|
||||||
getPrivacySettingsUseCase: i(),
|
getProfileVisibilityUseCase: i(),
|
||||||
updateLocationSharingUseCase: i(),
|
updateProfileVisibilityUseCase: i(),
|
||||||
getTermsUseCase: i(),
|
getTermsUseCase: i(),
|
||||||
getPrivacyPolicyUseCase: i(),
|
getPrivacyPolicyUseCase: i(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
export 'src/domain/entities/privacy_settings_entity.dart';
|
export 'src/domain/entities/privacy_settings_entity.dart';
|
||||||
export 'src/domain/repositories/privacy_settings_repository_interface.dart';
|
export 'src/domain/repositories/privacy_settings_repository_interface.dart';
|
||||||
export 'src/domain/usecases/get_privacy_settings_usecase.dart';
|
|
||||||
export 'src/domain/usecases/update_location_sharing_usecase.dart';
|
|
||||||
export 'src/domain/usecases/get_terms_usecase.dart';
|
export 'src/domain/usecases/get_terms_usecase.dart';
|
||||||
export 'src/domain/usecases/get_privacy_policy_usecase.dart';
|
export 'src/domain/usecases/get_privacy_policy_usecase.dart';
|
||||||
export 'src/data/repositories_impl/privacy_settings_repository_impl.dart';
|
export 'src/data/repositories_impl/privacy_settings_repository_impl.dart';
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ class ShiftsRepositoryImpl
|
|||||||
// Cache: ApplicationID -> RoleID (For Accept/Decline w/ Update mutation)
|
// Cache: ApplicationID -> RoleID (For Accept/Decline w/ Update mutation)
|
||||||
final Map<String, String> _appToRoleIdMap = {};
|
final Map<String, String> _appToRoleIdMap = {};
|
||||||
|
|
||||||
|
// This need to be an APPLICATION
|
||||||
|
// THERE SHOULD BE APPLICATIONSTATUS and SHIFTSTATUS enums in the domain layer to avoid this string mapping and potential bugs.
|
||||||
@override
|
@override
|
||||||
Future<List<Shift>> getMyShifts({
|
Future<List<Shift>> getMyShifts({
|
||||||
required DateTime start,
|
required DateTime start,
|
||||||
|
|||||||
@@ -214,3 +214,12 @@ mutation UpdateStaff(
|
|||||||
mutation DeleteStaff($id: UUID!) @auth(level: USER) {
|
mutation DeleteStaff($id: UUID!) @auth(level: USER) {
|
||||||
staff_delete(id: $id)
|
staff_delete(id: $id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutation UpdateStaffProfileVisibility($id: UUID!, $isProfileVisible: Boolean!) @auth(level: USER) {
|
||||||
|
staff_update(
|
||||||
|
id: $id
|
||||||
|
data: {
|
||||||
|
isProfileVisible: $isProfileVisible
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -204,3 +204,10 @@ query filterStaff(
|
|||||||
zipCode
|
zipCode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
query getStaffProfileVisibility($staffId: UUID!) @auth(level: USER) {
|
||||||
|
staff(id: $staffId) {
|
||||||
|
id
|
||||||
|
isProfileVisible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user