Skip to content

Commit 03414b5

Browse files
committed
feat(llc): implement attachment uploads
This commit introduces the `StreamAttachmentUploader` for handling file and image uploads to the Stream CDN. Key changes: - Added `CdnApi` with methods for uploading and deleting files and images. - Implemented `FeedsCdnClient` to wrap `CdnApi` and conform to the `CdnClient` interface from `stream_core`. - Integrated `StreamAttachmentUploader` into `FeedsClientImpl`. - Modified `ActivitiesRepository` and `CommentsRepository` to use `StreamAttachmentUploader` for uploading attachments before creating activities or comments. - Updated `FeedAddActivityRequest` and `ActivityAddCommentRequest` to include `attachmentUploads` (list of `StreamAttachment`). - Renamed `onSendProgress` to `onUploadProgress` in `CdnApi` for clarity. - Updated relevant public API surfaces and internal logic to support attachment uploads. - Regenerated `cdn_api.g.dart`.
1 parent 1932eeb commit 03414b5

15 files changed

+449
-75
lines changed

packages/stream_feeds/lib/src/client/feeds_client_impl.dart

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import 'dart:async';
33
import 'package:stream_core/stream_core.dart';
44

55
import '../feeds_client.dart';
6+
import '../file/cdn_api.dart';
7+
import '../file/feeds_cdn_client.dart';
68
import '../generated/api/api.dart' as api;
79
import '../models/activity_data.dart';
810
import '../models/app_data.dart';
@@ -143,20 +145,23 @@ class StreamFeedsClientImpl implements StreamFeedsClient {
143145
]),
144146
);
145147

146-
final apiClient = api.DefaultApi(httpClient);
147-
148148
// endregion
149149

150150
// region Initialize repositories
151151

152-
_activitiesRepository = ActivitiesRepository(apiClient);
153-
_appRepository = AppRepository(apiClient);
154-
_bookmarksRepository = BookmarksRepository(apiClient);
155-
_commentsRepository = CommentsRepository(apiClient);
156-
_devicesRepository = DevicesRepository(apiClient);
157-
_feedsRepository = FeedsRepository(apiClient);
158-
_moderationRepository = ModerationRepository(apiClient);
159-
_pollsRepository = PollsRepository(apiClient);
152+
_cdnClient = config.cdnClient ?? FeedsCdnClient(CdnApi(httpClient));
153+
attachmentUploader = StreamAttachmentUploader(cdn: _cdnClient);
154+
155+
final feedsApi = api.DefaultApi(httpClient);
156+
157+
_activitiesRepository = ActivitiesRepository(feedsApi, attachmentUploader);
158+
_appRepository = AppRepository(feedsApi);
159+
_bookmarksRepository = BookmarksRepository(feedsApi);
160+
_commentsRepository = CommentsRepository(feedsApi, attachmentUploader);
161+
_devicesRepository = DevicesRepository(feedsApi);
162+
_feedsRepository = FeedsRepository(feedsApi);
163+
_moderationRepository = ModerationRepository(feedsApi);
164+
_pollsRepository = PollsRepository(feedsApi);
160165

161166
moderation = ModerationClient(_moderationRepository);
162167

@@ -174,6 +179,11 @@ class StreamFeedsClientImpl implements StreamFeedsClient {
174179
late final StreamWebSocketClient _ws;
175180
late final ConnectionRecoveryHandler _connectionRecoveryHandler;
176181

182+
late final CdnClient _cdnClient;
183+
184+
@override
185+
late final StreamAttachmentUploader attachmentUploader;
186+
177187
late final ActivitiesRepository _activitiesRepository;
178188
late final AppRepository _appRepository;
179189
late final BookmarksRepository _bookmarksRepository;
@@ -432,14 +442,8 @@ class StreamFeedsClientImpl implements StreamFeedsClient {
432442
}
433443

434444
@override
435-
Future<Result<void>> deleteFile(String url) {
436-
// TODO: implement deleteFile
437-
throw UnimplementedError();
438-
}
445+
Future<Result<void>> deleteFile(String url) => _cdnClient.deleteFile(url);
439446

440447
@override
441-
Future<Result<void>> deleteImage(String url) {
442-
// TODO: implement deleteImage
443-
throw UnimplementedError();
444-
}
448+
Future<Result<void>> deleteImage(String url) => _cdnClient.deleteImage(url);
445449
}

