Skip to content

Commit

Permalink
chore: observe file upload state (AppFlowy-IO#6172)
Browse files Browse the repository at this point in the history
* chore: observe file upload state

* chore: observe file upload state

* chore: upgrade client api

* Update frontend/appflowy_flutter/lib/startup/tasks/file_storage_task.dart

Co-authored-by: Lucas.Xu <[email protected]>

* chore: fix tauri build

---------

Co-authored-by: Lucas.Xu <[email protected]>
  • Loading branch information
appflowy and LucasXu0 authored Sep 3, 2024
1 parent 2bb33e1 commit e9b2cbb
Show file tree
Hide file tree
Showing 29 changed files with 484 additions and 123 deletions.
2 changes: 2 additions & 0 deletions frontend/appflowy_flutter/lib/startup/startup.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import 'deps_resolver.dart';
import 'entry_point.dart';
import 'launch_configuration.dart';
import 'plugin/plugin.dart';
import 'tasks/file_storage_task.dart';
import 'tasks/prelude.dart';

final getIt = GetIt.instance;
Expand Down Expand Up @@ -126,6 +127,7 @@ class FlowyRunner {
InitRustSDKTask(customApplicationPath: applicationDataDirectory),
// Load Plugins, like document, grid ...
const PluginLoadTask(),
const FileStorageTask(),

// init the app widget
// ignore in test mode
Expand Down
149 changes: 149 additions & 0 deletions frontend/appflowy_flutter/lib/startup/tasks/file_storage_task.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import 'dart:async';
import 'dart:convert';
import 'dart:ffi';
import 'dart:isolate';

import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-storage/protobuf.dart';
import 'package:appflowy_result/appflowy_result.dart';
import 'package:flutter/foundation.dart';
import 'package:fixnum/fixnum.dart';

import '../startup.dart';

class FileStorageTask extends LaunchTask {
const FileStorageTask();

@override
Future<void> initialize(LaunchContext context) async {
context.getIt.registerSingleton(
FileStorageService(),
dispose: (service) async {
await service.dispose();
},
);
}

@override
Future<void> dispose() async {}
}

class FileStorageService {
FileStorageService() {
_port.handler = _controller.add;
_subscription = _controller.stream.listen(
(event) {
final fileProgress = FileProgress.fromJsonString(event);
if (fileProgress != null) {
Log.debug(
"Upload progress: file: ${fileProgress.fileUrl} ${fileProgress.progress}",
);
final notifier = _notifierList[fileProgress.fileUrl];
if (notifier != null) {
notifier.value = fileProgress;
}
}
},
);

final payload = RegisterStreamPB()..port = Int64(_port.sendPort.nativePort);
FileStorageEventRegisterStream(payload).send();
}

final Map<String, AutoRemoveNotifier<FileProgress>> _notifierList = {};
final RawReceivePort _port = RawReceivePort();
final StreamController<String> _controller = StreamController.broadcast();
late StreamSubscription<String> _subscription;

AutoRemoveNotifier<FileProgress> onFileProgress({required String fileUrl}) {
_notifierList.remove(fileUrl)?.dispose();

final notifier = AutoRemoveNotifier<FileProgress>(
FileProgress(fileUrl: fileUrl, progress: 0),
notifierList: _notifierList,
fileId: fileUrl,
);
_notifierList[fileUrl] = notifier;

// trigger the initial file state
getFileState(fileUrl);

return notifier;
}

Future<FlowyResult<FileStatePB, FlowyError>> getFileState(String url) {
final payload = QueryFilePB()..url = url;
return FileStorageEventQueryFile(payload).send();
}

Future<void> dispose() async {
// dispose all notifiers
for (final notifier in _notifierList.values) {
notifier.dispose();
}

await _controller.close();
await _subscription.cancel();
_port.close();
}
}

class FileProgress {
FileProgress({
required this.fileUrl,
required this.progress,
this.error,
});

static FileProgress? fromJson(Map<String, dynamic>? json) {
if (json == null) {
return null;
}

try {
if (json.containsKey('file_url') && json.containsKey('progress')) {
return FileProgress(
fileUrl: json['file_url'] as String,
progress: (json['progress'] as num).toDouble(),
error: json['error'] as String?,
);
}
} catch (e) {
Log.error('unable to parse file progress: $e');
}
return null;
}

// Method to parse a JSON string and return a FileProgress object or null
static FileProgress? fromJsonString(String jsonString) {
try {
final Map<String, dynamic> jsonMap = jsonDecode(jsonString);
return FileProgress.fromJson(jsonMap);
} catch (e) {
return null;
}
}

final double progress;
final String fileUrl;
final String? error;
}

class AutoRemoveNotifier<T> extends ValueNotifier<T> {
AutoRemoveNotifier(
super.value, {
required this.fileId,
required Map<String, AutoRemoveNotifier<FileProgress>> notifierList,
}) : _notifierList = notifierList;

final String fileId;
final Map<String, AutoRemoveNotifier<FileProgress>> _notifierList;

@override
void dispose() {
_notifierList.remove(fileId);
super.dispose();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-search/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-ai/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-storage/protobuf.dart';
import 'package:appflowy_result/appflowy_result.dart';
import 'package:ffi/ffi.dart';
import 'package:isolates/isolates.dart';
Expand All @@ -38,6 +39,7 @@ part 'dart_event/flowy-config/dart_event.dart';
part 'dart_event/flowy-date/dart_event.dart';
part 'dart_event/flowy-search/dart_event.dart';
part 'dart_event/flowy-ai/dart_event.dart';
part 'dart_event/flowy-storage/dart_event.dart';

enum FFIException {
RequestIsEmpty,
Expand Down
29 changes: 17 additions & 12 deletions frontend/appflowy_tauri/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion frontend/appflowy_tauri/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ collab-user = { version = "0.2" }
# Run the script:
# scripts/tool/update_client_api_rev.sh new_rev_id
# ⚠️⚠️⚠️️
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "72652086f5dfb6f22c67616f8873794cb5f28e1a" }
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "5b5e561afbb161e25d77f3ae5f2ae50ffa2b1755" }

[dependencies]
serde_json.workspace = true
Expand Down
1 change: 1 addition & 0 deletions frontend/appflowy_tauri/src/services/backend/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from "./models/flowy-error";
export * from "./models/flowy-config";
export * from "./models/flowy-date";
export * from "./models/flowy-search";
export * from "./models/flowy-storage";
Loading

0 comments on commit e9b2cbb

Please sign in to comment.