@@ -4,6 +4,8 @@ import 'package:appflowy/plugins/database/application/cell/cell_controller_build
44import 'package:appflowy/plugins/database/application/field/field_info.dart' ;
55import 'package:appflowy/plugins/database/application/field/type_option/type_option_data_parser.dart' ;
66import 'package:appflowy/plugins/database/application/row/row_service.dart' ;
7+ import 'package:appflowy/startup/startup.dart' ;
8+ import 'package:appflowy/startup/tasks/file_storage_task.dart' ;
79import 'package:appflowy/user/application/user_service.dart' ;
810import 'package:appflowy_backend/dispatch/dispatch.dart' ;
911import 'package:appflowy_backend/log.dart' ;
@@ -12,7 +14,9 @@ import 'package:appflowy_backend/protobuf/flowy-database2/file_entities.pbenum.d
1214import 'package:appflowy_backend/protobuf/flowy-database2/media_entities.pb.dart' ;
1315import 'package:appflowy_backend/protobuf/flowy-database2/row_entities.pb.dart' ;
1416import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart' ;
17+ import 'package:collection/collection.dart' ;
1518import 'package:flowy_infra/uuid.dart' ;
19+ import 'package:flutter/scheduler.dart' ;
1620import 'package:flutter_bloc/flutter_bloc.dart' ;
1721import 'package:freezed_annotation/freezed_annotation.dart' ;
1822
@@ -29,6 +33,8 @@ class MediaCellBloc extends Bloc<MediaCellEvent, MediaCellState> {
2933 late final RowBackendService _rowService =
3034 RowBackendService (viewId: cellController.viewId);
3135 final MediaCellController cellController;
36+ final FileStorageService _fileStorageService = getIt <FileStorageService >();
37+ final Map <String , AutoRemoveNotifier <FileProgress >> _progressNotifiers = {};
3238
3339 void Function ()? _onCellChangedFn;
3440
@@ -53,6 +59,8 @@ class MediaCellBloc extends Bloc<MediaCellEvent, MediaCellState> {
5359 (event, emit) async {
5460 await event.when (
5561 initial: () async {
62+ _checkFileStatus ();
63+
5664 // Fetch user profile
5765 final userProfileResult =
5866 await UserBackendService .getCurrentUserProfile ();
@@ -96,9 +104,14 @@ class MediaCellBloc extends Bloc<MediaCellEvent, MediaCellState> {
96104 );
97105
98106 final result = await DatabaseEventUpdateMediaCell (payload).send ();
99- result.fold ((l) => null , (err) => Log .error (err));
107+ result.fold (
108+ (_) => _registerProgressNotifier (newFile),
109+ (err) => Log .error (err),
110+ );
100111 },
101112 removeFile: (id) async {
113+ _removeNotifier (id);
114+
102115 final payload = MediaCellChangesetPB (
103116 viewId: cellController.viewId,
104117 cellId: CellIdPB (
@@ -158,6 +171,40 @@ class MediaCellBloc extends Bloc<MediaCellEvent, MediaCellState> {
158171 rowId: cellController.rowId,
159172 cover: cover,
160173 ),
174+ onProgressUpdate: (id) {
175+ final FileProgress ? progress = _progressNotifiers[id]? .value;
176+ if (progress != null ) {
177+ MediaUploadProgress ? mediaUploadProgress =
178+ state.uploadProgress.firstWhereOrNull ((u) => u.fileId == id);
179+
180+ if (progress.error != null ) {
181+ // Remove file from cell
182+ add (MediaCellEvent .removeFile (fileId: id));
183+ _removeNotifier (id);
184+
185+ // Remove progress
186+ final uploadProgress = [...state.uploadProgress];
187+ uploadProgress.removeWhere ((u) => u.fileId == id);
188+ emit (state.copyWith (uploadProgress: uploadProgress));
189+ return ;
190+ }
191+
192+ mediaUploadProgress ?? = MediaUploadProgress (
193+ fileId: id,
194+ uploadState: progress.progress >= 1
195+ ? MediaUploadState .completed
196+ : MediaUploadState .uploading,
197+ fileProgress: progress,
198+ );
199+
200+ final uploadProgress = [...state.uploadProgress];
201+ uploadProgress
202+ ..removeWhere ((u) => u.fileId == id)
203+ ..add (mediaUploadProgress);
204+
205+ emit (state.copyWith (uploadProgress: uploadProgress));
206+ }
207+ },
161208 );
162209 },
163210 );
@@ -174,6 +221,44 @@ class MediaCellBloc extends Bloc<MediaCellEvent, MediaCellState> {
174221 );
175222 }
176223
224+ /// We check the file state of all the files that are in Cloud (hosted by us) in the cell.
225+ ///
226+ /// If any file has failed, we should notify the user about it,
227+ /// and also remove it from the cell.
228+ ///
229+ /// This method registers the progress notifiers for each file.
230+ ///
231+ void _checkFileStatus () {
232+ for (final file in state.files) {
233+ _registerProgressNotifier (file);
234+ }
235+ }
236+
237+ void _registerProgressNotifier (MediaFilePB file) {
238+ if (file.uploadType != FileUploadTypePB .CloudFile ) {
239+ return ;
240+ }
241+
242+ final notifier = _fileStorageService.onFileProgress (fileUrl: file.url);
243+ _progressNotifiers[file.id] = notifier;
244+ notifier.addListener (() => _onProgressChanged (file.id));
245+
246+ add (MediaCellEvent .onProgressUpdate (file.id));
247+ }
248+
249+ void _onProgressChanged (String id) =>
250+ add (MediaCellEvent .onProgressUpdate (id));
251+
252+ /// Removes and disposes of a progress notifier if found
253+ ///
254+ void _removeNotifier (String id) {
255+ SchedulerBinding .instance.addPostFrameCallback ((_) {
256+ final notifier = _progressNotifiers.remove (id);
257+ notifier? .removeListener (() => _onProgressChanged (id));
258+ notifier? .dispose ();
259+ });
260+ }
261+
177262 void _onFieldChangedListener (FieldInfo fieldInfo) {
178263 if (! isClosed) {
179264 add (MediaCellEvent .didUpdateField (fieldInfo.name));
@@ -221,6 +306,9 @@ class MediaCellEvent with _$MediaCellEvent {
221306 const factory MediaCellEvent .toggleShowAllFiles () = _ToggleShowAllFiles ;
222307
223308 const factory MediaCellEvent .setCover (RowCoverPB cover) = _SetCover ;
309+
310+ const factory MediaCellEvent .onProgressUpdate (String fileId) =
311+ _OnProgressUpdate ;
224312}
225313
226314@freezed
@@ -231,6 +319,7 @@ class MediaCellState with _$MediaCellState {
231319 @Default ([]) List <MediaFilePB > files,
232320 @Default (false ) showAllFiles,
233321 @Default (true ) hideFileNames,
322+ @Default ([]) List <MediaUploadProgress > uploadProgress,
234323 }) = _MediaCellState ;
235324
236325 factory MediaCellState .initial (MediaCellController cellController) {
@@ -245,3 +334,17 @@ class MediaCellState with _$MediaCellState {
245334 );
246335 }
247336}
337+
338+ enum MediaUploadState { uploading, completed }
339+
340+ class MediaUploadProgress {
341+ const MediaUploadProgress ({
342+ required this .fileId,
343+ required this .uploadState,
344+ required this .fileProgress,
345+ });
346+
347+ final String fileId;
348+ final MediaUploadState uploadState;
349+ final FileProgress fileProgress;
350+ }
0 commit comments