packages/stream_feeds/lib/src/feeds_client.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,12 @@ abstract interface class StreamFeedsClient {
696696
/// Provides access to moderation configurations, content moderation, and moderation-related
697697
/// queries.
698698
ModerationClient get moderation;
699+
700+
/// The attachment uploader for managing file and image uploads.
701+
///
702+
/// Provides functionality for uploading files and images to the Stream CDN with
703+
/// support for various file types, progress tracking, and upload configurations.
704+
StreamAttachmentUploader get attachmentUploader;
699705
}
700706

701707
/// Extension methods for the [StreamFeedsClient] to simplify feed creation.

packages/stream_feeds/lib/src/file/cdn_api.dart

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,32 @@ abstract interface class CdnApi {
1414

1515
@MultiPart()
1616
@POST('/api/v2/uploads/file')
17-
Future<Result<FileUploadResponse>> sendFile({
17+
Future<Result<FileUploadResponse>> uploadFile({
1818
// TODO: change to single file upload once upgrade to retrofit ^4.7.0
1919
@Part(name: 'file') required List<MultipartFile> file,
20-
@SendProgress() ProgressCallback? onSendProgress,
20+
@SendProgress() ProgressCallback? onUploadProgress,
21+
@CancelRequest() CancelToken? cancelToken,
2122
});
2223

2324
@MultiPart()
2425
@POST('/api/v2/uploads/image')
25-
Future<Result<FileUploadResponse>> sendImage({
26+
Future<Result<ImageUploadResponse>> uploadImage({
2627
// TODO: change to single file upload once upgrade to retrofit ^4.7.0
2728
@Part(name: 'file') required List<MultipartFile> file,
28-
@SendProgress() ProgressCallback? onSendProgress,
29+
@SendProgress() ProgressCallback? onUploadProgress,
30+
@CancelRequest() CancelToken? cancelToken,
31+
});
32+
33+
@DELETE('/api/v2/uploads/file')
34+
Future<Result<DurationResponse>> deleteFile({
35+
@Query('url') String? url,
36+
@CancelRequest() CancelToken? cancelToken,
37+
});
38+
39+
@DELETE('/api/v2/uploads/image')
40+
Future<Result<DurationResponse>> deleteImage({
41+
@Query('url') String? url,
42+
@CancelRequest() CancelToken? cancelToken,
2943
});
3044
}
3145

packages/stream_feeds/lib/src/file/cdn_api.g.dart

Lines changed: 112 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import 'package:stream_core/stream_core.dart';
2+
3+
import 'cdn_api.dart';
4+
5+
class FeedsCdnClient implements CdnClient {
6+
const FeedsCdnClient(this._api);
7+
8+
final CdnApi _api;
9+
10+
@override
11+
Future<Result<UploadedFile>> uploadFile(
12+
AttachmentFile file, {
13+
ProgressCallback? onProgress,
14+
CancelToken? cancelToken,
15+
}) async {
16+
final multipartFile = await file.toMultipartFile();
17+
18+
final result = await _api.uploadFile(
19+
file: [multipartFile],
20+
onUploadProgress: onProgress,
21+
cancelToken: cancelToken,
22+
);
23+
24+
return result.map(
25+
(response) => UploadedFile(
26+
fileUrl: response.file,
27+
thumbUrl: response.thumbUrl,
28+
),
29+
);
30+
}
31+
32+
@override
33+
Future<Result<UploadedFile>> uploadImage(
34+
AttachmentFile image, {
35+
ProgressCallback? onProgress,
36+
CancelToken? cancelToken,
37+
}) async {
38+
final multipartFile = await image.toMultipartFile();
39+
40+
final result = await _api.uploadImage(
41+
file: [multipartFile],
42+
onUploadProgress: onProgress,
43+
cancelToken: cancelToken,
44+
);
45+
46+
return result.map(
47+
(response) => UploadedFile(
48+
fileUrl: response.file,
49+
thumbUrl: response.thumbUrl,
50+
),
51+
);
52+
}
53+
54+
@override
55+
Future<Result<void>> deleteFile(
56+
String url, {
57+
CancelToken? cancelToken,
58+
}) async {
59+
final result = await _api.deleteFile(
60+
url: url,
61+
cancelToken: cancelToken,
62+
);
63+
64+
return result;
65+
}
66+
67+
@override
68+
Future<Result<void>> deleteImage(
69+
String url, {
70+
CancelToken? cancelToken,
71+
}) async {
72+
final result = await _api.deleteImage(
73+
url: url,
74+
cancelToken: cancelToken,
75+
);
76+
77+
return result;
78+
}
79+
}

0 commit comments

Comments
 (0)