[data] migrate to bloc management

This commit is contained in:
jscampucci 2024-06-20 08:02:50 +02:00
parent ecb537bf12
commit c25256b13b
22 changed files with 197 additions and 227 deletions

View File

@ -0,0 +1,31 @@
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:notube/services/download.dart';
import 'dart:developer';
part 'dl_form_state.dart';
class DlFormCubit extends Cubit<DlFormState> {
DlFormCubit() : super(DlFormInitial());
late DLServices dlService;
void setFormat(String newFormat) {
emit(state.copyWith(
format: newFormat,
));
print(newFormat);
}
void setUrl(String newUrl) {
emit(state.copyWith(
url: newUrl,
));
}
void download() async {
dlService = await DLServices.init();
await dlService.analyseUrl(state.url!);
}
}

View File

@ -0,0 +1,28 @@
part of 'dl_form_cubit.dart';
class DlFormState extends Equatable {
const DlFormState({
this.url = '',
this.format = 'MP3',
});
final String url;
final String format;
DlFormState copyWith({
String? url,
String? format,
}) {
return DlFormState(
url: url ?? this.url,
format: format ?? this.format,
);
}
@override
List<Object> get props => [url, format];
}
final class DlFormInitial extends DlFormState {
const DlFormInitial();
}

View File

