full stack second commit
This commit is contained in:
@@ -26,41 +26,40 @@ class DocumentViewerBloc
|
||||
event.orgId,
|
||||
event.fileId,
|
||||
);
|
||||
if (session.expiresAt.isBefore(DateTime.now())) {
|
||||
emit(DocumentViewerSessionExpired());
|
||||
return;
|
||||
}
|
||||
emit(
|
||||
DocumentViewerReady(
|
||||
viewUrl: session.viewUrl,
|
||||
caps: session.capabilities,
|
||||
),
|
||||
);
|
||||
if (session.expiresAt.isAfter(DateTime.now())) {
|
||||
_expiryTimer = Timer(
|
||||
session.expiresAt.difference(DateTime.now()),
|
||||
() => emit(DocumentViewerSessionExpired()),
|
||||
);
|
||||
}
|
||||
_expiryTimer = Timer(
|
||||
session.expiresAt.difference(DateTime.now()),
|
||||
() => emit(DocumentViewerSessionExpired()),
|
||||
);
|
||||
} catch (e) {
|
||||
if (e is ApiError) {
|
||||
switch (e.code) {
|
||||
case 'unauthorized':
|
||||
case 'UNAUTHENTICATED':
|
||||
// Already handled by ApiClient
|
||||
break;
|
||||
case 'permission_denied':
|
||||
case 'PERMISSION_DENIED':
|
||||
emit(
|
||||
DocumentViewerError(
|
||||
message: 'You don\'t have access to this document.',
|
||||
),
|
||||
);
|
||||
break;
|
||||
case 'not_found':
|
||||
case 'NOT_FOUND':
|
||||
emit(
|
||||
DocumentViewerError(
|
||||
message: 'This document no longer exists or was moved.',
|
||||
),
|
||||
);
|
||||
break;
|
||||
case 'not_found':
|
||||
emit(DocumentViewerError(message: 'Document not found'));
|
||||
break;
|
||||
default:
|
||||
emit(
|
||||
DocumentViewerError(
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:bloc/bloc.dart';
|
||||
import 'editor_session_event.dart';
|
||||
import 'editor_session_state.dart';
|
||||
import '../../services/file_service.dart';
|
||||
import '../../models/api_error.dart';
|
||||
|
||||
class EditorSessionBloc extends Bloc<EditorSessionEvent, EditorSessionState> {
|
||||
final FileService _fileService;
|
||||
@@ -15,6 +16,27 @@ class EditorSessionBloc extends Bloc<EditorSessionEvent, EditorSessionState> {
|
||||
on<EditorSessionEnded>(_onEditorSessionEnded);
|
||||
}
|
||||
|
||||
String _getErrorMessage(dynamic error) {
|
||||
if (error is ApiError) {
|
||||
switch (error.code) {
|
||||
case 'UNAUTHENTICATED':
|
||||
return 'Session expired. Please log in again.';
|
||||
case 'PERMISSION_DENIED':
|
||||
return 'You do not have permission to edit this file.';
|
||||
case 'NOT_FOUND':
|
||||
return 'The file was not found.';
|
||||
case 'CONFLICT':
|
||||
return 'The file has been modified. Please refresh and try again.';
|
||||
case 'INVALID_ARGUMENT':
|
||||
return 'Invalid request.';
|
||||
case 'INTERNAL':
|
||||
default:
|
||||
return 'An error occurred while opening the editor.';
|
||||
}
|
||||
}
|
||||
return error.toString();
|
||||
}
|
||||
|
||||
void _onEditorSessionStarted(
|
||||
EditorSessionStarted event,
|
||||
Emitter<EditorSessionState> emit,
|
||||
@@ -25,19 +47,21 @@ class EditorSessionBloc extends Bloc<EditorSessionEvent, EditorSessionState> {
|
||||
event.orgId,
|
||||
event.fileId,
|
||||
);
|
||||
if (session.expiresAt.isBefore(DateTime.now())) {
|
||||
emit(EditorSessionExpired());
|
||||
return;
|
||||
}
|
||||
if (!session.readOnly) {
|
||||
emit(EditorSessionActive(editUrl: session.editUrl));
|
||||
} else {
|
||||
emit(EditorSessionReadOnly(viewUrl: session.editUrl));
|
||||
}
|
||||
if (session.expiresAt.isAfter(DateTime.now())) {
|
||||
_expiryTimer = Timer(
|
||||
session.expiresAt.difference(DateTime.now()),
|
||||
() => emit(EditorSessionExpired()),
|
||||
);
|
||||
}
|
||||
_expiryTimer = Timer(
|
||||
session.expiresAt.difference(DateTime.now()),
|
||||
() => add(EditorSessionEnded()), // Or emit expired
|
||||
);
|
||||
} catch (e) {
|
||||
emit(EditorSessionFailed(message: e.toString()));
|
||||
emit(EditorSessionFailed(message: _getErrorMessage(e)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'file_browser_event.dart';
|
||||
import 'file_browser_state.dart';
|
||||
import '../../services/file_service.dart';
|
||||
import '../../models/file_item.dart';
|
||||
import '../../models/api_error.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
class FileBrowserBloc extends Bloc<FileBrowserEvent, FileBrowserState> {
|
||||
@@ -33,6 +34,27 @@ class FileBrowserBloc extends Bloc<FileBrowserEvent, FileBrowserState> {
|
||||
on<SearchFiles>(_onSearchFiles);
|
||||
}
|
||||
|
||||
String _getErrorMessage(dynamic error) {
|
||||
if (error is ApiError) {
|
||||
switch (error.code) {
|
||||
case 'UNAUTHENTICATED':
|
||||
return 'Session expired. Please log in again.';
|
||||
case 'PERMISSION_DENIED':
|
||||
return 'You do not have access to this folder.';
|
||||
case 'NOT_FOUND':
|
||||
return 'The requested folder or file was not found.';
|
||||
case 'CONFLICT':
|
||||
return 'A conflict occurred. Please try again.';
|
||||
case 'INVALID_ARGUMENT':
|
||||
return 'Invalid input provided.';
|
||||
case 'INTERNAL':
|
||||
default:
|
||||
return 'An internal error occurred. Please try again later.';
|
||||
}
|
||||
}
|
||||
return error.toString();
|
||||
}
|
||||
|
||||
void _onLoadDirectory(
|
||||
LoadDirectory event,
|
||||
Emitter<FileBrowserState> emit,
|
||||
@@ -52,7 +74,7 @@ class FileBrowserBloc extends Bloc<FileBrowserEvent, FileBrowserState> {
|
||||
_emitLoadedState(emit);
|
||||
}
|
||||
} catch (e) {
|
||||
emit(DirectoryError(e.toString()));
|
||||
emit(DirectoryError(_getErrorMessage(e)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +142,7 @@ class FileBrowserBloc extends Bloc<FileBrowserEvent, FileBrowserState> {
|
||||
add(LoadDirectory(orgId: event.orgId, path: event.parentPath));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(DirectoryError(e.toString()));
|
||||
emit(DirectoryError(_getErrorMessage(e)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +165,7 @@ class FileBrowserBloc extends Bloc<FileBrowserEvent, FileBrowserState> {
|
||||
_emitLoadedState(emit);
|
||||
}
|
||||
} catch (e) {
|
||||
emit(DirectoryError(e.toString()));
|
||||
emit(DirectoryError(_getErrorMessage(e)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +185,7 @@ class FileBrowserBloc extends Bloc<FileBrowserEvent, FileBrowserState> {
|
||||
_emitLoadedState(emit);
|
||||
}
|
||||
} catch (e) {
|
||||
emit(DirectoryError(e.toString()));
|
||||
emit(DirectoryError(_getErrorMessage(e)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +198,7 @@ class FileBrowserBloc extends Bloc<FileBrowserEvent, FileBrowserState> {
|
||||
.toList();
|
||||
_emitLoadedState(emit);
|
||||
} catch (e) {
|
||||
emit(DirectoryError(e.toString()));
|
||||
emit(DirectoryError(_getErrorMessage(e)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,7 +241,7 @@ class FileBrowserBloc extends Bloc<FileBrowserEvent, FileBrowserState> {
|
||||
_emitLoadedState(emit);
|
||||
}
|
||||
} catch (e) {
|
||||
emit(DirectoryError(e.toString()));
|
||||
emit(DirectoryError(_getErrorMessage(e)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import '../file_browser/file_browser_event.dart';
|
||||
import '../upload/upload_bloc.dart';
|
||||
import '../upload/upload_event.dart';
|
||||
import '../../services/org_api.dart';
|
||||
import '../../models/api_error.dart';
|
||||
|
||||
class OrganizationBloc extends Bloc<OrganizationEvent, OrganizationState> {
|
||||
final PermissionBloc permissionBloc;
|
||||
@@ -27,6 +28,27 @@ class OrganizationBloc extends Bloc<OrganizationEvent, OrganizationState> {
|
||||
on<CreateOrganization>(_onCreateOrganization);
|
||||
}
|
||||
|
||||
String _getErrorMessage(dynamic error) {
|
||||
if (error is ApiError) {
|
||||
switch (error.code) {
|
||||
case 'UNAUTHENTICATED':
|
||||
return 'Session expired. Please log in again.';
|
||||
case 'PERMISSION_DENIED':
|
||||
return 'You do not have permission to perform this action.';
|
||||
case 'NOT_FOUND':
|
||||
return 'The requested resource was not found.';
|
||||
case 'CONFLICT':
|
||||
return 'A conflict occurred. Please try again.';
|
||||
case 'INVALID_ARGUMENT':
|
||||
return 'Invalid input provided.';
|
||||
case 'INTERNAL':
|
||||
default:
|
||||
return 'An internal error occurred. Please try again later.';
|
||||
}
|
||||
}
|
||||
return error.toString();
|
||||
}
|
||||
|
||||
void _onLoadOrganizations(
|
||||
LoadOrganizations event,
|
||||
Emitter<OrganizationState> emit,
|
||||
@@ -41,7 +63,7 @@ class OrganizationBloc extends Bloc<OrganizationEvent, OrganizationState> {
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
emit(OrganizationError(e.toString()));
|
||||
emit(OrganizationError(_getErrorMessage(e)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +146,7 @@ class OrganizationBloc extends Bloc<OrganizationEvent, OrganizationState> {
|
||||
organizations: currentState.organizations,
|
||||
selectedOrg: currentState.selectedOrg,
|
||||
isLoading: false,
|
||||
error: e.toString(),
|
||||
error: _getErrorMessage(e),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'pdf_annotation_event.dart';
|
||||
import 'pdf_annotation_state.dart';
|
||||
import '../../services/file_service.dart';
|
||||
import '../../models/annotation.dart';
|
||||
import '../../models/api_error.dart';
|
||||
|
||||
class PdfAnnotationBloc extends Bloc<PdfAnnotationEvent, PdfAnnotationState> {
|
||||
final FileService _fileService;
|
||||
@@ -22,6 +23,20 @@ class PdfAnnotationBloc extends Bloc<PdfAnnotationEvent, PdfAnnotationState> {
|
||||
on<AnnotationsSaved>(_onAnnotationsSaved);
|
||||
}
|
||||
|
||||
String _getErrorMessage(dynamic error) {
|
||||
if (error is ApiError) {
|
||||
switch (error.code) {
|
||||
case 'CONFLICT':
|
||||
return 'The document has changed. Please reload to see the latest version.';
|
||||
case 'PERMISSION_DENIED':
|
||||
return 'You do not have permission to annotate this document.';
|
||||
default:
|
||||
return error.message;
|
||||
}
|
||||
}
|
||||
return error.toString();
|
||||
}
|
||||
|
||||
void _onAnnotationToolSelected(
|
||||
AnnotationToolSelected event,
|
||||
Emitter<PdfAnnotationState> emit,
|
||||
@@ -107,7 +122,7 @@ class PdfAnnotationBloc extends Bloc<PdfAnnotationEvent, PdfAnnotationState> {
|
||||
// Reset to idle or editing?
|
||||
emit(PdfAnnotationIdle());
|
||||
} catch (e) {
|
||||
emit(PdfAnnotationError(message: e.toString()));
|
||||
emit(PdfAnnotationError(message: _getErrorMessage(e)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:b0esche_cloud/services/api_client.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'repositories/auth_repository.dart';
|
||||
import 'repositories/file_repository.dart';
|
||||
@@ -17,7 +18,7 @@ void configureDependencies() {
|
||||
|
||||
// Register services
|
||||
getIt.registerSingleton<AuthService>(AuthService(getIt<AuthRepository>()));
|
||||
getIt.registerSingleton<FileService>(FileService(getIt<FileRepository>()));
|
||||
getIt.registerSingleton<FileService>(FileService(getIt<ApiClient>()));
|
||||
|
||||
// Register viewmodels
|
||||
getIt.registerSingleton<LoginViewModel>(LoginViewModel(getIt<AuthService>()));
|
||||
|
||||
@@ -118,36 +118,21 @@ class ApiClient {
|
||||
ApiError _handleError(DioException e) {
|
||||
final status = e.response?.statusCode;
|
||||
final data = e.response?.data;
|
||||
if (status == 403) {
|
||||
|
||||
// Handle network errors
|
||||
if (e.type == DioExceptionType.connectionError ||
|
||||
e.type == DioExceptionType.connectionTimeout ||
|
||||
e.type == DioExceptionType.receiveTimeout ||
|
||||
e.type == DioExceptionType.sendTimeout) {
|
||||
return ApiError(
|
||||
code: 'permission_denied',
|
||||
message: 'Access denied',
|
||||
status: status,
|
||||
);
|
||||
} else if (status == 404) {
|
||||
return ApiError(
|
||||
code: 'not_found',
|
||||
message: 'Resource not found',
|
||||
status: status,
|
||||
);
|
||||
} else if (status == 409) {
|
||||
return ApiError(
|
||||
code: 'conflict',
|
||||
message: 'Version conflict',
|
||||
status: status,
|
||||
);
|
||||
} else if (status == 401) {
|
||||
return ApiError(
|
||||
code: 'unauthorized',
|
||||
message: 'Unauthorized',
|
||||
status: status,
|
||||
);
|
||||
} else {
|
||||
return ApiError(
|
||||
code: 'server_error',
|
||||
message: data?['message'] ?? 'Server error',
|
||||
code: 'NETWORK_ERROR',
|
||||
message: 'Network error. Please check your connection and try again.',
|
||||
status: status,
|
||||
);
|
||||
}
|
||||
|
||||
String code = data?['code'] ?? 'UNKNOWN';
|
||||
String message = data?['message'] ?? 'Unknown error';
|
||||
return ApiError(code: code, message: message, status: status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,15 +20,6 @@ class MockFileService extends Mock implements FileService {
|
||||
_viewerResponse = null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ViewerSession> requestViewerSession(String orgId, String fileId) {
|
||||
return _viewerResponse ?? super.noSuchMethod(
|
||||
Invocation.method(#requestViewerSession, [orgId, fileId]),
|
||||
returnValue: Future.value(null),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ViewerSession> requestViewerSession(String orgId, String fileId) {
|
||||
return _viewerResponse ??
|
||||
@@ -39,6 +30,16 @@ class MockFileService extends Mock implements FileService {
|
||||
}
|
||||
}
|
||||
|
||||
// @override
|
||||
// Future<ViewerSession> requestViewerSession(String orgId, String fileId) {
|
||||
// return _viewerResponse ??
|
||||
// super.noSuchMethod(
|
||||
// Invocation.method(#requestViewerSession, [orgId, fileId]),
|
||||
// returnValue: Future.value(null),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
void main() {
|
||||
late MockFileService mockFileService;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user