Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,22 @@ import 'package:flutter/foundation.dart';
/// - Does NOT return domain entities, only data models

class UploadTrackRemoteDataSource {
UploadTrackRemoteDataSource({
Dio? dio,
Future<MultipartFile> Function(String path, DioMediaType contentType)?
multipartFileFactory,
}) : _dio = dio ?? apiClient.dio,
_multipartFileFactory =
multipartFileFactory ??
((path, contentType) {
return MultipartFile.fromFile(path, contentType: contentType);
});

// Uses the shared ApiClient your team leader built
// Auth token is attached automatically — you don't touch it here
final Dio _dio = apiClient.dio;
final Dio _dio;
final Future<MultipartFile> Function(String path, DioMediaType contentType)
_multipartFileFactory;

// ── Fetch Tags ─────────────────────────────────────────────────────────────

Expand Down Expand Up @@ -85,10 +98,10 @@ class UploadTrackRemoteDataSource {
'genre': genre,
'is_public': isPublic, // Pass as boolean
'is_hidden': isHidden,
'audio_file': await MultipartFile.fromFile(
'audio_file': await _multipartFileFactory(
// spec: 'audio_file'
audioFile.path,
contentType: DioMediaType.parse(audioMime),
DioMediaType.parse(audioMime),
),
};

Expand All @@ -103,10 +116,10 @@ class UploadTrackRemoteDataSource {
// Cover image — spec: 'cover_image'
if (artworkFile != null) {
final artMime = lookupMimeType(artworkFile.path) ?? 'image/jpeg';
fields['cover_image'] = await MultipartFile.fromFile(
fields['cover_image'] = await _multipartFileFactory(
// spec: 'cover_image'
artworkFile.path,
contentType: DioMediaType.parse(artMime),
DioMediaType.parse(artMime),
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,16 @@ import 'package:rythmify/features/track_upload/domain/repositories/upload_track_

class UploadTrackUseCase {
final UploadTrackRepository repository;
final File Function(String path) fileFactory;
final bool Function(File file) fileExists;

const UploadTrackUseCase(this.repository);
const UploadTrackUseCase(
this.repository, {
this.fileFactory = File.new,
bool Function(File file)? fileExists,
}) : fileExists = fileExists ?? _defaultFileExists;

static bool _defaultFileExists(File file) => file.existsSync();

Future<Either<Failure, String>> call({
required TrackDraft draft,
Expand All @@ -47,7 +55,7 @@ class UploadTrackUseCase {

// Double-check audio file still exists on device
//commented for teting will uncomment later
if (!audioFile.existsSync()) {
if (!fileExists(audioFile)) {
return const Left(
FileFailure('Audio file no longer exists on your device.'),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,8 @@ class UploadFormNotifier extends Notifier<UploadFormState> {

//added in backend integration phase
Future<void> startUpload({
required WidgetRef ref,
WidgetRef? ref,
UploadTrackUseCase? useCaseOverride,
required void Function(String trackId) onSuccess,
required void Function(String error) onError,
}) async {
Expand All @@ -224,8 +225,14 @@ class UploadFormNotifier extends Notifier<UploadFormState> {
draft.copyWith(status: UploadStatus.uploading, uploadProgress: 0.0),
);

// Get the usecase
final useCase = ref.read(uploadUseCaseProvider);
// Get the usecase. Production passes WidgetRef; tests can inject the
// use case directly because WidgetRef is sealed.
final useCase = useCaseOverride ?? ref?.read(uploadUseCaseProvider);
if (useCase == null) {
_updateDraft(state.draft!.copyWith(status: UploadStatus.error));
onError('Upload service unavailable.');
return;
}

// Call usecase with draft and progress callback
final result = await useCase(
Expand Down Expand Up @@ -257,9 +264,14 @@ class UploadFormNotifier extends Notifier<UploadFormState> {
}

//added in back integration to fix 400 error when ftetching genres
Future<void> fetchGenres(WidgetRef ref) async {
Future<void> fetchGenres([
WidgetRef? ref,
UploadTrackRemoteDataSource? dataSourceOverride,
]) async {
try {
final dataSource = ref.read(_uploadDataSourceProvider);
final dataSource =
dataSourceOverride ?? ref?.read(_uploadDataSourceProvider);
if (dataSource == null) return;
final genres = await dataSource.fetchGenres();
state = state.copyWith(availableGenres: genres);
debugPrint('Genres loaded: $genres');
Expand All @@ -268,9 +280,14 @@ class UploadFormNotifier extends Notifier<UploadFormState> {
}
}

Future<void> fetchTags(WidgetRef ref) async {
Future<void> fetchTags([
WidgetRef? ref,
UploadTrackRemoteDataSource? dataSourceOverride,
]) async {
try {
final dataSource = ref.read(_uploadDataSourceProvider);
final dataSource =
dataSourceOverride ?? ref?.read(_uploadDataSourceProvider);
if (dataSource == null) return;
final tags = await dataSource.fetchTags();
state = state.copyWith(availableTags: tags);
debugPrint('Tags loaded: $tags');
Expand Down Expand Up @@ -305,7 +322,9 @@ class UploadFormNotifier extends Notifier<UploadFormState> {
}

Future<void> handleUpdate({
required WidgetRef ref,
WidgetRef? ref,
Future<void> Function(String trackId, Map<String, dynamic> data)?
updateOverride,
required void Function() onSuccess,
required void Function(String error) onError,
}) async {
Expand All @@ -314,7 +333,20 @@ class UploadFormNotifier extends Notifier<UploadFormState> {

state = state.copyWith(isLoading: true);

final updateTrack = ref.read(updateTrackUseCaseProvider);
final updateTrack =
updateOverride ??
(ref == null
? null
: (String trackId, Map<String, dynamic> data) =>
ref.read(updateTrackUseCaseProvider).call(trackId, data));
if (updateTrack == null) {
state = state.copyWith(
isLoading: false,
errorMessage: 'Update service unavailable.',
);
onError('Update service unavailable.');
return;
}

final Map<String, dynamic> data = {
"title": draft.title,
Expand All @@ -330,7 +362,7 @@ class UploadFormNotifier extends Notifier<UploadFormState> {
};

try {
await updateTrack.call(draft.trackId!, data);
await updateTrack(draft.trackId!, data);
state = state.copyWith(isLoading: false);
onSuccess();
} catch (e) {
Expand All @@ -340,7 +372,8 @@ class UploadFormNotifier extends Notifier<UploadFormState> {
}

Future<void> handleDelete({
required WidgetRef ref,
WidgetRef? ref,
Future<void> Function(String trackId)? deleteOverride,
required void Function() onSuccess,
required void Function(String error) onError,
}) async {
Expand All @@ -349,10 +382,23 @@ class UploadFormNotifier extends Notifier<UploadFormState> {

state = state.copyWith(isLoading: true);

final deleteTrack = ref.read(deleteTrackUseCaseProvider);
final deleteTrack =
deleteOverride ??
(ref == null
? null
: (String trackId) =>
ref.read(deleteTrackUseCaseProvider).call(trackId));
if (deleteTrack == null) {
state = state.copyWith(
isLoading: false,
errorMessage: 'Delete service unavailable.',
);
onError('Delete service unavailable.');
return;
}

try {
await deleteTrack.call(draft.trackId!);
await deleteTrack(draft.trackId!);
state = state.copyWith(isLoading: false);
onSuccess();
} catch (e) {
Expand Down
Loading
Loading