@ -1,17 +1,17 @@
import 'package:flutter/material.dart';
import 'package:notube/components/formComponents/formatDropdown.dart';
import 'package:notube/components/formComponents/submitButton.dart';
import 'package:notube/components/formComponents/urlTextField.dart';
import 'package:notube/states/dlFormState.dart';
import 'package:provider/provider.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:notube/dlForm/cubit/dl_form_cubit.dart';
import 'package:notube/dlForm/formComponents/formatDropdown.dart';
import 'package:notube/dlForm/formComponents/submitButton.dart';
import 'package:notube/dlForm/formComponents/urlTextField.dart';
class DlForm extends StatelessWidget {
const DlForm({super.key});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => DlFormState(),
return BlocProvider(
create: (context) => DlFormCubit(),
child: Form(child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
if (constraints.maxWidth < 320) {
@ -63,13 +63,16 @@ class DebugDlFormState extends StatelessWidget {
@override
Widget build(BuildContext context) {
var dlForm = context.watch<DlFormState>();
return BlocBuilder<DlFormCubit, DlFormState>(builder: (context, state) {
final url = state.url;
final format = state.format;
return Column(
children: <Widget>[
Text('Url: ${dlForm.url}'),
Text('Format: ${dlForm.format}'),
],
);
return Column(
children: <Widget>[
Text('Url: ${url}'),
Text('Format: ${format}'),
],
);
});
}
}

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:notube/constants.dart';
import 'package:provider/provider.dart';
import 'package:notube/states/dlFormState.dart';
import 'package:notube/dlForm/cubit/dl_form_cubit.dart';
const List<String> formatList = <String>[
"MP3",
@ -19,7 +19,7 @@ class DropdownFormat extends StatelessWidget {
@override
Widget build(BuildContext context) {
var dlForm = context.watch<DlFormState>();
final dlFormState = context.select((DlFormCubit cubit) => cubit.state);
return Container(
padding: EdgeInsets.symmetric(
@ -29,12 +29,12 @@ class DropdownFormat extends StatelessWidget {
// dropdown below..
child: DropdownButton<String>(
value: dlForm.format,
value: dlFormState.format,
elevation: 16,
style: const TextStyle(color: colorMainWhite),
dropdownColor: colorMainBlue,
onChanged: (String? value) {
dlForm.setFormat(value!);
context.read<DlFormCubit>().setFormat(value!);
},
underline: Container(),
items: formatList.map<DropdownMenuItem<String>>((String value) {

View File

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:notube/constants.dart';
import 'package:provider/provider.dart';
import 'package:notube/states/dlFormState.dart';
import 'package:notube/dlForm/cubit/dl_form_cubit.dart';
class SubmitButton extends StatefulWidget {
const SubmitButton({super.key});
@ -16,14 +16,13 @@ class _SubmitButtonState extends State<SubmitButton> {
@override
Widget build(BuildContext context) {
var dlForm = context.watch<DlFormState>();
return ConstrainedBox(
constraints: BoxConstraints.tightFor(width: 200),
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () {
dlForm.download();
context.read<DlFormCubit>().download();
},
child: InkWell(
onHover: (hovering) {

View File

@ -2,15 +2,14 @@ import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:notube/constants.dart';
import 'package:provider/provider.dart';
import 'package:notube/states/dlFormState.dart';
import 'package:notube/dlForm/cubit/dl_form_cubit.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class UrlTextField extends StatelessWidget {
const UrlTextField({super.key});
@override
Widget build(BuildContext context) {
var dlForm = context.watch<DlFormState>();
return Flexible(
child: TextFormField(
decoration: InputDecoration(
@ -28,8 +27,8 @@ class UrlTextField extends StatelessWidget {
}
return null;
},
onChanged: (value) {
dlForm.setUrl(value);
onChanged: (newUrl) {
context.read<DlFormCubit>().setUrl(newUrl);
},
),
);

View File

@ -4,11 +4,9 @@ import 'package:notube/wrapper.dart';
import 'package:notube/constants.dart';
import 'package:window_manager/window_manager.dart';
import 'package:upgrader/upgrader.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:redux/redux.dart';
import 'package:notube/store/app.state.dart';
import 'package:notube/store/app.reducer.dart';
import 'package:notube/store/videos/videos.state.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:notube/videoList/cubit/videos_cubit.dart';
import 'package:notube/models/Video.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
@ -26,25 +24,17 @@ void main() async {
await windowManager.focus();
});
final store = Store<AppState>(
appReducer,
initialState: AppState(
videosState: VideosState.initial(),
),
);
runApp(
EasyLocalization(
supportedLocales: [Locale('en', 'US'), Locale('fr', 'FR')],
path: 'lib/translations',
fallbackLocale: Locale('en', 'US'),
child: NoTubeApp(store: store)),
child: NoTubeApp()),
);
}
class NoTubeApp extends StatefulWidget {
final Store<AppState> store;
const NoTubeApp({super.key, required this.store});
const NoTubeApp({super.key});
@override
State<NoTubeApp> createState() => _NoTubeAppState();
@ -61,24 +51,22 @@ class _NoTubeAppState extends State<NoTubeApp> {
@override
Widget build(BuildContext context) {
return StoreProvider<AppState>(
store: widget.store,
child: MaterialApp(
title: 'NoTube',
theme: ThemeData(
fontFamily: 'Poppins',
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: colorMainRed, brightness: Brightness.dark),
appBarTheme: const AppBarTheme(
color: colorBackgroundBlack, elevation: 0),
scaffoldBackgroundColor: colorMainGrey),
localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
home: StoreBuilder<AppState>(
builder: (BuildContext context, Store<AppState> store) =>
UpgradeAlert(upgrader: upgrader, child: const Wrapper()),
)));
return MaterialApp(
title: 'NoTube',
theme: ThemeData(
fontFamily: 'Poppins',
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: colorMainRed, brightness: Brightness.dark),
appBarTheme:
const AppBarTheme(color: colorBackgroundBlack, elevation: 0),
scaffoldBackgroundColor: colorMainGrey),
localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
home: BlocProvider(
create: (context) => VideosCubit(),
child: UpgradeAlert(upgrader: upgrader, child: const Wrapper())),
);
}
}

View File

@ -1,14 +1,6 @@
import 'dart:developer' as developer;
class Video {
String id;
String title;
String thumbnail;
String description;
String duration;
String uploader;
String uploadDate;
import 'package:equatable/equatable.dart';
class Video extends Equatable {
Video({
required this.id,
required this.title,
@ -19,6 +11,14 @@ class Video {
required this.uploadDate,
});
String id;
String title;
String thumbnail;
String description;
String duration;
String uploader;
String uploadDate;
factory Video.fromJson(Map<String, dynamic> json) {
return Video(
id: json['id'],
@ -29,4 +29,8 @@ class Video {
uploader: json['uploader'] ?? '',
uploadDate: json['upload_date'] ?? '');
}
@override
List<Object> get props =>
[id, title, thumbnail, description, duration, uploader, uploadDate];
}

View File

@ -1,7 +1,10 @@
import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:notube/constants.dart';
import 'package:notube/components/dlForm.dart';
import 'package:notube/dlForm/dlForm.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:notube/videoList/cubit/videos_cubit.dart';
import 'package:notube/models/Video.dart';
class Home extends StatelessWidget {
Home({super.key});
@ -28,7 +31,18 @@ class Home extends StatelessWidget {
SizedBox(height: 10),
Padding(
padding: EdgeInsets.symmetric(horizontal: 20),
child: DlForm())
child: DlForm()),
BlocConsumer<VideosCubit, VideosState>(
listener: (context, state) {},
builder: (context, state) {
return Column(
children: <Widget>[
for (Video video
in context.read<VideosCubit>().videoList)
Text(video.title),
],
);
}),
],
),
),

View File

@ -4,10 +4,10 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:notube/models/Video.dart';
import 'package:notube/states/dlFormState.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:notube/videoList/cubit/videos_cubit.dart';
import 'package:path_provider/path_provider.dart';
import 'package:process_run/shell.dart';
import 'package:provider/provider.dart';
import 'dart:developer';

View File

@ -1,25 +0,0 @@
import 'package:flutter/material.dart';
import 'package:notube/services/download.dart';
class DlFormState extends ChangeNotifier {
String url =
'https://youtu.be/playlist?list=PLk1fi9OrZmvGBdh9BdWIhZImGVDUqls1X';
String format = 'MP3';
late DLServices dlService;
void setUrl(String newUrl) {
url = newUrl;
notifyListeners();
}
void setFormat(String newFormat) {
format = newFormat;
notifyListeners();
}
Future<void> download() async {
dlService = await DLServices.init();
await dlService.analyseUrl(url);
}
}

View File

@ -1,12 +0,0 @@
import 'package:redux/redux.dart';
import './app.state.dart';
List<Middleware<AppState>> appMiddleware() {
// final Middleware<AppState> _login = login(_repo);
return [
// TypedMiddleware<AppState, LoginAction>(_login),
];
}

View File

@ -1,7 +0,0 @@
import 'package:notube/store/videos/videos.reducer.dart';
import './app.state.dart';
AppState appReducer(AppState state, action) => AppState(
videosState: videosReducer(state.videosState, action),
);

View File

@ -1,28 +0,0 @@
import 'package:notube/store/videos/videos.state.dart';
import 'package:flutter/material.dart';
class AppState {
final VideosState videosState;
AppState({required this.videosState});
factory AppState.initial() => AppState(
videosState: VideosState.initial(),
);
@override
bool operator ==(other) =>
identical(this, other) ||
other is AppState &&
runtimeType == other.runtimeType &&
videosState == other.videosState;
@override
int get hashCode => super.hashCode ^ videosState.hashCode;
@override
String toString() {
return "AppState { videosState: $videosState }";
}
}

View File

@ -1,32 +0,0 @@
import 'package:flutter/material.dart';
class VideosAction {
@override
String toString() {
return 'VideosAction { }';
}
}
class VideosSuccessAction {
final int isSuccess;
VideosSuccessAction({required this.isSuccess});
@override
String toString() {
return 'VideosSuccessAction { isSuccess: $isSuccess }';
}
}
class VideosFailedAction {
final String error;
VideosFailedAction({required this.error});
@override
String toString() {
return 'VideosFailedAction { error: $error }';
}
}

View File

@ -1,15 +0,0 @@
import 'package:redux/redux.dart';
import 'package:notube/store/app.state.dart';
import 'package:notube/models/Video.dart';
Middleware<AppState> getVideos(Video _video) {
return (Store<AppState> store, action, NextDispatcher dispatch) async {
dispatch(action);
try {
// TODO: Write here your middleware logic and api calls
} catch (error) {
// TODO: API Error handling
print(error);
}
};
}

View File

@ -1,4 +0,0 @@
import 'package:redux/redux.dart';
import 'videos.state.dart';
final videosReducer = combineReducers<VideosState>([]);

View File

@ -1,28 +0,0 @@
class VideosState {
final bool loading;
final String error;
VideosState(this.loading, this.error);
factory VideosState.initial() => VideosState(false, '');
VideosState copyWith({bool? loading, String? error}) =>
VideosState(loading ?? this.loading, error ?? this.error);
@override
bool operator ==(other) =>
identical(this, other) ||
other is VideosState &&
runtimeType == other.runtimeType &&
loading == other.loading &&
error == other.error;
@override
int get hashCode =>
super.hashCode ^ runtimeType.hashCode ^ loading.hashCode ^ error.hashCode;
@override
String toString() => "VideosState { loading: $loading, error: $error}";
}

View File

@ -0,0 +1,27 @@
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:notube/models/Video.dart';
part 'videos_state.dart';
class VideosCubit extends Cubit<VideosState> {
VideosCubit() : super(VideosState());
final List<Video> _videoList = [];
List<Video> get videoList => _videoList;
void addVideo(Video video) {
_videoList.add(video);
emit(VideosState(videoList: _videoList));
}
void clearVideos() {
_videoList.clear();
emit(VideosState(videoList: _videoList));
}
void removeVideo(Video video) {
_videoList.remove(video);
emit(VideosState(videoList: _videoList));
}
}

View File

@ -0,0 +1,10 @@
part of 'videos_cubit.dart';
final class VideosState extends Equatable {
final List<Video> videoList;
VideosState({this.videoList = const []});
@override
List<Object> get props => [videoList];
}

View File

@ -105,6 +105,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.0.0"
equatable:
dependency: "direct main"
description:
name: equatable
sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2
url: "https://pub.dev"
source: hosted
version: "2.0.5"
fake_async:
dependency: transitive
description:
@ -221,6 +229,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.19.0"
json_annotation:
dependency: "direct main"
description:
name: json_annotation
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
url: "https://pub.dev"
source: hosted
version: "4.9.0"
leak_tracker:
dependency: transitive
description:

View File

@ -6,7 +6,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
version: 0.0.1+1
environment:
sdk: ">=2.19.4 <4.0.0"
sdk: ">=3.0.0 <4.0.0"
dependencies:
flutter:
@ -25,6 +25,8 @@ dependencies:
redux: ^5.0.0
redux_thunk: ^0.4.0
flutter_bloc: ^8.1.6
equatable: ^2.0.5
json_annotation: ^4.9.0
dev_dependencies:
flutter_test: