Skip to content

Commit

Permalink
refactor(checkin): integrate checkin feature
Browse files Browse the repository at this point in the history
  • Loading branch information
realth000 committed Oct 4, 2024
1 parent 52da87a commit 05603a2
Show file tree
Hide file tree
Showing 20 changed files with 339 additions and 353 deletions.
51 changes: 34 additions & 17 deletions lib/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:tsdm_client/features/authentication/repository/authentication_repository.dart';
import 'package:tsdm_client/features/cache/bloc/image_cache_trigger_cubit.dart';
import 'package:tsdm_client/features/cache/repository/image_cache_repository.dart';
import 'package:tsdm_client/features/checkin/bloc/checkin_bloc.dart';
import 'package:tsdm_client/features/checkin/repository/checkin_repository.dart';
import 'package:tsdm_client/features/editor/repository/editor_repository.dart';
import 'package:tsdm_client/features/forum/repository/forum_repository.dart';
import 'package:tsdm_client/features/profile/repository/profile_repository.dart';
Expand Down Expand Up @@ -125,6 +127,9 @@ class _AppState extends State<App> with WindowListener {
RepositoryProvider<AuthenticationRepository>(
create: (_) => AuthenticationRepository(),
),
RepositoryProvider<CheckinRepository>(
create: (_) => const CheckinRepository(),
),
RepositoryProvider<ForumHomeRepository>(
create: (_) => ForumHomeRepository(),
),
Expand Down Expand Up @@ -153,27 +158,39 @@ class _AppState extends State<App> with WindowListener {
create: (context) =>
ImageCacheTriggerCubit(RepositoryProvider.of(context)),
),
BlocProvider(
create: (context) => SettingsBloc(
fragmentsRepository:
RepositoryProvider.of<FragmentsRepository>(context),
settingsRepository: getIt.get<SettingsRepository>(),
),
),
RepositoryProvider<ThreadVisitHistoryRepo>(
create: (_) => ThreadVisitHistoryRepo(getIt.get<StorageProvider>()),
),
BlocProvider(
create: (context) =>
ThreadVisitHistoryBloc(RepositoryProvider.of(context))
..add(const ThreadVisitHistoryFetchAllRequested()),
),
],
child: BlocProvider(
create: (context) => ThemeCubit(
accentColor: widget.color >= 0 ? Color(widget.color) : null,
themeModeIndex: widget.themeModeIndex,
),
child: MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => SettingsBloc(
fragmentsRepository:
RepositoryProvider.of<FragmentsRepository>(context),
settingsRepository: getIt.get<SettingsRepository>(),
),
),
BlocProvider(
create: (context) =>
ThreadVisitHistoryBloc(RepositoryProvider.of(context))
..add(const ThreadVisitHistoryFetchAllRequested()),
),
// Become top-level because of background auto-checkin feature.
BlocProvider(
create: (context) => CheckinBloc(
checkinRepository: RepositoryProvider.of(context),
authenticationRepository: RepositoryProvider.of(context),
settingsRepository: getIt(),
),
),
BlocProvider(
create: (context) => ThemeCubit(
accentColor: widget.color >= 0 ? Color(widget.color) : null,
themeModeIndex: widget.themeModeIndex,
),
),
],
child: BlocBuilder<ThemeCubit, ThemeState>(
buildWhen: (prev, curr) => prev != curr,
builder: (context, state) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,31 @@ import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:dart_mappable/dart_mappable.dart';
import 'package:tsdm_client/features/authentication/repository/authentication_repository.dart';
import 'package:tsdm_client/features/checkin/models/models.dart';
import 'package:tsdm_client/features/checkin/repository/checkin_repository.dart';
import 'package:tsdm_client/features/settings/repositories/settings_repository.dart';
import 'package:tsdm_client/shared/models/models.dart';
import 'package:tsdm_client/shared/providers/checkin_provider/checkin_provider.dart';
import 'package:tsdm_client/shared/providers/checkin_provider/models/check_in_feeling.dart';
import 'package:tsdm_client/shared/providers/checkin_provider/models/checkin_result.dart';

part 'checkin_button_bloc.mapper.dart';
part 'checkin_button_event.dart';
part 'checkin_button_state.dart';
part 'checkin_bloc.mapper.dart';
part 'checkin_event.dart';
part 'checkin_state.dart';

/// Bloc of checkin.
class CheckinButtonBloc extends Bloc<CheckinButtonEvent, CheckinButtonState> {
final class CheckinBloc extends Bloc<CheckinEvent, CheckinState> {
/// Constructor.
CheckinButtonBloc({
required CheckinProvider checkinProvider,
CheckinBloc({
required CheckinRepository checkinRepository,
required AuthenticationRepository authenticationRepository,
required SettingsRepository settingsRepository,
}) : _checkinProvider = checkinProvider,
}) : _checkinRepository = checkinRepository,
_authenticationRepository = authenticationRepository,
_settingsRepository = settingsRepository,
super(const CheckinButtonInitial()) {
on<CheckinButtonRequested>(_onCheckinButtonRequested);
on<CheckinButtonAuthChanged>(_onCheckinButtonAuthChanged);
super(const CheckinStateInitial()) {
on<CheckinRequested>(_onCheckinRequested);
on<CheckinAuthChanged>(_onCheckinAuthChanged);
_authStreamSub = _authenticationRepository.status.listen(
(status) => add(
CheckinButtonAuthChanged(
CheckinAuthChanged(
authed: status == AuthenticationStatus.authenticated,
),
),
Expand All @@ -37,48 +36,48 @@ class CheckinButtonBloc extends Bloc<CheckinButtonEvent, CheckinButtonState> {

late StreamSubscription<AuthenticationStatus> _authStreamSub;

final CheckinProvider _checkinProvider;
final CheckinRepository _checkinRepository;
final AuthenticationRepository _authenticationRepository;
final SettingsRepository _settingsRepository;

Future<void> _onCheckinButtonRequested(
CheckinButtonRequested event,
Emitter<CheckinButtonState> emit,
Future<void> _onCheckinRequested(
CheckinRequested event,
Emitter<CheckinState> emit,
) async {
if (_authenticationRepository.currentUser == null) {
emit(const CheckinButtonNeedLogin());
emit(const CheckinStateNeedLogin());
return;
}
emit(const CheckinButtonLoading());
emit(const CheckinStateLoading());
final checkinFeeling =
await _settingsRepository.getValue<String>(SettingsKeys.checkinFeeling);
final checkinMessage =
await _settingsRepository.getValue<String>(SettingsKeys.checkinMessage);
final result = await _checkinProvider.checkin(
final result = await _checkinRepository.checkin(
CheckinFeeling.from(checkinFeeling),
checkinMessage,
);
if (result is CheckinButtonSuccess) {
emit(CheckinButtonSuccess((result as CheckinButtonSuccess).message));
if (result is CheckinStateSuccess) {
emit(CheckinStateSuccess((result as CheckinStateSuccess).message));
return;
}
emit(CheckinButtonFailed(result));
emit(CheckinStateFailed(result));
}

void _onCheckinButtonAuthChanged(
CheckinButtonAuthChanged event,
Emitter<CheckinButtonState> emit,
void _onCheckinAuthChanged(
CheckinAuthChanged event,
Emitter<CheckinState> emit,
) {
if (event.authed) {
if (state is CheckinButtonLoading) {
if (state is CheckinStateLoading) {
return;
}
emit(const CheckinButtonInitial());
emit(const CheckinStateInitial());
} else {
if (state is CheckinButtonLoading) {
if (state is CheckinStateLoading) {
return;
}
emit(const CheckinButtonNeedLogin());
emit(const CheckinStateNeedLogin());
}
}

Expand Down
31 changes: 31 additions & 0 deletions lib/features/checkin/bloc/checkin_event.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
part of 'checkin_bloc.dart';

/// Event of checkin.
@MappableClass()
sealed class CheckinEvent with CheckinEventMappable {
/// Constructor.
const CheckinEvent();
}

/// User required to checkin.
@MappableClass()
final class CheckinRequested extends CheckinEvent
with CheckinRequestedMappable {
/// Constructor.
const CheckinRequested() : super();
}

/// Auth status changed.
///
/// Triggered by [CheckinBloc].
///
/// Passive event.
@MappableClass()
final class CheckinAuthChanged extends CheckinEvent
with CheckinAuthChangedMappable {
/// Constructor.
const CheckinAuthChanged({required this.authed}) : super();

/// Latest auth status.
final bool authed;
}
53 changes: 53 additions & 0 deletions lib/features/checkin/bloc/checkin_state.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
part of 'checkin_bloc.dart';

/// State of checkin button.
@MappableClass()
sealed class CheckinState with CheckinStateMappable {
const CheckinState();
}

/// Initial state.
@MappableClass()
final class CheckinStateInitial extends CheckinState
with CheckinStateInitialMappable {
/// Constructor.
const CheckinStateInitial() : super();
}

/// Loading data: checking in.
@MappableClass()
final class CheckinStateLoading extends CheckinState
with CheckinStateLoadingMappable {
/// Constructor.
const CheckinStateLoading() : super();
}

/// Need to login to checkin.
@MappableClass()
final class CheckinStateNeedLogin extends CheckinState
with CheckinStateNeedLoginMappable {
/// Constructor.
const CheckinStateNeedLogin() : super();
}

/// Checkin failed.
@MappableClass()
final class CheckinStateFailed extends CheckinState
with CheckinStateFailedMappable {
/// Constructor.
const CheckinStateFailed(this.result) : super();

/// Result of checkin.
final CheckinResult result;
}

/// Checkin succeed.
@MappableClass()
final class CheckinStateSuccess extends CheckinState
with CheckinStateSuccessMappable {
/// Constructor.
const CheckinStateSuccess(this.message) : super();

/// Error text.
final String message;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:tsdm_client/i18n/strings.g.dart';
part of 'models.dart';

// ignore_for_file: public_member_api_docs

Expand Down
81 changes: 81 additions & 0 deletions lib/features/checkin/models/checkin_result.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
part of 'models.dart';

/// Basic checkin result.
@MappableClass()
sealed class CheckinResult with CheckinResultMappable {
/// Constructor.
const CheckinResult();
}

/// Succeed.
@MappableClass()
final class CheckinResultSuccess extends CheckinResult
with CheckinResultSuccessMappable {
/// Constructor.
const CheckinResultSuccess(this.message) : super();

/// Message carried.
final String message;
}

/// User is not authorized.
@MappableClass()
final class CheckinResultNotAuthorized extends CheckinResult
with CheckinResultNotAuthorizedMappable {
/// Constructor.
const CheckinResultNotAuthorized() : super();
}

/// Failed to make the checkin web request.
@MappableClass()
final class CheckinResultWebRequestFailed extends CheckinResult
with CheckinResultWebRequestFailedMappable {
/// Constructor.
const CheckinResultWebRequestFailed(this.statusCode) : super();

/// Response status code.
final int? statusCode;
}

/// Form hash used in checkin request is not found.
@MappableClass()
final class CheckinResultFormHashNotFound extends CheckinResult
with CheckinResultFormHashNotFoundMappable {
/// Constructor.
const CheckinResultFormHashNotFound() : super();
}

/// Already checked today.
@MappableClass()
final class CheckinResultAlreadyChecked extends CheckinResult
with CheckinResultAlreadyCheckedMappable {
/// Constructor.
const CheckinResultAlreadyChecked() : super();
}

/// Not in the allowed checkin time: too early
@MappableClass()
final class CheckinResultEarlyInTime extends CheckinResult
with CheckinResultEarlyInTimeMappable {
/// Constructor.
const CheckinResultEarlyInTime() : super();
}

/// Not in the allowed checkin time: too late
@MappableClass()
final class CheckinResultLateInTime extends CheckinResult
with CheckinResultLateInTimeMappable {
/// Constructor.
const CheckinResultLateInTime() : super();
}

/// Some other error that not specialized.
@MappableClass()
final class CheckinResultOtherError extends CheckinResult
with CheckinResultOtherErrorMappable {
/// Constructor.
const CheckinResultOtherError(this.message) : super();

/// Error message.
final String message;
}
7 changes: 7 additions & 0 deletions lib/features/checkin/models/models.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import 'package:dart_mappable/dart_mappable.dart';
import 'package:flutter/material.dart';
import 'package:tsdm_client/i18n/strings.g.dart';

part 'checkin_feeling.dart';
part 'checkin_result.dart';
part 'models.mapper.dart';
Loading

0 comments on commit 05603a2

Please sign in to comment.