mirror of
https://github.com/localsend/localsend.git
synced 2026-06-23 04:10:07 +00:00
feat: use rhttp (#1988)
This commit is contained in:
@@ -65,6 +65,8 @@ PODS:
|
||||
- photo_manager (2.0.0):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- rhttp (0.0.1):
|
||||
- Flutter
|
||||
- SDWebImage (5.19.6):
|
||||
- SDWebImage/Core (= 5.19.6)
|
||||
- SDWebImage/Core (5.19.6)
|
||||
@@ -108,6 +110,7 @@ DEPENDENCIES:
|
||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||
- photo_manager (from `.symlinks/plugins/photo_manager/ios`)
|
||||
- rhttp (from `.symlinks/plugins/rhttp/ios`)
|
||||
- share_handler_ios (from `.symlinks/plugins/share_handler_ios/ios`)
|
||||
- share_handler_ios_models (from `.symlinks/plugins/share_handler_ios/ios/Models`)
|
||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
@@ -155,6 +158,8 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||
photo_manager:
|
||||
:path: ".symlinks/plugins/photo_manager/ios"
|
||||
rhttp:
|
||||
:path: ".symlinks/plugins/rhttp/ios"
|
||||
share_handler_ios:
|
||||
:path: ".symlinks/plugins/share_handler_ios/ios"
|
||||
share_handler_ios_models:
|
||||
@@ -190,6 +195,7 @@ SPEC CHECKSUMS:
|
||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
|
||||
photo_manager: ff695c7a1dd5bc379974953a2b5c0a293f7c4c8a
|
||||
rhttp: 367a8162e63311c6dde543169b591cc04454dcd6
|
||||
SDWebImage: a79252b60f4678812d94316c91da69ec83089c9f
|
||||
share_handler_ios: ae3584532280673e02aacdf77f2cdfb2c96b9211
|
||||
share_handler_ios_models: fc638c9b4330dc7f082586c92aee9dfa0b87b871
|
||||
|
||||
@@ -43,10 +43,12 @@ import 'package:localsend_app/util/native/device_info_helper.dart';
|
||||
import 'package:localsend_app/util/native/macos_channel.dart';
|
||||
import 'package:localsend_app/util/native/platform_check.dart';
|
||||
import 'package:localsend_app/util/native/tray_helper.dart';
|
||||
import 'package:localsend_app/util/rhttp.dart';
|
||||
import 'package:localsend_app/util/ui/dynamic_colors.dart';
|
||||
import 'package:localsend_app/util/ui/snackbar.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:refena_flutter/refena_flutter.dart';
|
||||
import 'package:rhttp/rhttp.dart';
|
||||
import 'package:share_handler/share_handler.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
@@ -145,7 +147,11 @@ Future<RefenaContainer> preInit(List<String> args) async {
|
||||
return IsolateController(
|
||||
initialState: ParentIsolateState.initial(
|
||||
SyncState(
|
||||
init: () async {
|
||||
await Rhttp.init();
|
||||
},
|
||||
rootIsolateToken: RootIsolateToken.instance!,
|
||||
httpClientFactory: RhttpWrapper.create,
|
||||
securityContext: persistenceService.getSecurityContext(),
|
||||
deviceInfo: ref.read(deviceInfoProvider),
|
||||
alias: settings.alias,
|
||||
|
||||
@@ -349,11 +349,16 @@ class SendNotifier extends Notifier<Map<String, SendSessionState>> {
|
||||
}
|
||||
|
||||
void _finish({required String sessionId}) {
|
||||
if (state[sessionId] != null && state[sessionId]!.status != SessionStatus.sending) {
|
||||
final sessionState = state[sessionId];
|
||||
if (sessionState == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (state[sessionId]!.status != SessionStatus.sending) {
|
||||
_logger.info('Transfer was canceled.');
|
||||
} else {
|
||||
final hasError = state[sessionId]!.files.values.any((file) => file.status == FileStatus.failed);
|
||||
if (!hasError && state[sessionId]!.background == true) {
|
||||
final hasError = sessionState.files.values.any((file) => file.status == FileStatus.failed);
|
||||
if (!hasError && sessionState.background == true) {
|
||||
// close session because everything is fine and it is in background
|
||||
closeSession(sessionId);
|
||||
_logger.info('Transfer finished and session removed.');
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import 'package:common/isolate.dart';
|
||||
import 'package:common/model/stored_security_context.dart';
|
||||
import 'package:rhttp/rhttp.dart';
|
||||
|
||||
class RhttpWrapper implements CustomHttpClient {
|
||||
final RhttpClient _client;
|
||||
|
||||
RhttpWrapper._(this._client);
|
||||
|
||||
factory RhttpWrapper.create(Duration timeout, StoredSecurityContext securityContext) {
|
||||
final client = RhttpClient.createSync(
|
||||
settings: ClientSettings(
|
||||
timeoutSettings: TimeoutSettings(
|
||||
timeout: timeout,
|
||||
),
|
||||
tlsSettings: TlsSettings(
|
||||
verifyCertificates: false,
|
||||
clientCertificate: ClientCertificate(
|
||||
certificate: securityContext.certificate,
|
||||
privateKey: securityContext.privateKey,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
return RhttpWrapper._(client);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> postStream({
|
||||
required String uri,
|
||||
required Map<String, String> query,
|
||||
required Map<String, String> headers,
|
||||
required Stream<List<int>> stream,
|
||||
required void Function(double progress) onSendProgress,
|
||||
required CustomCancelToken cancelToken,
|
||||
}) async {
|
||||
final token = CancelToken();
|
||||
cancelToken.setCancel(token.cancel);
|
||||
await _client.request(
|
||||
method: HttpMethod.post,
|
||||
expectBody: HttpExpectBody.bytes,
|
||||
url: uri,
|
||||
query: query,
|
||||
headers: HttpHeaders.rawMap(headers),
|
||||
body: HttpBody.stream(
|
||||
stream,
|
||||
length: int.parse(headers['Content-Length']!),
|
||||
),
|
||||
onSendProgress: (curr, total) {
|
||||
onSendProgress(curr / total);
|
||||
},
|
||||
cancelToken: token,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
rhttp
|
||||
)
|
||||
|
||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||
|
||||
@@ -30,6 +30,8 @@ PODS:
|
||||
- photo_manager (2.0.0):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- rhttp (0.0.1):
|
||||
- FlutterMacOS
|
||||
- screen_retriever (0.0.1):
|
||||
- FlutterMacOS
|
||||
- shared_preferences_foundation (0.0.1):
|
||||
@@ -64,6 +66,7 @@ DEPENDENCIES:
|
||||
- pasteboard (from `Flutter/ephemeral/.symlinks/plugins/pasteboard/macos`)
|
||||
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- photo_manager (from `Flutter/ephemeral/.symlinks/plugins/photo_manager/macos`)
|
||||
- rhttp (from `Flutter/ephemeral/.symlinks/plugins/rhttp/macos`)
|
||||
- screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`)
|
||||
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
- tray_manager (from `Flutter/ephemeral/.symlinks/plugins/tray_manager/macos`)
|
||||
@@ -104,6 +107,8 @@ EXTERNAL SOURCES:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
|
||||
photo_manager:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/photo_manager/macos
|
||||
rhttp:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/rhttp/macos
|
||||
screen_retriever:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos
|
||||
shared_preferences_foundation:
|
||||
@@ -136,6 +141,7 @@ SPEC CHECKSUMS:
|
||||
pasteboard: 9b69dba6fedbb04866be632205d532fe2f6b1d99
|
||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||
photo_manager: ff695c7a1dd5bc379974953a2b5c0a293f7c4c8a
|
||||
rhttp: 337afda4c3e4df31087160719ffca6452c225cc2
|
||||
screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38
|
||||
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
||||
tray_manager: 9064e219c56d75c476e46b9a21182087930baf90
|
||||
|
||||
@@ -94,6 +94,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
build_cli_annotations:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_cli_annotations
|
||||
sha256: b59d2769769efd6c9ff6d4c4cede0be115a566afc591705c2040b707534b1172
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
build_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -543,6 +551,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.23"
|
||||
flutter_rust_bridge:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_rust_bridge
|
||||
sha256: "5fe868d3cb8cbc4d83091748552e03f00ccfa41b8e44691bc382611f831d5f8b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.1"
|
||||
flutter_test:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
@@ -553,6 +569,14 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
freezed_annotation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: freezed_annotation
|
||||
sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.4"
|
||||
frontend_server_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1251,6 +1275,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
rhttp:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: rhttp
|
||||
sha256: "8dc4e1b50cbbd4422a3da93accfd40ea96227fb7d2264d6856754122aedfb194"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.9.0"
|
||||
routerino:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
@@ -50,6 +50,7 @@ dependencies:
|
||||
pretty_qr_code: 3.3.0
|
||||
refena_flutter: 2.1.1
|
||||
refena_inspector_client: 2.0.0
|
||||
rhttp: 0.9.0
|
||||
routerino: 0.8.0
|
||||
saf_stream: 0.7.5
|
||||
screen_retriever: 0.1.9
|
||||
|
||||
@@ -19,6 +19,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
rhttp
|
||||
)
|
||||
|
||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export 'package:common/src/isolate/child/http_provider.dart' show CustomHttpClient, CustomCancelToken;
|
||||
export 'package:common/src/isolate/child/sync_provider.dart';
|
||||
export 'package:common/src/isolate/child/upload_isolate.dart' show UriContentStreamResolver;
|
||||
export 'package:common/src/isolate/parent/actions.dart';
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import 'package:common/src/isolate/child/sync_provider.dart';
|
||||
import 'package:refena/refena.dart';
|
||||
|
||||
/// An abstraction to provide a custom http client.
|
||||
abstract class CustomHttpClient {
|
||||
Future<void> postStream({
|
||||
required String uri,
|
||||
required Map<String, String> query,
|
||||
required Map<String, String> headers,
|
||||
required Stream<List<int>> stream,
|
||||
required void Function(double) onSendProgress,
|
||||
required CustomCancelToken cancelToken,
|
||||
});
|
||||
}
|
||||
|
||||
class CustomCancelToken {
|
||||
void Function()? _cancel;
|
||||
|
||||
void cancel() {
|
||||
_cancel?.call();
|
||||
}
|
||||
|
||||
void setCancel(void Function() cancel) {
|
||||
_cancel = cancel;
|
||||
}
|
||||
}
|
||||
|
||||
class HttpClientCollection {
|
||||
final CustomHttpClient discovery;
|
||||
final CustomHttpClient longLiving;
|
||||
|
||||
HttpClientCollection({
|
||||
required this.discovery,
|
||||
required this.longLiving,
|
||||
});
|
||||
}
|
||||
|
||||
final httpProvider = ViewProvider((ref) {
|
||||
final (clientFactory, securityContext, discoveryTimeout) =
|
||||
ref.watch(syncProvider.select((state) => (state.httpClientFactory, state.securityContext, state.discoveryTimeout)));
|
||||
return HttpClientCollection(
|
||||
discovery: clientFactory(Duration(milliseconds: discoveryTimeout), securityContext),
|
||||
longLiving: clientFactory(const Duration(days: 30), securityContext),
|
||||
);
|
||||
});
|
||||
@@ -49,6 +49,8 @@ Future<void> setupChildIsolateHelper<S, R>({
|
||||
),
|
||||
);
|
||||
|
||||
await initialData.syncState.init();
|
||||
|
||||
if (init != null) {
|
||||
await init(_isolateContainer);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:common/model/device_info_result.dart';
|
||||
import 'package:common/model/dto/multicast_dto.dart';
|
||||
import 'package:common/model/stored_security_context.dart';
|
||||
import 'package:common/src/isolate/child/http_provider.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:refena/refena.dart';
|
||||
@@ -11,7 +12,9 @@ part 'sync_provider.mapper.dart';
|
||||
/// In other words, the main isolate sends this state to the child isolate.
|
||||
@MappableClass()
|
||||
class SyncState with SyncStateMappable {
|
||||
final Future<void> Function() init;
|
||||
final Object rootIsolateToken;
|
||||
final CustomHttpClient Function(Duration timeout, StoredSecurityContext) httpClientFactory;
|
||||
final StoredSecurityContext securityContext;
|
||||
final DeviceInfoResult deviceInfo;
|
||||
final String alias;
|
||||
@@ -24,7 +27,9 @@ class SyncState with SyncStateMappable {
|
||||
final bool download;
|
||||
|
||||
SyncState({
|
||||
required this.init,
|
||||
required this.rootIsolateToken,
|
||||
required this.httpClientFactory,
|
||||
required this.securityContext,
|
||||
required this.deviceInfo,
|
||||
required this.alias,
|
||||
|
||||
@@ -22,8 +22,14 @@ class SyncStateMapper extends ClassMapperBase<SyncState> {
|
||||
@override
|
||||
final String id = 'SyncState';
|
||||
|
||||
static Function _$init(SyncState v) => (v as dynamic).init as Function;
|
||||
static dynamic _arg$init(f) => f<Future<void> Function()>();
|
||||
static const Field<SyncState, Function> _f$init = Field('init', _$init, arg: _arg$init);
|
||||
static Object _$rootIsolateToken(SyncState v) => v.rootIsolateToken;
|
||||
static const Field<SyncState, Object> _f$rootIsolateToken = Field('rootIsolateToken', _$rootIsolateToken);
|
||||
static Function _$httpClientFactory(SyncState v) => (v as dynamic).httpClientFactory as Function;
|
||||
static dynamic _arg$httpClientFactory(f) => f<CustomHttpClient Function(Duration, StoredSecurityContext)>();
|
||||
static const Field<SyncState, Function> _f$httpClientFactory = Field('httpClientFactory', _$httpClientFactory, arg: _arg$httpClientFactory);
|
||||
static StoredSecurityContext _$securityContext(SyncState v) => v.securityContext;
|
||||
static const Field<SyncState, StoredSecurityContext> _f$securityContext = Field('securityContext', _$securityContext);
|
||||
static DeviceInfoResult _$deviceInfo(SyncState v) => v.deviceInfo;
|
||||
@@ -45,7 +51,9 @@ class SyncStateMapper extends ClassMapperBase<SyncState> {
|
||||
|
||||
@override
|
||||
final MappableFields<SyncState> fields = const {
|
||||
#init: _f$init,
|
||||
#rootIsolateToken: _f$rootIsolateToken,
|
||||
#httpClientFactory: _f$httpClientFactory,
|
||||
#securityContext: _f$securityContext,
|
||||
#deviceInfo: _f$deviceInfo,
|
||||
#alias: _f$alias,
|
||||
@@ -59,7 +67,9 @@ class SyncStateMapper extends ClassMapperBase<SyncState> {
|
||||
|
||||
static SyncState _instantiate(DecodingData data) {
|
||||
return SyncState(
|
||||
init: data.dec(_f$init),
|
||||
rootIsolateToken: data.dec(_f$rootIsolateToken),
|
||||
httpClientFactory: data.dec(_f$httpClientFactory),
|
||||
securityContext: data.dec(_f$securityContext),
|
||||
deviceInfo: data.dec(_f$deviceInfo),
|
||||
alias: data.dec(_f$alias),
|
||||
@@ -116,7 +126,9 @@ extension SyncStateValueCopy<$R, $Out> on ObjectCopyWith<$R, SyncState, $Out> {
|
||||
abstract class SyncStateCopyWith<$R, $In extends SyncState, $Out> implements ClassCopyWith<$R, $In, $Out> {
|
||||
StoredSecurityContextCopyWith<$R, StoredSecurityContext, StoredSecurityContext> get securityContext;
|
||||
$R call(
|
||||
{Object? rootIsolateToken,
|
||||
{Future<void> Function()? init,
|
||||
Object? rootIsolateToken,
|
||||
CustomHttpClient Function(Duration, StoredSecurityContext)? httpClientFactory,
|
||||
StoredSecurityContext? securityContext,
|
||||
DeviceInfoResult? deviceInfo,
|
||||
String? alias,
|
||||
@@ -139,7 +151,9 @@ class _SyncStateCopyWithImpl<$R, $Out> extends ClassCopyWithBase<$R, SyncState,
|
||||
$value.securityContext.copyWith.$chain((v) => call(securityContext: v));
|
||||
@override
|
||||
$R call(
|
||||
{Object? rootIsolateToken,
|
||||
{Future<void> Function()? init,
|
||||
Object? rootIsolateToken,
|
||||
CustomHttpClient Function(Duration, StoredSecurityContext)? httpClientFactory,
|
||||
StoredSecurityContext? securityContext,
|
||||
DeviceInfoResult? deviceInfo,
|
||||
String? alias,
|
||||
@@ -150,7 +164,9 @@ class _SyncStateCopyWithImpl<$R, $Out> extends ClassCopyWithBase<$R, SyncState,
|
||||
bool? serverRunning,
|
||||
bool? download}) =>
|
||||
$apply(FieldCopyWithData({
|
||||
if (init != null) #init: init,
|
||||
if (rootIsolateToken != null) #rootIsolateToken: rootIsolateToken,
|
||||
if (httpClientFactory != null) #httpClientFactory: httpClientFactory,
|
||||
if (securityContext != null) #securityContext: securityContext,
|
||||
if (deviceInfo != null) #deviceInfo: deviceInfo,
|
||||
if (alias != null) #alias: alias,
|
||||
@@ -163,7 +179,9 @@ class _SyncStateCopyWithImpl<$R, $Out> extends ClassCopyWithBase<$R, SyncState,
|
||||
}));
|
||||
@override
|
||||
SyncState $make(CopyWithData data) => SyncState(
|
||||
init: data.get(#init, or: $value.init),
|
||||
rootIsolateToken: data.get(#rootIsolateToken, or: $value.rootIsolateToken),
|
||||
httpClientFactory: data.get(#httpClientFactory, or: $value.httpClientFactory),
|
||||
securityContext: data.get(#securityContext, or: $value.securityContext),
|
||||
deviceInfo: data.get(#deviceInfo, or: $value.deviceInfo),
|
||||
alias: data.get(#alias, or: $value.alias),
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:common/isolate.dart';
|
||||
import 'package:common/model/device.dart';
|
||||
import 'package:common/src/isolate/child/main.dart';
|
||||
import 'package:common/src/isolate/child/sync_provider.dart';
|
||||
import 'package:common/src/isolate/dto/isolate_task.dart';
|
||||
import 'package:common/src/isolate/dto/isolate_task_result.dart';
|
||||
import 'package:common/src/isolate/dto/send_to_isolate_data.dart';
|
||||
import 'package:common/src/task/upload/http_upload.dart';
|
||||
import 'package:common/util/stream.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:refena/refena.dart';
|
||||
|
||||
@@ -53,7 +52,7 @@ class HttpUploadCancelTask implements BaseHttpUploadTask {
|
||||
|
||||
/// Map of cancel tokens for each task.
|
||||
/// Task ID -> CancelToken
|
||||
final _cancelTokenProvider = Provider((ref) => <int, CancelToken>{});
|
||||
final _cancelTokenProvider = Provider((ref) => <int, CustomCancelToken>{});
|
||||
|
||||
abstract class UriContentStreamResolver {
|
||||
/// Separate initialization method to create instance in the child isolate.
|
||||
@@ -106,7 +105,7 @@ Future<void> setupHttpUploadIsolate(
|
||||
final (streamController, subscription) = fileStream?.digested() ?? (null, null);
|
||||
|
||||
try {
|
||||
final cancelToken = CancelToken();
|
||||
final cancelToken = CustomCancelToken();
|
||||
ref.read(_cancelTokenProvider).putIfAbsent(task.id, () => cancelToken);
|
||||
|
||||
await ref.read(httpUploadProvider).upload(
|
||||
|
||||
@@ -196,6 +196,7 @@ class IsolateHttpUploadAction extends ReduxActionWithResult<IsolateController, P
|
||||
final progress = _sendTaskAndListenStream(
|
||||
task: task,
|
||||
connection: connection,
|
||||
taskId: taskId,
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -240,9 +241,10 @@ class IsolateHttpUploadCancelAction extends ReduxAction<IsolateController, Paren
|
||||
Stream<R> _sendTaskAndListenStream<R, T>({
|
||||
required T task,
|
||||
required IsolateConnector<IsolateTaskStreamResult<R>, SendToIsolateData<IsolateTask<T>>> connection,
|
||||
int? taskId,
|
||||
}) {
|
||||
final wrappedTask = IsolateTask(
|
||||
id: _idProvider.getNextId(),
|
||||
id: taskId ?? _idProvider.getNextId(),
|
||||
data: task,
|
||||
);
|
||||
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import 'package:common/api_route_builder.dart';
|
||||
import 'package:common/model/device.dart';
|
||||
import 'package:common/src/isolate/child/dio_provider.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:common/src/isolate/child/http_provider.dart';
|
||||
import 'package:refena/refena.dart';
|
||||
|
||||
final httpUploadProvider = ViewProvider((ref) {
|
||||
final dio = ref.watch(dioProvider).longLiving;
|
||||
return HttpUploadService(dio);
|
||||
final client = ref.watch(httpProvider).longLiving;
|
||||
return HttpUploadService(client);
|
||||
});
|
||||
|
||||
class HttpUploadService {
|
||||
final Dio _dio;
|
||||
final CustomHttpClient _client;
|
||||
|
||||
HttpUploadService(this._dio);
|
||||
HttpUploadService(this._client);
|
||||
|
||||
Future<void> upload({
|
||||
required Stream<List<int>> stream,
|
||||
@@ -23,28 +22,21 @@ class HttpUploadService {
|
||||
required String fileId,
|
||||
required String token,
|
||||
required void Function(double) onSendProgress,
|
||||
required CancelToken cancelToken,
|
||||
required CustomCancelToken cancelToken,
|
||||
}) async {
|
||||
final stopwatch = Stopwatch()..start();
|
||||
await _dio.post(
|
||||
ApiRoute.upload.target(target, query: {
|
||||
await _client.postStream(
|
||||
uri: ApiRoute.upload.target(target),
|
||||
query: {
|
||||
if (remoteSessionId != null) 'sessionId': remoteSessionId,
|
||||
'fileId': fileId,
|
||||
'token': token,
|
||||
}),
|
||||
options: Options(
|
||||
headers: {
|
||||
'Content-Length': contentLength,
|
||||
'Content-Type': contentType,
|
||||
},
|
||||
),
|
||||
data: stream,
|
||||
onSendProgress: (curr, total) {
|
||||
if (stopwatch.elapsedMilliseconds >= 100) {
|
||||
stopwatch.reset();
|
||||
onSendProgress(curr / total);
|
||||
}
|
||||
},
|
||||
headers: {
|
||||
'Content-Length': contentLength.toString(),
|
||||
'Content-Type': contentType,
|
||||
},
|
||||
stream: stream,
|
||||
onSendProgress: onSendProgress,
|
||||
cancelToken: cancelToken,
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user