claude refactor
This commit is contained in:
@@ -25,8 +25,9 @@ class LoadableStateConsumer<TController extends Bloc<LoadableHydratedBlocEvent<T
|
||||
Widget build(BuildContext context) {
|
||||
var loadableState = context.watch<TController>().state;
|
||||
|
||||
if(!loadableState.isLoading && onLoad != null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) => onLoad!(loadableState.data!));
|
||||
final loadedData = loadableState.data;
|
||||
if(!loadableState.isLoading && onLoad != null && loadedData is TState) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) => onLoad!(loadedData));
|
||||
}
|
||||
|
||||
var childWidget = ConditionalWrapper(
|
||||
@@ -47,8 +48,8 @@ class LoadableStateConsumer<TController extends Bloc<LoadableHydratedBlocEvent<T
|
||||
),
|
||||
child: SizedBox(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
child: loadableState.showContent()
|
||||
? child(loadableState.data!, loadableState.isLoading)
|
||||
child: loadableState.showContent() && loadedData is TState
|
||||
? child(loadedData, loadableState.isLoading)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -14,8 +14,8 @@ class BlocModule<TBloc extends StateStreamableSource<TState>, TState> extends St
|
||||
@override
|
||||
Widget build(BuildContext context) => BlocProvider<TBloc>(
|
||||
create: (context) {
|
||||
var bloc = create(context);
|
||||
this.onInitialisation != null ? this.onInitialisation!(context, bloc) : null;
|
||||
final bloc = create(context);
|
||||
onInitialisation?.call(context, bloc);
|
||||
return bloc;
|
||||
},
|
||||
child: Builder(
|
||||
|
||||
+2
-1
@@ -103,7 +103,8 @@ abstract class LoadableHydratedBloc<
|
||||
Map<String, dynamic>? toJson(LoadableState<TState> state) {
|
||||
Map<String, dynamic>? data;
|
||||
try {
|
||||
data = state.data == null ? null : toStorage(state.data!);
|
||||
final stateData = state.data;
|
||||
data = stateData is TState ? toStorage(stateData) : null;
|
||||
} catch(e) {
|
||||
log('Failed to save state ${TState.toString()}: ${e.toString()}');
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import 'account_event.dart';
|
||||
import 'account_state.dart';
|
||||
|
||||
class AccountBloc extends Bloc<AccountEvent, AccountState> {
|
||||
AccountBloc() : super(const AccountState()) {
|
||||
on<AccountStatusChanged>((event, emit) => emit(state.copyWith(status: event.status)));
|
||||
}
|
||||
|
||||
void setStatus(AccountStatus status) => add(AccountStatusChanged(status));
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import 'account_state.dart';
|
||||
|
||||
sealed class AccountEvent {
|
||||
const AccountEvent();
|
||||
}
|
||||
|
||||
class AccountStatusChanged extends AccountEvent {
|
||||
final AccountStatus status;
|
||||
const AccountStatusChanged(this.status);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
enum AccountStatus { undefined, loggedIn, loggedOut }
|
||||
|
||||
class AccountState {
|
||||
final AccountStatus status;
|
||||
const AccountState({this.status = AccountStatus.undefined});
|
||||
|
||||
AccountState copyWith({AccountStatus? status}) => AccountState(status: status ?? this.status);
|
||||
}
|
||||
@@ -1,16 +1,18 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../../api/mhsl/breaker/getBreakers/getBreakersResponse.dart';
|
||||
import '../../../model/breakers/Breaker.dart';
|
||||
import '../../../model/chatList/chatListProps.dart';
|
||||
import '../../../storage/base/settingsProvider.dart';
|
||||
import '../../../widget/breaker/breaker.dart';
|
||||
import '../../../view/pages/files/files.dart';
|
||||
import '../../../view/pages/more/roomplan/roomplan.dart';
|
||||
import '../../../view/pages/talk/chatList.dart';
|
||||
import '../../../view/pages/timetable/timetable.dart';
|
||||
import '../../../widget/centeredLeading.dart';
|
||||
import 'chatList/bloc/chat_list_bloc.dart';
|
||||
import 'chatList/bloc/chat_list_state.dart';
|
||||
import 'settings/bloc/settings_cubit.dart';
|
||||
import '../infrastructure/loadableState/loadable_state.dart';
|
||||
import 'gradeAverages/view/grade_averages_view.dart';
|
||||
import 'holidays/view/holidays_view.dart';
|
||||
import 'marianumMessage/view/marianum_message_list_view.dart';
|
||||
@@ -27,7 +29,7 @@ class AppModule {
|
||||
AppModule(this.module, {required this.name, required this.icon, this.breakerArea = BreakerArea.global, required this.create});
|
||||
|
||||
static Map<Modules, AppModule> modules(BuildContext context, { showFiltered = false }) {
|
||||
var settings = Provider.of<SettingsProvider>(context, listen: false);
|
||||
var settings = context.read<SettingsCubit>();
|
||||
var available = {
|
||||
Modules.timetable: AppModule(
|
||||
Modules.timetable,
|
||||
@@ -39,10 +41,11 @@ class AppModule {
|
||||
Modules.talk: AppModule(
|
||||
Modules.talk,
|
||||
name: 'Talk',
|
||||
icon: () => Consumer<ChatListProps>(
|
||||
builder: (context, value, child) {
|
||||
if(value.primaryLoading()) return Icon(Icons.chat);
|
||||
var messages = value.getRoomsResponse.data.map((e) => e.unreadMessages).reduce((a, b) => a+b);
|
||||
icon: () => BlocBuilder<ChatListBloc, LoadableState<ChatListState>>(
|
||||
builder: (context, state) {
|
||||
final rooms = state.data?.rooms;
|
||||
if (rooms == null || rooms.data.isEmpty) return const Icon(Icons.chat);
|
||||
final messages = rooms.data.map((e) => e.unreadMessages).reduce((a, b) => a + b);
|
||||
return badges.Badge(
|
||||
showBadge: messages > 0,
|
||||
position: badges.BadgePosition.topEnd(top: -3, end: -3),
|
||||
@@ -53,7 +56,7 @@ class AppModule {
|
||||
elevation: 1,
|
||||
),
|
||||
badgeContent: Text('$messages', style: const TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold)),
|
||||
child: Icon(Icons.chat),
|
||||
child: const Icon(Icons.chat),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
|
||||
import '../../../../../api/mhsl/breaker/getBreakers/getBreakersResponse.dart';
|
||||
import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc.dart';
|
||||
import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart';
|
||||
import '../repository/breaker_repository.dart';
|
||||
import 'breaker_event.dart';
|
||||
import 'breaker_state.dart';
|
||||
|
||||
class BreakerBloc extends LoadableHydratedBloc<BreakerEvent, BreakerState, BreakerRepository> {
|
||||
PackageInfo? _packageInfo;
|
||||
|
||||
@override
|
||||
BreakerRepository repository() => BreakerRepository();
|
||||
|
||||
@override
|
||||
BreakerState fromNothing() => const BreakerState();
|
||||
|
||||
@override
|
||||
BreakerState fromStorage(Map<String, dynamic> json) => BreakerState.fromJson(json);
|
||||
|
||||
@override
|
||||
Map<String, dynamic>? toStorage(BreakerState state) => state.toJson();
|
||||
|
||||
@override
|
||||
Future<void> gatherData() async {
|
||||
_packageInfo ??= await PackageInfo.fromPlatform();
|
||||
final response = await repo.data.getBreakers();
|
||||
add(DataGathered((s) => s.copyWith(response: response)));
|
||||
}
|
||||
|
||||
void refresh() => fetch();
|
||||
|
||||
String? isBlocked(BreakerArea? type) {
|
||||
if (kDebugMode) return null;
|
||||
final response = innerState?.response;
|
||||
if (response == null || _packageInfo == null) return null;
|
||||
|
||||
if (response.global.areas.contains(type)) return response.global.message;
|
||||
|
||||
final selfBuild = int.parse(_packageInfo!.buildNumber);
|
||||
for (final entry in response.regional.entries) {
|
||||
final affectedBuild = int.parse(entry.key.split('b')[1]);
|
||||
if (affectedBuild >= selfBuild && entry.value.areas.contains(type)) {
|
||||
return entry.value.message;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart';
|
||||
import 'breaker_state.dart';
|
||||
|
||||
sealed class BreakerEvent extends LoadableHydratedBlocEvent<BreakerState> {}
|
||||
@@ -0,0 +1,15 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import '../../../../../api/mhsl/breaker/getBreakers/getBreakersResponse.dart';
|
||||
|
||||
part 'breaker_state.freezed.dart';
|
||||
part 'breaker_state.g.dart';
|
||||
|
||||
@freezed
|
||||
abstract class BreakerState with _$BreakerState {
|
||||
const factory BreakerState({
|
||||
GetBreakersResponse? response,
|
||||
}) = _BreakerState;
|
||||
|
||||
factory BreakerState.fromJson(Map<String, Object?> json) => _$BreakerStateFromJson(json);
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'breaker_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
/// @nodoc
|
||||
mixin _$BreakerState {
|
||||
|
||||
GetBreakersResponse? get response;
|
||||
/// Create a copy of BreakerState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$BreakerStateCopyWith<BreakerState> get copyWith => _$BreakerStateCopyWithImpl<BreakerState>(this as BreakerState, _$identity);
|
||||
|
||||
/// Serializes this BreakerState to a JSON map.
|
||||
Map<String, dynamic> toJson();
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is BreakerState&&(identical(other.response, response) || other.response == response));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,response);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'BreakerState(response: $response)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $BreakerStateCopyWith<$Res> {
|
||||
factory $BreakerStateCopyWith(BreakerState value, $Res Function(BreakerState) _then) = _$BreakerStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
GetBreakersResponse? response
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$BreakerStateCopyWithImpl<$Res>
|
||||
implements $BreakerStateCopyWith<$Res> {
|
||||
_$BreakerStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final BreakerState _self;
|
||||
final $Res Function(BreakerState) _then;
|
||||
|
||||
/// Create a copy of BreakerState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? response = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
response: freezed == response ? _self.response : response // ignore: cast_nullable_to_non_nullable
|
||||
as GetBreakersResponse?,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [BreakerState].
|
||||
extension BreakerStatePatterns on BreakerState {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _BreakerState value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _BreakerState() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _BreakerState value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _BreakerState():
|
||||
return $default(_that);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _BreakerState value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _BreakerState() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( GetBreakersResponse? response)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _BreakerState() when $default != null:
|
||||
return $default(_that.response);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( GetBreakersResponse? response) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _BreakerState():
|
||||
return $default(_that.response);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( GetBreakersResponse? response)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _BreakerState() when $default != null:
|
||||
return $default(_that.response);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
|
||||
class _BreakerState implements BreakerState {
|
||||
const _BreakerState({this.response});
|
||||
factory _BreakerState.fromJson(Map<String, dynamic> json) => _$BreakerStateFromJson(json);
|
||||
|
||||
@override final GetBreakersResponse? response;
|
||||
|
||||
/// Create a copy of BreakerState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$BreakerStateCopyWith<_BreakerState> get copyWith => __$BreakerStateCopyWithImpl<_BreakerState>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$BreakerStateToJson(this, );
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _BreakerState&&(identical(other.response, response) || other.response == response));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,response);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'BreakerState(response: $response)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$BreakerStateCopyWith<$Res> implements $BreakerStateCopyWith<$Res> {
|
||||
factory _$BreakerStateCopyWith(_BreakerState value, $Res Function(_BreakerState) _then) = __$BreakerStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
GetBreakersResponse? response
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$BreakerStateCopyWithImpl<$Res>
|
||||
implements _$BreakerStateCopyWith<$Res> {
|
||||
__$BreakerStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _BreakerState _self;
|
||||
final $Res Function(_BreakerState) _then;
|
||||
|
||||
/// Create a copy of BreakerState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? response = freezed,}) {
|
||||
return _then(_BreakerState(
|
||||
response: freezed == response ? _self.response : response // ignore: cast_nullable_to_non_nullable
|
||||
as GetBreakersResponse?,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
@@ -0,0 +1,19 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'breaker_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_BreakerState _$BreakerStateFromJson(Map<String, dynamic> json) =>
|
||||
_BreakerState(
|
||||
response: json['response'] == null
|
||||
? null
|
||||
: GetBreakersResponse.fromJson(
|
||||
json['response'] as Map<String, dynamic>,
|
||||
),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$BreakerStateToJson(_BreakerState instance) =>
|
||||
<String, dynamic>{'response': instance.response};
|
||||
@@ -0,0 +1,14 @@
|
||||
import 'dart:async';
|
||||
|
||||
import '../../../../../api/mhsl/breaker/getBreakers/getBreakersCache.dart';
|
||||
import '../../../../../api/mhsl/breaker/getBreakers/getBreakersResponse.dart';
|
||||
|
||||
class BreakerDataProvider {
|
||||
Future<GetBreakersResponse> getBreakers() {
|
||||
final completer = Completer<GetBreakersResponse>();
|
||||
GetBreakersCache(onUpdate: (data) {
|
||||
if (!completer.isCompleted) completer.complete(data);
|
||||
});
|
||||
return completer.future;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import '../../../infrastructure/repository/repository.dart';
|
||||
import '../bloc/breaker_state.dart';
|
||||
import '../dataProvider/breaker_data_provider.dart';
|
||||
|
||||
class BreakerRepository extends Repository<BreakerState> {
|
||||
final BreakerDataProvider _provider;
|
||||
|
||||
BreakerRepository([BreakerDataProvider? provider]) : _provider = provider ?? BreakerDataProvider();
|
||||
|
||||
BreakerDataProvider get data => _provider;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc.dart';
|
||||
import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart';
|
||||
import '../repository/chat_repository.dart';
|
||||
import 'chat_event.dart';
|
||||
import 'chat_state.dart';
|
||||
|
||||
class ChatBloc extends LoadableHydratedBloc<ChatEvent, ChatState, ChatRepository> {
|
||||
DateTime _lastTokenSet = DateTime.fromMillisecondsSinceEpoch(0);
|
||||
|
||||
@override
|
||||
ChatRepository repository() => ChatRepository();
|
||||
|
||||
@override
|
||||
ChatState fromNothing() => const ChatState();
|
||||
|
||||
@override
|
||||
ChatState fromStorage(Map<String, dynamic> json) => ChatState.fromJson(json);
|
||||
|
||||
@override
|
||||
Map<String, dynamic>? toStorage(ChatState state) => state.toJson();
|
||||
|
||||
@override
|
||||
Future<void> gatherData() async {
|
||||
final token = innerState?.currentToken ?? '';
|
||||
if (token.isEmpty) return;
|
||||
_loadChat(token);
|
||||
}
|
||||
|
||||
void setToken(String token) {
|
||||
if (token == (innerState?.currentToken ?? '')) {
|
||||
refresh();
|
||||
return;
|
||||
}
|
||||
add(Emit((s) => s.copyWith(currentToken: token, chatResponse: null)));
|
||||
_loadChat(token);
|
||||
}
|
||||
|
||||
void setReferenceMessageId(int? messageId) {
|
||||
add(Emit((s) => s.copyWith(referenceMessageId: messageId)));
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
final token = innerState?.currentToken ?? '';
|
||||
if (token.isNotEmpty) _loadChat(token);
|
||||
}
|
||||
|
||||
void _loadChat(String token) {
|
||||
final requestStart = DateTime.now();
|
||||
_lastTokenSet = requestStart;
|
||||
repo.data.getChat(
|
||||
token: token,
|
||||
onUpdate: (data) {
|
||||
if (_lastTokenSet.isAfter(requestStart)) return;
|
||||
if ((innerState?.currentToken ?? '') != token) return;
|
||||
add(DataGathered((s) => s.copyWith(chatResponse: data)));
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart';
|
||||
import 'chat_state.dart';
|
||||
|
||||
sealed class ChatEvent extends LoadableHydratedBlocEvent<ChatState> {}
|
||||
@@ -0,0 +1,17 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import '../../../../../api/marianumcloud/talk/chat/getChatResponse.dart';
|
||||
|
||||
part 'chat_state.freezed.dart';
|
||||
part 'chat_state.g.dart';
|
||||
|
||||
@freezed
|
||||
abstract class ChatState with _$ChatState {
|
||||
const factory ChatState({
|
||||
@Default('') String currentToken,
|
||||
GetChatResponse? chatResponse,
|
||||
int? referenceMessageId,
|
||||
}) = _ChatState;
|
||||
|
||||
factory ChatState.fromJson(Map<String, Object?> json) => _$ChatStateFromJson(json);
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'chat_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
/// @nodoc
|
||||
mixin _$ChatState {
|
||||
|
||||
String get currentToken; GetChatResponse? get chatResponse; int? get referenceMessageId;
|
||||
/// Create a copy of ChatState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$ChatStateCopyWith<ChatState> get copyWith => _$ChatStateCopyWithImpl<ChatState>(this as ChatState, _$identity);
|
||||
|
||||
/// Serializes this ChatState to a JSON map.
|
||||
Map<String, dynamic> toJson();
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is ChatState&&(identical(other.currentToken, currentToken) || other.currentToken == currentToken)&&(identical(other.chatResponse, chatResponse) || other.chatResponse == chatResponse)&&(identical(other.referenceMessageId, referenceMessageId) || other.referenceMessageId == referenceMessageId));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,currentToken,chatResponse,referenceMessageId);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ChatState(currentToken: $currentToken, chatResponse: $chatResponse, referenceMessageId: $referenceMessageId)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $ChatStateCopyWith<$Res> {
|
||||
factory $ChatStateCopyWith(ChatState value, $Res Function(ChatState) _then) = _$ChatStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String currentToken, GetChatResponse? chatResponse, int? referenceMessageId
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$ChatStateCopyWithImpl<$Res>
|
||||
implements $ChatStateCopyWith<$Res> {
|
||||
_$ChatStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final ChatState _self;
|
||||
final $Res Function(ChatState) _then;
|
||||
|
||||
/// Create a copy of ChatState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? currentToken = null,Object? chatResponse = freezed,Object? referenceMessageId = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
currentToken: null == currentToken ? _self.currentToken : currentToken // ignore: cast_nullable_to_non_nullable
|
||||
as String,chatResponse: freezed == chatResponse ? _self.chatResponse : chatResponse // ignore: cast_nullable_to_non_nullable
|
||||
as GetChatResponse?,referenceMessageId: freezed == referenceMessageId ? _self.referenceMessageId : referenceMessageId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [ChatState].
|
||||
extension ChatStatePatterns on ChatState {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ChatState value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _ChatState() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ChatState value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _ChatState():
|
||||
return $default(_that);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ChatState value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _ChatState() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String currentToken, GetChatResponse? chatResponse, int? referenceMessageId)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ChatState() when $default != null:
|
||||
return $default(_that.currentToken,_that.chatResponse,_that.referenceMessageId);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String currentToken, GetChatResponse? chatResponse, int? referenceMessageId) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ChatState():
|
||||
return $default(_that.currentToken,_that.chatResponse,_that.referenceMessageId);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String currentToken, GetChatResponse? chatResponse, int? referenceMessageId)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ChatState() when $default != null:
|
||||
return $default(_that.currentToken,_that.chatResponse,_that.referenceMessageId);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
|
||||
class _ChatState implements ChatState {
|
||||
const _ChatState({this.currentToken = '', this.chatResponse, this.referenceMessageId});
|
||||
factory _ChatState.fromJson(Map<String, dynamic> json) => _$ChatStateFromJson(json);
|
||||
|
||||
@override@JsonKey() final String currentToken;
|
||||
@override final GetChatResponse? chatResponse;
|
||||
@override final int? referenceMessageId;
|
||||
|
||||
/// Create a copy of ChatState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$ChatStateCopyWith<_ChatState> get copyWith => __$ChatStateCopyWithImpl<_ChatState>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$ChatStateToJson(this, );
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ChatState&&(identical(other.currentToken, currentToken) || other.currentToken == currentToken)&&(identical(other.chatResponse, chatResponse) || other.chatResponse == chatResponse)&&(identical(other.referenceMessageId, referenceMessageId) || other.referenceMessageId == referenceMessageId));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,currentToken,chatResponse,referenceMessageId);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ChatState(currentToken: $currentToken, chatResponse: $chatResponse, referenceMessageId: $referenceMessageId)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$ChatStateCopyWith<$Res> implements $ChatStateCopyWith<$Res> {
|
||||
factory _$ChatStateCopyWith(_ChatState value, $Res Function(_ChatState) _then) = __$ChatStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String currentToken, GetChatResponse? chatResponse, int? referenceMessageId
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$ChatStateCopyWithImpl<$Res>
|
||||
implements _$ChatStateCopyWith<$Res> {
|
||||
__$ChatStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _ChatState _self;
|
||||
final $Res Function(_ChatState) _then;
|
||||
|
||||
/// Create a copy of ChatState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? currentToken = null,Object? chatResponse = freezed,Object? referenceMessageId = freezed,}) {
|
||||
return _then(_ChatState(
|
||||
currentToken: null == currentToken ? _self.currentToken : currentToken // ignore: cast_nullable_to_non_nullable
|
||||
as String,chatResponse: freezed == chatResponse ? _self.chatResponse : chatResponse // ignore: cast_nullable_to_non_nullable
|
||||
as GetChatResponse?,referenceMessageId: freezed == referenceMessageId ? _self.referenceMessageId : referenceMessageId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
@@ -0,0 +1,22 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'chat_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_ChatState _$ChatStateFromJson(Map<String, dynamic> json) => _ChatState(
|
||||
currentToken: json['currentToken'] as String? ?? '',
|
||||
chatResponse: json['chatResponse'] == null
|
||||
? null
|
||||
: GetChatResponse.fromJson(json['chatResponse'] as Map<String, dynamic>),
|
||||
referenceMessageId: (json['referenceMessageId'] as num?)?.toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ChatStateToJson(_ChatState instance) =>
|
||||
<String, dynamic>{
|
||||
'currentToken': instance.currentToken,
|
||||
'chatResponse': instance.chatResponse,
|
||||
'referenceMessageId': instance.referenceMessageId,
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
import '../../../../../api/marianumcloud/talk/chat/getChatCache.dart';
|
||||
import '../../../../../api/marianumcloud/talk/chat/getChatResponse.dart';
|
||||
|
||||
class ChatDataProvider {
|
||||
void getChat({
|
||||
required String token,
|
||||
required void Function(GetChatResponse data) onUpdate,
|
||||
}) {
|
||||
GetChatCache(chatToken: token, onUpdate: onUpdate);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import '../../../infrastructure/repository/repository.dart';
|
||||
import '../bloc/chat_state.dart';
|
||||
import '../dataProvider/chat_data_provider.dart';
|
||||
|
||||
class ChatRepository extends Repository<ChatState> {
|
||||
final ChatDataProvider _provider;
|
||||
|
||||
ChatRepository([ChatDataProvider? provider]) : _provider = provider ?? ChatDataProvider();
|
||||
|
||||
ChatDataProvider get data => _provider;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import 'package:flutter_app_badge/flutter_app_badge.dart';
|
||||
|
||||
import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc.dart';
|
||||
import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart';
|
||||
import '../repository/chat_list_repository.dart';
|
||||
import 'chat_list_event.dart';
|
||||
import 'chat_list_state.dart';
|
||||
|
||||
class ChatListBloc extends LoadableHydratedBloc<ChatListEvent, ChatListState, ChatListRepository> {
|
||||
@override
|
||||
ChatListRepository repository() => ChatListRepository();
|
||||
|
||||
@override
|
||||
ChatListState fromNothing() => const ChatListState();
|
||||
|
||||
@override
|
||||
ChatListState fromStorage(Map<String, dynamic> json) => ChatListState.fromJson(json);
|
||||
|
||||
@override
|
||||
Map<String, dynamic>? toStorage(ChatListState state) => state.toJson();
|
||||
|
||||
@override
|
||||
Future<void> gatherData() async {
|
||||
final rooms = await repo.data.getRooms();
|
||||
add(DataGathered((s) => s.copyWith(rooms: rooms)));
|
||||
_updateAppBadge(rooms);
|
||||
}
|
||||
|
||||
Future<void> refresh({bool renew = true}) async {
|
||||
final rooms = await repo.data.getRooms(renew: renew);
|
||||
add(DataGathered((s) => s.copyWith(rooms: rooms)));
|
||||
_updateAppBadge(rooms);
|
||||
}
|
||||
|
||||
Future<void> createDirectChat(String invite) async {
|
||||
await repo.data.createDirectRoom(invite);
|
||||
await refresh();
|
||||
}
|
||||
|
||||
void _updateAppBadge(dynamic rooms) {
|
||||
try {
|
||||
final unread = rooms.data.map((e) => e.unreadMessages).fold<int>(0, (a, b) => a + b as int);
|
||||
FlutterAppBadge.count(unread);
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart';
|
||||
import 'chat_list_state.dart';
|
||||
|
||||
sealed class ChatListEvent extends LoadableHydratedBlocEvent<ChatListState> {}
|
||||
@@ -0,0 +1,15 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import '../../../../../api/marianumcloud/talk/room/getRoomResponse.dart';
|
||||
|
||||
part 'chat_list_state.freezed.dart';
|
||||
part 'chat_list_state.g.dart';
|
||||
|
||||
@freezed
|
||||
abstract class ChatListState with _$ChatListState {
|
||||
const factory ChatListState({
|
||||
GetRoomResponse? rooms,
|
||||
}) = _ChatListState;
|
||||
|
||||
factory ChatListState.fromJson(Map<String, Object?> json) => _$ChatListStateFromJson(json);
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'chat_list_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
/// @nodoc
|
||||
mixin _$ChatListState {
|
||||
|
||||
GetRoomResponse? get rooms;
|
||||
/// Create a copy of ChatListState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$ChatListStateCopyWith<ChatListState> get copyWith => _$ChatListStateCopyWithImpl<ChatListState>(this as ChatListState, _$identity);
|
||||
|
||||
/// Serializes this ChatListState to a JSON map.
|
||||
Map<String, dynamic> toJson();
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is ChatListState&&(identical(other.rooms, rooms) || other.rooms == rooms));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,rooms);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ChatListState(rooms: $rooms)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $ChatListStateCopyWith<$Res> {
|
||||
factory $ChatListStateCopyWith(ChatListState value, $Res Function(ChatListState) _then) = _$ChatListStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
GetRoomResponse? rooms
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$ChatListStateCopyWithImpl<$Res>
|
||||
implements $ChatListStateCopyWith<$Res> {
|
||||
_$ChatListStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final ChatListState _self;
|
||||
final $Res Function(ChatListState) _then;
|
||||
|
||||
/// Create a copy of ChatListState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? rooms = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
rooms: freezed == rooms ? _self.rooms : rooms // ignore: cast_nullable_to_non_nullable
|
||||
as GetRoomResponse?,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [ChatListState].
|
||||
extension ChatListStatePatterns on ChatListState {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ChatListState value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _ChatListState() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ChatListState value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _ChatListState():
|
||||
return $default(_that);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ChatListState value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _ChatListState() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( GetRoomResponse? rooms)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ChatListState() when $default != null:
|
||||
return $default(_that.rooms);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( GetRoomResponse? rooms) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ChatListState():
|
||||
return $default(_that.rooms);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( GetRoomResponse? rooms)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ChatListState() when $default != null:
|
||||
return $default(_that.rooms);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
|
||||
class _ChatListState implements ChatListState {
|
||||
const _ChatListState({this.rooms});
|
||||
factory _ChatListState.fromJson(Map<String, dynamic> json) => _$ChatListStateFromJson(json);
|
||||
|
||||
@override final GetRoomResponse? rooms;
|
||||
|
||||
/// Create a copy of ChatListState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$ChatListStateCopyWith<_ChatListState> get copyWith => __$ChatListStateCopyWithImpl<_ChatListState>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$ChatListStateToJson(this, );
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ChatListState&&(identical(other.rooms, rooms) || other.rooms == rooms));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,rooms);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ChatListState(rooms: $rooms)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$ChatListStateCopyWith<$Res> implements $ChatListStateCopyWith<$Res> {
|
||||
factory _$ChatListStateCopyWith(_ChatListState value, $Res Function(_ChatListState) _then) = __$ChatListStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
GetRoomResponse? rooms
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$ChatListStateCopyWithImpl<$Res>
|
||||
implements _$ChatListStateCopyWith<$Res> {
|
||||
__$ChatListStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _ChatListState _self;
|
||||
final $Res Function(_ChatListState) _then;
|
||||
|
||||
/// Create a copy of ChatListState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? rooms = freezed,}) {
|
||||
return _then(_ChatListState(
|
||||
rooms: freezed == rooms ? _self.rooms : rooms // ignore: cast_nullable_to_non_nullable
|
||||
as GetRoomResponse?,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
@@ -0,0 +1,17 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'chat_list_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_ChatListState _$ChatListStateFromJson(Map<String, dynamic> json) =>
|
||||
_ChatListState(
|
||||
rooms: json['rooms'] == null
|
||||
? null
|
||||
: GetRoomResponse.fromJson(json['rooms'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ChatListStateToJson(_ChatListState instance) =>
|
||||
<String, dynamic>{'rooms': instance.rooms};
|
||||
@@ -0,0 +1,22 @@
|
||||
import 'dart:async';
|
||||
|
||||
import '../../../../../api/marianumcloud/talk/room/getRoomCache.dart';
|
||||
import '../../../../../api/marianumcloud/talk/room/getRoomResponse.dart';
|
||||
import '../../../../../api/marianumcloud/talk/createRoom/createRoom.dart';
|
||||
import '../../../../../api/marianumcloud/talk/createRoom/createRoomParams.dart';
|
||||
|
||||
class ChatListDataProvider {
|
||||
Future<GetRoomResponse> getRooms({bool renew = false}) {
|
||||
final completer = Completer<GetRoomResponse>();
|
||||
GetRoomCache(
|
||||
renew: renew,
|
||||
onUpdate: (data) {
|
||||
if (!completer.isCompleted) completer.complete(data);
|
||||
},
|
||||
);
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
Future<void> createDirectRoom(String invite) =>
|
||||
CreateRoom(CreateRoomParams(roomType: 1, invite: invite)).run();
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import '../../../infrastructure/repository/repository.dart';
|
||||
import '../bloc/chat_list_state.dart';
|
||||
import '../dataProvider/chat_list_data_provider.dart';
|
||||
|
||||
class ChatListRepository extends Repository<ChatListState> {
|
||||
final ChatListDataProvider _provider;
|
||||
|
||||
ChatListRepository([ChatListDataProvider? provider]) : _provider = provider ?? ChatListDataProvider();
|
||||
|
||||
ChatListDataProvider get data => _provider;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc.dart';
|
||||
import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart';
|
||||
import '../repository/files_repository.dart';
|
||||
import 'files_event.dart';
|
||||
import 'files_state.dart';
|
||||
|
||||
class FilesBloc extends LoadableHydratedBloc<FilesEvent, FilesState, FilesRepository> {
|
||||
final List<String> initialPath;
|
||||
|
||||
FilesBloc({this.initialPath = const []});
|
||||
|
||||
@override
|
||||
FilesRepository repository() => FilesRepository();
|
||||
|
||||
@override
|
||||
FilesState fromNothing() => FilesState(currentPath: initialPath);
|
||||
|
||||
@override
|
||||
FilesState fromStorage(Map<String, dynamic> json) => FilesState.fromJson(json);
|
||||
|
||||
@override
|
||||
Map<String, dynamic>? toStorage(FilesState state) => null;
|
||||
|
||||
@override
|
||||
Future<void> gatherData() async {
|
||||
final path = innerState?.currentPath ?? initialPath;
|
||||
await _query(path);
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
final path = innerState?.currentPath ?? initialPath;
|
||||
await _query(path);
|
||||
}
|
||||
|
||||
Future<void> setPath(List<String> path) async {
|
||||
add(Emit((s) => s.copyWith(currentPath: path, listing: null)));
|
||||
await _query(path);
|
||||
}
|
||||
|
||||
Future<void> createFolder(String name) async {
|
||||
final path = innerState?.currentPath ?? initialPath;
|
||||
await repo.data.createFolder('${path.join('/')}/$name');
|
||||
await refresh();
|
||||
}
|
||||
|
||||
Future<void> _query(List<String> path) async {
|
||||
final pathString = path.isEmpty ? '/' : path.join('/');
|
||||
final listing = await repo.data.listFiles(pathString);
|
||||
listing.files.removeWhere((file) => file.name.isEmpty || file.name == path.lastOrNull);
|
||||
add(DataGathered((s) => s.copyWith(listing: listing)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart';
|
||||
import 'files_state.dart';
|
||||
|
||||
sealed class FilesEvent extends LoadableHydratedBlocEvent<FilesState> {}
|
||||
@@ -0,0 +1,16 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import '../../../../../api/marianumcloud/webdav/queries/listFiles/listFilesResponse.dart';
|
||||
|
||||
part 'files_state.freezed.dart';
|
||||
part 'files_state.g.dart';
|
||||
|
||||
@freezed
|
||||
abstract class FilesState with _$FilesState {
|
||||
const factory FilesState({
|
||||
@Default(<String>[]) List<String> currentPath,
|
||||
ListFilesResponse? listing,
|
||||
}) = _FilesState;
|
||||
|
||||
factory FilesState.fromJson(Map<String, Object?> json) => _$FilesStateFromJson(json);
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'files_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
/// @nodoc
|
||||
mixin _$FilesState {
|
||||
|
||||
List<String> get currentPath; ListFilesResponse? get listing;
|
||||
/// Create a copy of FilesState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$FilesStateCopyWith<FilesState> get copyWith => _$FilesStateCopyWithImpl<FilesState>(this as FilesState, _$identity);
|
||||
|
||||
/// Serializes this FilesState to a JSON map.
|
||||
Map<String, dynamic> toJson();
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is FilesState&&const DeepCollectionEquality().equals(other.currentPath, currentPath)&&(identical(other.listing, listing) || other.listing == listing));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(currentPath),listing);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'FilesState(currentPath: $currentPath, listing: $listing)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $FilesStateCopyWith<$Res> {
|
||||
factory $FilesStateCopyWith(FilesState value, $Res Function(FilesState) _then) = _$FilesStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
List<String> currentPath, ListFilesResponse? listing
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$FilesStateCopyWithImpl<$Res>
|
||||
implements $FilesStateCopyWith<$Res> {
|
||||
_$FilesStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final FilesState _self;
|
||||
final $Res Function(FilesState) _then;
|
||||
|
||||
/// Create a copy of FilesState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? currentPath = null,Object? listing = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
currentPath: null == currentPath ? _self.currentPath : currentPath // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,listing: freezed == listing ? _self.listing : listing // ignore: cast_nullable_to_non_nullable
|
||||
as ListFilesResponse?,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [FilesState].
|
||||
extension FilesStatePatterns on FilesState {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _FilesState value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _FilesState() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _FilesState value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _FilesState():
|
||||
return $default(_that);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _FilesState value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _FilesState() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<String> currentPath, ListFilesResponse? listing)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _FilesState() when $default != null:
|
||||
return $default(_that.currentPath,_that.listing);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<String> currentPath, ListFilesResponse? listing) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _FilesState():
|
||||
return $default(_that.currentPath,_that.listing);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<String> currentPath, ListFilesResponse? listing)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _FilesState() when $default != null:
|
||||
return $default(_that.currentPath,_that.listing);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
|
||||
class _FilesState implements FilesState {
|
||||
const _FilesState({final List<String> currentPath = const <String>[], this.listing}): _currentPath = currentPath;
|
||||
factory _FilesState.fromJson(Map<String, dynamic> json) => _$FilesStateFromJson(json);
|
||||
|
||||
final List<String> _currentPath;
|
||||
@override@JsonKey() List<String> get currentPath {
|
||||
if (_currentPath is EqualUnmodifiableListView) return _currentPath;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_currentPath);
|
||||
}
|
||||
|
||||
@override final ListFilesResponse? listing;
|
||||
|
||||
/// Create a copy of FilesState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$FilesStateCopyWith<_FilesState> get copyWith => __$FilesStateCopyWithImpl<_FilesState>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$FilesStateToJson(this, );
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _FilesState&&const DeepCollectionEquality().equals(other._currentPath, _currentPath)&&(identical(other.listing, listing) || other.listing == listing));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_currentPath),listing);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'FilesState(currentPath: $currentPath, listing: $listing)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$FilesStateCopyWith<$Res> implements $FilesStateCopyWith<$Res> {
|
||||
factory _$FilesStateCopyWith(_FilesState value, $Res Function(_FilesState) _then) = __$FilesStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
List<String> currentPath, ListFilesResponse? listing
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$FilesStateCopyWithImpl<$Res>
|
||||
implements _$FilesStateCopyWith<$Res> {
|
||||
__$FilesStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _FilesState _self;
|
||||
final $Res Function(_FilesState) _then;
|
||||
|
||||
/// Create a copy of FilesState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? currentPath = null,Object? listing = freezed,}) {
|
||||
return _then(_FilesState(
|
||||
currentPath: null == currentPath ? _self._currentPath : currentPath // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,listing: freezed == listing ? _self.listing : listing // ignore: cast_nullable_to_non_nullable
|
||||
as ListFilesResponse?,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
@@ -0,0 +1,24 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'files_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_FilesState _$FilesStateFromJson(Map<String, dynamic> json) => _FilesState(
|
||||
currentPath:
|
||||
(json['currentPath'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const <String>[],
|
||||
listing: json['listing'] == null
|
||||
? null
|
||||
: ListFilesResponse.fromJson(json['listing'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$FilesStateToJson(_FilesState instance) =>
|
||||
<String, dynamic>{
|
||||
'currentPath': instance.currentPath,
|
||||
'listing': instance.listing,
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:nextcloud/nextcloud.dart';
|
||||
|
||||
import '../../../../../api/marianumcloud/webdav/queries/listFiles/listFilesCache.dart';
|
||||
import '../../../../../api/marianumcloud/webdav/queries/listFiles/listFilesResponse.dart';
|
||||
import '../../../../../api/marianumcloud/webdav/webdavApi.dart';
|
||||
|
||||
class FilesDataProvider {
|
||||
Future<ListFilesResponse> listFiles(String path) {
|
||||
final completer = Completer<ListFilesResponse>();
|
||||
ListFilesCache(
|
||||
path: path,
|
||||
onUpdate: (data) {
|
||||
if (!completer.isCompleted) completer.complete(data);
|
||||
},
|
||||
);
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
Future<void> createFolder(String fullPath) async {
|
||||
final webdav = await WebdavApi.webdav;
|
||||
await webdav.mkcol(PathUri.parse(fullPath));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import '../../../infrastructure/repository/repository.dart';
|
||||
import '../bloc/files_state.dart';
|
||||
import '../dataProvider/files_data_provider.dart';
|
||||
|
||||
class FilesRepository extends Repository<FilesState> {
|
||||
final FilesDataProvider _provider;
|
||||
|
||||
FilesRepository([FilesDataProvider? provider]) : _provider = provider ?? FilesDataProvider();
|
||||
|
||||
FilesDataProvider get data => _provider;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:easy_debounce/easy_debounce.dart';
|
||||
import 'package:hydrated_bloc/hydrated_bloc.dart';
|
||||
|
||||
import '../../../../../storage/base/settings.dart';
|
||||
import '../../../../../view/settings/defaultSettings.dart';
|
||||
|
||||
class SettingsCubit extends HydratedCubit<Settings> {
|
||||
static const _debounceTag = 'settings_persist';
|
||||
|
||||
SettingsCubit() : super(DefaultSettings.get());
|
||||
|
||||
Settings val({bool write = false}) {
|
||||
if (write) {
|
||||
// Notify listeners immediately so the UI reflects the mutation right away;
|
||||
// debounce the actual persistence to disk to avoid hammering on rapid edits.
|
||||
_emitFreshInstance();
|
||||
EasyDebounce.debounce(_debounceTag, const Duration(milliseconds: 500), _emitFreshInstance);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
void _emitFreshInstance() {
|
||||
try {
|
||||
emit(Settings.fromJson(state.toJson()));
|
||||
} catch (e) {
|
||||
log('Failed to refresh settings state: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> reset() async {
|
||||
emit(DefaultSettings.get());
|
||||
}
|
||||
|
||||
@override
|
||||
Settings fromJson(Map<String, dynamic> json) {
|
||||
try {
|
||||
return Settings.fromJson(json);
|
||||
} catch (_) {
|
||||
try {
|
||||
return Settings.fromJson(_mergeSettings(json, DefaultSettings.get().toJson()));
|
||||
} catch (_) {
|
||||
return DefaultSettings.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic>? toJson(Settings state) => state.toJson();
|
||||
|
||||
Map<String, dynamic> _mergeSettings(Map<String, dynamic> oldMap, Map<String, dynamic> newMap) {
|
||||
final merged = Map<String, dynamic>.from(newMap);
|
||||
oldMap.forEach((key, value) {
|
||||
if (merged.containsKey(key)) {
|
||||
if (value is Map<String, dynamic> && merged[key] is Map<String, dynamic>) {
|
||||
merged[key] = _mergeSettings(value, merged[key]);
|
||||
} else {
|
||||
merged[key] = value;
|
||||
}
|
||||
}
|
||||
});
|
||||
return merged;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../../../../../api/mhsl/customTimetableEvent/customTimetableEvent.dart';
|
||||
import '../../../../../api/webuntis/queries/getTimetable/getTimetableResponse.dart';
|
||||
import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc.dart';
|
||||
import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart';
|
||||
import '../repository/timetable_repository.dart';
|
||||
import 'timetable_event.dart';
|
||||
import 'timetable_state.dart';
|
||||
|
||||
class TimetableBloc extends LoadableHydratedBloc<TimetableEvent, TimetableState, TimetableRepository> {
|
||||
static const Duration _weekSpan = Duration(days: 7);
|
||||
static final DateFormat _weekKeyFormat = DateFormat('yyyyMMdd');
|
||||
|
||||
DateTime _lastWeekRequestStart = DateTime.fromMillisecondsSinceEpoch(0);
|
||||
|
||||
@override
|
||||
TimetableRepository repository() => TimetableRepository();
|
||||
|
||||
@override
|
||||
TimetableState fromNothing() {
|
||||
final reference = DateTime.now().add(const Duration(days: 2));
|
||||
return TimetableState(
|
||||
startDate: _startOfWeek(reference),
|
||||
endDate: _endOfWeek(reference),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TimetableState fromStorage(Map<String, dynamic> json) => TimetableState.fromJson(json);
|
||||
|
||||
@override
|
||||
Map<String, dynamic>? toStorage(TimetableState state) => state.toJson();
|
||||
|
||||
@override
|
||||
Future<void> gatherData() async {
|
||||
final initial = innerState ?? fromNothing();
|
||||
await Future.wait([
|
||||
_loadCurrentWeek(initial.startDate, initial.endDate),
|
||||
_loadStaticReferenceData(),
|
||||
_loadCustomEvents(),
|
||||
]);
|
||||
_prefetchAdjacentWeeks(initial.startDate, initial.endDate);
|
||||
}
|
||||
|
||||
void changeWeek(DateTime startDate, DateTime endDate) {
|
||||
final current = innerState ?? fromNothing();
|
||||
if (current.startDate == startDate && current.endDate == endDate) return;
|
||||
add(Emit((s) => s.copyWith(startDate: startDate, endDate: endDate)));
|
||||
_loadCurrentWeek(startDate, endDate);
|
||||
_prefetchAdjacentWeeks(startDate, endDate);
|
||||
}
|
||||
|
||||
void resetWeek() {
|
||||
final reference = DateTime.now().add(const Duration(days: 2));
|
||||
changeWeek(_startOfWeek(reference), _endOfWeek(reference));
|
||||
}
|
||||
|
||||
void refresh() => fetch();
|
||||
|
||||
Future<void> addCustomEvent(CustomTimetableEvent event) async {
|
||||
await repo.data.addCustomEvent(event);
|
||||
await _refreshCustomEvents();
|
||||
}
|
||||
|
||||
Future<void> updateCustomEvent(String id, CustomTimetableEvent event) async {
|
||||
await repo.data.updateCustomEvent(id, event);
|
||||
await _refreshCustomEvents();
|
||||
}
|
||||
|
||||
Future<void> removeCustomEvent(String id) async {
|
||||
await repo.data.removeCustomEvent(id);
|
||||
await _refreshCustomEvents();
|
||||
}
|
||||
|
||||
Future<void> _loadCurrentWeek(DateTime startDate, DateTime endDate) async {
|
||||
final requestStart = DateTime.now();
|
||||
_lastWeekRequestStart = requestStart;
|
||||
try {
|
||||
final week = await repo.data.getWeek(startDate, endDate);
|
||||
if (_lastWeekRequestStart.isAfter(requestStart)) return;
|
||||
_writeWeekToCache(startDate, week);
|
||||
} catch (_) {
|
||||
// Errors are surfaced via LoadableHydratedBloc.fetch's catchError.
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadStaticReferenceData() async {
|
||||
final (rooms, subjects, schoolHolidays) = await (
|
||||
repo.data.getRooms(),
|
||||
repo.data.getSubjects(),
|
||||
repo.data.getSchoolHolidays(),
|
||||
).wait;
|
||||
|
||||
add(DataGathered((s) => s.copyWith(
|
||||
rooms: rooms,
|
||||
subjects: subjects,
|
||||
schoolHolidays: schoolHolidays,
|
||||
dataVersion: s.dataVersion + 1,
|
||||
)));
|
||||
}
|
||||
|
||||
Future<void> _loadCustomEvents({bool renew = false}) async {
|
||||
final events = await repo.data.getCustomEvents(renew: renew);
|
||||
add(DataGathered((s) => s.copyWith(customEvents: events, dataVersion: s.dataVersion + 1)));
|
||||
}
|
||||
|
||||
Future<void> _refreshCustomEvents() => _loadCustomEvents(renew: true);
|
||||
|
||||
void _prefetchAdjacentWeeks(DateTime start, DateTime end) {
|
||||
_prefetchWeek(start.subtract(_weekSpan), end.subtract(_weekSpan));
|
||||
_prefetchWeek(start.add(_weekSpan), end.add(_weekSpan));
|
||||
}
|
||||
|
||||
void _prefetchWeek(DateTime start, DateTime end) {
|
||||
repo.data.getWeek(start, end).then((week) => _writeWeekToCache(start, week)).catchError((_) {});
|
||||
}
|
||||
|
||||
void _writeWeekToCache(DateTime weekStart, GetTimetableResponse week) {
|
||||
final key = _weekKeyFormat.format(weekStart);
|
||||
add(DataGathered((s) {
|
||||
final updated = Map<String, GetTimetableResponse>.of(s.weekCache);
|
||||
updated[key] = week;
|
||||
return s.copyWith(weekCache: updated, dataVersion: s.dataVersion + 1);
|
||||
}));
|
||||
}
|
||||
|
||||
static DateTime _startOfWeek(DateTime reference) {
|
||||
final monday = reference.subtract(Duration(days: reference.weekday - 1));
|
||||
return DateTime(monday.year, monday.month, monday.day);
|
||||
}
|
||||
|
||||
static DateTime _endOfWeek(DateTime reference) {
|
||||
final friday = reference.add(Duration(days: DateTime.daysPerWeek - reference.weekday - 2));
|
||||
return DateTime(friday.year, friday.month, friday.day);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart';
|
||||
import 'timetable_state.dart';
|
||||
|
||||
sealed class TimetableEvent extends LoadableHydratedBlocEvent<TimetableState> {}
|
||||
@@ -0,0 +1,33 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import '../../../../../api/mhsl/customTimetableEvent/get/getCustomTimetableEventResponse.dart';
|
||||
import '../../../../../api/webuntis/queries/getHolidays/getHolidaysResponse.dart';
|
||||
import '../../../../../api/webuntis/queries/getRooms/getRoomsResponse.dart';
|
||||
import '../../../../../api/webuntis/queries/getSubjects/getSubjectsResponse.dart';
|
||||
import '../../../../../api/webuntis/queries/getTimetable/getTimetableResponse.dart';
|
||||
|
||||
part 'timetable_state.freezed.dart';
|
||||
part 'timetable_state.g.dart';
|
||||
|
||||
@freezed
|
||||
abstract class TimetableState with _$TimetableState {
|
||||
const TimetableState._();
|
||||
|
||||
const factory TimetableState({
|
||||
@Default(<String, GetTimetableResponse>{}) Map<String, GetTimetableResponse> weekCache,
|
||||
GetRoomsResponse? rooms,
|
||||
GetSubjectsResponse? subjects,
|
||||
GetHolidaysResponse? schoolHolidays,
|
||||
GetCustomTimetableEventResponse? customEvents,
|
||||
required DateTime startDate,
|
||||
required DateTime endDate,
|
||||
@Default(0) int dataVersion,
|
||||
}) = _TimetableState;
|
||||
|
||||
factory TimetableState.fromJson(Map<String, Object?> json) => _$TimetableStateFromJson(json);
|
||||
|
||||
Iterable<GetTimetableResponseObject> getAllKnownLessons() =>
|
||||
weekCache.values.expand((response) => response.result);
|
||||
|
||||
bool get hasReferenceData => rooms != null && subjects != null && schoolHolidays != null && customEvents != null;
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'timetable_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
/// @nodoc
|
||||
mixin _$TimetableState {
|
||||
|
||||
Map<String, GetTimetableResponse> get weekCache; GetRoomsResponse? get rooms; GetSubjectsResponse? get subjects; GetHolidaysResponse? get schoolHolidays; GetCustomTimetableEventResponse? get customEvents; DateTime get startDate; DateTime get endDate; int get dataVersion;
|
||||
/// Create a copy of TimetableState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$TimetableStateCopyWith<TimetableState> get copyWith => _$TimetableStateCopyWithImpl<TimetableState>(this as TimetableState, _$identity);
|
||||
|
||||
/// Serializes this TimetableState to a JSON map.
|
||||
Map<String, dynamic> toJson();
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is TimetableState&&const DeepCollectionEquality().equals(other.weekCache, weekCache)&&(identical(other.rooms, rooms) || other.rooms == rooms)&&(identical(other.subjects, subjects) || other.subjects == subjects)&&(identical(other.schoolHolidays, schoolHolidays) || other.schoolHolidays == schoolHolidays)&&(identical(other.customEvents, customEvents) || other.customEvents == customEvents)&&(identical(other.startDate, startDate) || other.startDate == startDate)&&(identical(other.endDate, endDate) || other.endDate == endDate)&&(identical(other.dataVersion, dataVersion) || other.dataVersion == dataVersion));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(weekCache),rooms,subjects,schoolHolidays,customEvents,startDate,endDate,dataVersion);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'TimetableState(weekCache: $weekCache, rooms: $rooms, subjects: $subjects, schoolHolidays: $schoolHolidays, customEvents: $customEvents, startDate: $startDate, endDate: $endDate, dataVersion: $dataVersion)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $TimetableStateCopyWith<$Res> {
|
||||
factory $TimetableStateCopyWith(TimetableState value, $Res Function(TimetableState) _then) = _$TimetableStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
Map<String, GetTimetableResponse> weekCache, GetRoomsResponse? rooms, GetSubjectsResponse? subjects, GetHolidaysResponse? schoolHolidays, GetCustomTimetableEventResponse? customEvents, DateTime startDate, DateTime endDate, int dataVersion
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$TimetableStateCopyWithImpl<$Res>
|
||||
implements $TimetableStateCopyWith<$Res> {
|
||||
_$TimetableStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final TimetableState _self;
|
||||
final $Res Function(TimetableState) _then;
|
||||
|
||||
/// Create a copy of TimetableState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? weekCache = null,Object? rooms = freezed,Object? subjects = freezed,Object? schoolHolidays = freezed,Object? customEvents = freezed,Object? startDate = null,Object? endDate = null,Object? dataVersion = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
weekCache: null == weekCache ? _self.weekCache : weekCache // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, GetTimetableResponse>,rooms: freezed == rooms ? _self.rooms : rooms // ignore: cast_nullable_to_non_nullable
|
||||
as GetRoomsResponse?,subjects: freezed == subjects ? _self.subjects : subjects // ignore: cast_nullable_to_non_nullable
|
||||
as GetSubjectsResponse?,schoolHolidays: freezed == schoolHolidays ? _self.schoolHolidays : schoolHolidays // ignore: cast_nullable_to_non_nullable
|
||||
as GetHolidaysResponse?,customEvents: freezed == customEvents ? _self.customEvents : customEvents // ignore: cast_nullable_to_non_nullable
|
||||
as GetCustomTimetableEventResponse?,startDate: null == startDate ? _self.startDate : startDate // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,endDate: null == endDate ? _self.endDate : endDate // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,dataVersion: null == dataVersion ? _self.dataVersion : dataVersion // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [TimetableState].
|
||||
extension TimetableStatePatterns on TimetableState {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _TimetableState value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _TimetableState() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _TimetableState value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _TimetableState():
|
||||
return $default(_that);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _TimetableState value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _TimetableState() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( Map<String, GetTimetableResponse> weekCache, GetRoomsResponse? rooms, GetSubjectsResponse? subjects, GetHolidaysResponse? schoolHolidays, GetCustomTimetableEventResponse? customEvents, DateTime startDate, DateTime endDate, int dataVersion)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _TimetableState() when $default != null:
|
||||
return $default(_that.weekCache,_that.rooms,_that.subjects,_that.schoolHolidays,_that.customEvents,_that.startDate,_that.endDate,_that.dataVersion);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( Map<String, GetTimetableResponse> weekCache, GetRoomsResponse? rooms, GetSubjectsResponse? subjects, GetHolidaysResponse? schoolHolidays, GetCustomTimetableEventResponse? customEvents, DateTime startDate, DateTime endDate, int dataVersion) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _TimetableState():
|
||||
return $default(_that.weekCache,_that.rooms,_that.subjects,_that.schoolHolidays,_that.customEvents,_that.startDate,_that.endDate,_that.dataVersion);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( Map<String, GetTimetableResponse> weekCache, GetRoomsResponse? rooms, GetSubjectsResponse? subjects, GetHolidaysResponse? schoolHolidays, GetCustomTimetableEventResponse? customEvents, DateTime startDate, DateTime endDate, int dataVersion)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _TimetableState() when $default != null:
|
||||
return $default(_that.weekCache,_that.rooms,_that.subjects,_that.schoolHolidays,_that.customEvents,_that.startDate,_that.endDate,_that.dataVersion);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
|
||||
class _TimetableState extends TimetableState {
|
||||
const _TimetableState({final Map<String, GetTimetableResponse> weekCache = const <String, GetTimetableResponse>{}, this.rooms, this.subjects, this.schoolHolidays, this.customEvents, required this.startDate, required this.endDate, this.dataVersion = 0}): _weekCache = weekCache,super._();
|
||||
factory _TimetableState.fromJson(Map<String, dynamic> json) => _$TimetableStateFromJson(json);
|
||||
|
||||
final Map<String, GetTimetableResponse> _weekCache;
|
||||
@override@JsonKey() Map<String, GetTimetableResponse> get weekCache {
|
||||
if (_weekCache is EqualUnmodifiableMapView) return _weekCache;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableMapView(_weekCache);
|
||||
}
|
||||
|
||||
@override final GetRoomsResponse? rooms;
|
||||
@override final GetSubjectsResponse? subjects;
|
||||
@override final GetHolidaysResponse? schoolHolidays;
|
||||
@override final GetCustomTimetableEventResponse? customEvents;
|
||||
@override final DateTime startDate;
|
||||
@override final DateTime endDate;
|
||||
@override@JsonKey() final int dataVersion;
|
||||
|
||||
/// Create a copy of TimetableState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$TimetableStateCopyWith<_TimetableState> get copyWith => __$TimetableStateCopyWithImpl<_TimetableState>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$TimetableStateToJson(this, );
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _TimetableState&&const DeepCollectionEquality().equals(other._weekCache, _weekCache)&&(identical(other.rooms, rooms) || other.rooms == rooms)&&(identical(other.subjects, subjects) || other.subjects == subjects)&&(identical(other.schoolHolidays, schoolHolidays) || other.schoolHolidays == schoolHolidays)&&(identical(other.customEvents, customEvents) || other.customEvents == customEvents)&&(identical(other.startDate, startDate) || other.startDate == startDate)&&(identical(other.endDate, endDate) || other.endDate == endDate)&&(identical(other.dataVersion, dataVersion) || other.dataVersion == dataVersion));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_weekCache),rooms,subjects,schoolHolidays,customEvents,startDate,endDate,dataVersion);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'TimetableState(weekCache: $weekCache, rooms: $rooms, subjects: $subjects, schoolHolidays: $schoolHolidays, customEvents: $customEvents, startDate: $startDate, endDate: $endDate, dataVersion: $dataVersion)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$TimetableStateCopyWith<$Res> implements $TimetableStateCopyWith<$Res> {
|
||||
factory _$TimetableStateCopyWith(_TimetableState value, $Res Function(_TimetableState) _then) = __$TimetableStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
Map<String, GetTimetableResponse> weekCache, GetRoomsResponse? rooms, GetSubjectsResponse? subjects, GetHolidaysResponse? schoolHolidays, GetCustomTimetableEventResponse? customEvents, DateTime startDate, DateTime endDate, int dataVersion
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$TimetableStateCopyWithImpl<$Res>
|
||||
implements _$TimetableStateCopyWith<$Res> {
|
||||
__$TimetableStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _TimetableState _self;
|
||||
final $Res Function(_TimetableState) _then;
|
||||
|
||||
/// Create a copy of TimetableState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? weekCache = null,Object? rooms = freezed,Object? subjects = freezed,Object? schoolHolidays = freezed,Object? customEvents = freezed,Object? startDate = null,Object? endDate = null,Object? dataVersion = null,}) {
|
||||
return _then(_TimetableState(
|
||||
weekCache: null == weekCache ? _self._weekCache : weekCache // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, GetTimetableResponse>,rooms: freezed == rooms ? _self.rooms : rooms // ignore: cast_nullable_to_non_nullable
|
||||
as GetRoomsResponse?,subjects: freezed == subjects ? _self.subjects : subjects // ignore: cast_nullable_to_non_nullable
|
||||
as GetSubjectsResponse?,schoolHolidays: freezed == schoolHolidays ? _self.schoolHolidays : schoolHolidays // ignore: cast_nullable_to_non_nullable
|
||||
as GetHolidaysResponse?,customEvents: freezed == customEvents ? _self.customEvents : customEvents // ignore: cast_nullable_to_non_nullable
|
||||
as GetCustomTimetableEventResponse?,startDate: null == startDate ? _self.startDate : startDate // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,endDate: null == endDate ? _self.endDate : endDate // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,dataVersion: null == dataVersion ? _self.dataVersion : dataVersion // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
@@ -0,0 +1,52 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'timetable_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_TimetableState _$TimetableStateFromJson(Map<String, dynamic> json) =>
|
||||
_TimetableState(
|
||||
weekCache:
|
||||
(json['weekCache'] as Map<String, dynamic>?)?.map(
|
||||
(k, e) => MapEntry(
|
||||
k,
|
||||
GetTimetableResponse.fromJson(e as Map<String, dynamic>),
|
||||
),
|
||||
) ??
|
||||
const <String, GetTimetableResponse>{},
|
||||
rooms: json['rooms'] == null
|
||||
? null
|
||||
: GetRoomsResponse.fromJson(json['rooms'] as Map<String, dynamic>),
|
||||
subjects: json['subjects'] == null
|
||||
? null
|
||||
: GetSubjectsResponse.fromJson(
|
||||
json['subjects'] as Map<String, dynamic>,
|
||||
),
|
||||
schoolHolidays: json['schoolHolidays'] == null
|
||||
? null
|
||||
: GetHolidaysResponse.fromJson(
|
||||
json['schoolHolidays'] as Map<String, dynamic>,
|
||||
),
|
||||
customEvents: json['customEvents'] == null
|
||||
? null
|
||||
: GetCustomTimetableEventResponse.fromJson(
|
||||
json['customEvents'] as Map<String, dynamic>,
|
||||
),
|
||||
startDate: DateTime.parse(json['startDate'] as String),
|
||||
endDate: DateTime.parse(json['endDate'] as String),
|
||||
dataVersion: (json['dataVersion'] as num?)?.toInt() ?? 0,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$TimetableStateToJson(_TimetableState instance) =>
|
||||
<String, dynamic>{
|
||||
'weekCache': instance.weekCache,
|
||||
'rooms': instance.rooms,
|
||||
'subjects': instance.subjects,
|
||||
'schoolHolidays': instance.schoolHolidays,
|
||||
'customEvents': instance.customEvents,
|
||||
'startDate': instance.startDate.toIso8601String(),
|
||||
'endDate': instance.endDate.toIso8601String(),
|
||||
'dataVersion': instance.dataVersion,
|
||||
};
|
||||
@@ -0,0 +1,87 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../../../../../api/mhsl/customTimetableEvent/add/addCustomTimetableEvent.dart';
|
||||
import '../../../../../api/mhsl/customTimetableEvent/add/addCustomTimetableEventParams.dart';
|
||||
import '../../../../../api/mhsl/customTimetableEvent/customTimetableEvent.dart';
|
||||
import '../../../../../api/mhsl/customTimetableEvent/get/getCustomTimetableEventCache.dart';
|
||||
import '../../../../../api/mhsl/customTimetableEvent/get/getCustomTimetableEventParams.dart';
|
||||
import '../../../../../api/mhsl/customTimetableEvent/get/getCustomTimetableEventResponse.dart';
|
||||
import '../../../../../api/mhsl/customTimetableEvent/remove/removeCustomTimetableEvent.dart';
|
||||
import '../../../../../api/mhsl/customTimetableEvent/remove/removeCustomTimetableEventParams.dart';
|
||||
import '../../../../../api/mhsl/customTimetableEvent/update/updateCustomTimetableEvent.dart';
|
||||
import '../../../../../api/mhsl/customTimetableEvent/update/updateCustomTimetableEventParams.dart';
|
||||
import '../../../../../api/webuntis/queries/getHolidays/getHolidaysCache.dart';
|
||||
import '../../../../../api/webuntis/queries/getHolidays/getHolidaysResponse.dart';
|
||||
import '../../../../../api/webuntis/queries/getRooms/getRoomsCache.dart';
|
||||
import '../../../../../api/webuntis/queries/getRooms/getRoomsResponse.dart';
|
||||
import '../../../../../api/webuntis/queries/getSubjects/getSubjectsCache.dart';
|
||||
import '../../../../../api/webuntis/queries/getSubjects/getSubjectsResponse.dart';
|
||||
import '../../../../../api/webuntis/queries/getTimetable/getTimetableCache.dart';
|
||||
import '../../../../../api/webuntis/queries/getTimetable/getTimetableResponse.dart';
|
||||
import '../../../../../model/accountData.dart';
|
||||
|
||||
class TimetableDataProvider {
|
||||
static final DateFormat _dateFormat = DateFormat('yyyyMMdd');
|
||||
|
||||
Future<GetTimetableResponse> getWeek(DateTime startDate, DateTime endDate) {
|
||||
final completer = Completer<GetTimetableResponse>();
|
||||
GetTimetableCache(
|
||||
startdate: int.parse(_dateFormat.format(startDate)),
|
||||
enddate: int.parse(_dateFormat.format(endDate)),
|
||||
onUpdate: (data) {
|
||||
if (!completer.isCompleted) completer.complete(data);
|
||||
},
|
||||
onError: (e) {
|
||||
if (!completer.isCompleted) completer.completeError(e);
|
||||
},
|
||||
);
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
Future<GetRoomsResponse> getRooms() {
|
||||
final completer = Completer<GetRoomsResponse>();
|
||||
GetRoomsCache(onUpdate: (data) {
|
||||
if (!completer.isCompleted) completer.complete(data);
|
||||
});
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
Future<GetSubjectsResponse> getSubjects() {
|
||||
final completer = Completer<GetSubjectsResponse>();
|
||||
GetSubjectsCache(onUpdate: (data) {
|
||||
if (!completer.isCompleted) completer.complete(data);
|
||||
});
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
Future<GetHolidaysResponse> getSchoolHolidays() {
|
||||
final completer = Completer<GetHolidaysResponse>();
|
||||
GetHolidaysCache(onUpdate: (data) {
|
||||
if (!completer.isCompleted) completer.complete(data);
|
||||
});
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
Future<GetCustomTimetableEventResponse> getCustomEvents({bool renew = false}) {
|
||||
final completer = Completer<GetCustomTimetableEventResponse>();
|
||||
GetCustomTimetableEventCache(
|
||||
GetCustomTimetableEventParams(AccountData().getUserSecret()),
|
||||
renew: renew,
|
||||
onUpdate: (data) {
|
||||
if (!completer.isCompleted) completer.complete(data);
|
||||
},
|
||||
);
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
Future<void> addCustomEvent(CustomTimetableEvent event) =>
|
||||
AddCustomTimetableEvent(AddCustomTimetableEventParams(AccountData().getUserSecret(), event)).run();
|
||||
|
||||
Future<void> updateCustomEvent(String id, CustomTimetableEvent event) =>
|
||||
UpdateCustomTimetableEvent(UpdateCustomTimetableEventParams(id, event)).run();
|
||||
|
||||
Future<void> removeCustomEvent(String id) =>
|
||||
RemoveCustomTimetableEvent(RemoveCustomTimetableEventParams(id)).run();
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import '../../../infrastructure/repository/repository.dart';
|
||||
import '../bloc/timetable_state.dart';
|
||||
import '../dataProvider/timetable_data_provider.dart';
|
||||
|
||||
class TimetableRepository extends Repository<TimetableState> {
|
||||
final TimetableDataProvider _provider;
|
||||
|
||||
TimetableRepository([TimetableDataProvider? provider]) : _provider = provider ?? TimetableDataProvider();
|
||||
|
||||
TimetableDataProvider get data => _provider;
|
||||
}
|
||||
Reference in New Issue
Block a user