Skip to content

Commit

Permalink
refactor: Improve initial loading and login routines
Browse files Browse the repository at this point in the history
  • Loading branch information
tmaegel committed Mar 18, 2024
1 parent bb9a588 commit 873b151
Show file tree
Hide file tree
Showing 15 changed files with 325 additions and 258 deletions.
28 changes: 8 additions & 20 deletions integration_test/screenshot_integration_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,9 @@ import 'package:ntodotxt/main.dart';
import 'package:ntodotxt/presentation/drawer/states/drawer_cubit.dart';
import 'package:ntodotxt/presentation/filter/states/filter_cubit.dart';
import 'package:ntodotxt/presentation/filter/states/filter_list_bloc.dart';
import 'package:ntodotxt/presentation/filter/states/filter_list_event.dart';
import 'package:ntodotxt/presentation/intro/page/intro_page.dart';
import 'package:ntodotxt/presentation/login/states/login_cubit.dart';
import 'package:ntodotxt/presentation/login/states/login_state.dart'
show LoginLoading, LoginOffline, LoginState, LoginWebDAV;
show LoginLocal, LoginState, LoginWebDAV;
import 'package:ntodotxt/presentation/todo_file/todo_file_cubit.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
Expand Down Expand Up @@ -116,7 +114,7 @@ class AppTester extends StatelessWidget {
create: (BuildContext context) => TodoFileCubit(
repository: context.read<SettingRepository>(),
defaultLocalPath: appCacheDir,
)..load(),
),
),
BlocProvider<DrawerCubit>(
create: (BuildContext context) => DrawerCubit(),
Expand All @@ -126,32 +124,22 @@ class AppTester extends StatelessWidget {
create: (BuildContext context) => FilterCubit(
settingRepository: context.read<SettingRepository>(),
filterRepository: context.read<FilterRepository>(),
)..load(),
),
),
BlocProvider<FilterListBloc>(
create: (BuildContext context) {
return FilterListBloc(
repository: context.read<FilterRepository>(),
)
..add(const FilterListSubscriped())
..add(const FilterListSynchronizationRequested());
},
create: (BuildContext context) => FilterListBloc(
repository: context.read<FilterRepository>(),
),
),
],
child: Builder(
builder: (BuildContext context) {
return BlocBuilder<LoginCubit, LoginState>(
builder: (BuildContext context, LoginState state) {
if (state is LoginLoading) {
return const InitialApp(
child: LoadingPage(),
);
} else if (state is LoginOffline || state is LoginWebDAV) {
if (state is LoginLocal || state is LoginWebDAV) {
return CoreApp(loginState: state);
} else {
return const InitialApp(
child: IntroPage(),
);
return const InitialApp();
}
},
);
Expand Down
16 changes: 11 additions & 5 deletions lib/data/database.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ class DatabaseController {
}

Future<void> close() async {
log.info('Close database $path');
if (_database != null) {
await _database!.close();
}
Expand All @@ -45,9 +44,7 @@ class DatabaseController {
log.info('Perform database upgrade');
}
},
onOpen: (Database db) {
log.info('Open database $path');
},
onOpen: (Database db) {},
singleInstance: true,
);
}
Expand All @@ -56,7 +53,16 @@ class DatabaseController {
abstract class ModelController<T> extends DatabaseController {
ModelController(super.path);

Future<Database> get database async => await instance;
Future<Database> get database async {
log.fine('Access database by $T');
return await instance;
}

@override
Future<void> close() async {
log.fine('Close database by $T');
await super.close();
}

Future<List<T>> list();

Expand Down
98 changes: 64 additions & 34 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import 'package:ntodotxt/presentation/drawer/states/drawer_cubit.dart';
import 'package:ntodotxt/presentation/filter/states/filter_cubit.dart';
import 'package:ntodotxt/presentation/filter/states/filter_list_bloc.dart';
import 'package:ntodotxt/presentation/filter/states/filter_list_event.dart';
import 'package:ntodotxt/presentation/filter/states/filter_list_state.dart';
import 'package:ntodotxt/presentation/filter/states/filter_state.dart';
import 'package:ntodotxt/presentation/intro/page/intro_page.dart';
import 'package:ntodotxt/presentation/login/states/login_cubit.dart';
Expand Down Expand Up @@ -129,16 +130,10 @@ class App extends StatelessWidget {
builder: (BuildContext context) {
return BlocBuilder<LoginCubit, LoginState>(
builder: (BuildContext context, LoginState state) {
if (state is LoginLoading) {
return const InitialApp(
child: LoadingPage(),
);
} else if (state is LoginOffline || state is LoginWebDAV) {
if (state is LoginLocal || state is LoginWebDAV) {
return CoreApp(loginState: state);
} else {
return const InitialApp(
child: IntroPage(),
);
return const InitialApp();
}
},
);
Expand All @@ -150,15 +145,32 @@ class App extends StatelessWidget {
}

class InitialApp extends StatelessWidget {
final Widget child;
final ThemeMode? themeMode;

const InitialApp({
required this.child,
this.themeMode,
super.key,
});

Future<bool> _initialize(BuildContext context) async {
if (context.mounted) {
await context.read<FilterCubit>().load();
}
if (context.mounted) {
await context.read<TodoFileCubit>().load();
}
if (context.mounted) {
context.read<FilterListBloc>()
..add(const FilterListSubscriped())
..add(const FilterListSynchronizationRequested());
}
if (context.mounted) {
await context.read<LoginCubit>().login();
}

return true;
}

@override
Widget build(BuildContext context) {
return MaterialApp(
Expand All @@ -177,10 +189,14 @@ class InitialApp extends StatelessWidget {
),
BlocListener<TodoFileCubit, TodoFileState>(
listener: (BuildContext context, TodoFileState state) {
if (state is TodoFileReady) {
context.read<LoginCubit>().login();
} else if (state is TodoFileError) {
context.read<LoginCubit>().logout();
if (state is TodoFileError) {
SnackBarHandler.error(context, state.message);
}
},
),
BlocListener<FilterListBloc, FilterListState>(
listener: (BuildContext context, FilterListState state) {
if (state is FilterListError) {
SnackBarHandler.error(context, state.message);
}
},
Expand All @@ -193,31 +209,45 @@ class InitialApp extends StatelessWidget {
},
),
],
child: child,
child: FutureBuilder<bool>(
future: _initialize(context),
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
if (snapshot.hasData) {
return BlocBuilder<LoginCubit, LoginState>(
builder: (BuildContext context, LoginState state) {
if (state is LoginLoading ||
state is LoginLocal ||
state is LoginWebDAV) {
// Keep loading screen to prevent screen flickering.
return _loadingScreen();
} else {
return const IntroPage();
}
},
);
} else if (snapshot.hasError) {
return _errorScreen();
} else {
return _loadingScreen();
}
},
),
),
);
}
}

class LoadingPage extends StatelessWidget {
final String message;

const LoadingPage({
this.message = 'Loading',
super.key,
});

@override
Widget build(BuildContext context) {
context.read<TodoFileCubit>().load();
context.read<FilterCubit>().load();
context.read<FilterListBloc>()
..add(const FilterListSubscriped())
..add(const FilterListSynchronizationRequested());
Widget _loadingScreen() {
return const Scaffold(
body: Center(
child: Text('Loading'),
),
);
}

return Scaffold(
Widget _errorScreen() {
return const Scaffold(
body: Center(
child: Text(message),
child: Text('Something went wrong.'),
),
);
}
Expand Down Expand Up @@ -273,7 +303,7 @@ class CoreApp extends StatelessWidget {
'${todoFileState.localPath}${Platform.pathSeparator}${todoFileState.todoFilename}');
log.info('Use todo file ${todoFile.path}');
switch (loginState) {
case LoginOffline():
case LoginLocal():
log.info('Use local backend');
api = LocalTodoListApi(todoFile: todoFile);
case LoginWebDAV():
Expand Down
95 changes: 44 additions & 51 deletions lib/presentation/filter/pages/filter_create_edit_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,65 +46,58 @@ class FilterCreateEditPage extends StatelessWidget {
filterRepository: context.read<FilterRepository>(),
filter: initFilter,
),
child: BlocListener<FilterCubit, FilterState>(
listener: (BuildContext context, FilterState state) {
if (state is FilterError) {
SnackBarHandler.error(context, state.message);
child: GestureDetector(
onTap: () {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
},
child: GestureDetector(
onTap: () {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
},
child: Scaffold(
appBar: MainAppBar(
title: createMode ? 'Create' : 'Edit',
toolbar: Row(
children: <Widget>[
if (!createMode) const DeleteFilterIconButton(),
if (!narrowView) SaveFilterIconButton(initFilter: initFilter),
],
),
child: Scaffold(
appBar: MainAppBar(
title: createMode ? 'Create' : 'Edit',
toolbar: Row(
children: <Widget>[
if (!createMode) const DeleteFilterIconButton(),
if (!narrowView) SaveFilterIconButton(initFilter: initFilter),
],
),
body: ListView(
children: [
const FilterNameTextField(),
const Divider(),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: ListTile(
title: Text(
'General',
style: Theme.of(context).textTheme.titleSmall,
),
),
body: ListView(
children: [
const FilterNameTextField(),
const Divider(),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: ListTile(
title: Text(
'General',
style: Theme.of(context).textTheme.titleSmall,
),
),
const FilterOrderItem(),
const FilterFilterItem(),
const FilterGroupItem(),
const Divider(),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: ListTile(
title: Text(
'Tags',
style: Theme.of(context).textTheme.titleSmall,
),
),
const FilterOrderItem(),
const FilterFilterItem(),
const FilterGroupItem(),
const Divider(),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: ListTile(
title: Text(
'Tags',
style: Theme.of(context).textTheme.titleSmall,
),
),
const FilterPrioritiesItem(),
FilterProjectTagsItem(availableTags: projects),
FilterContextTagsItem(availableTags: contexts),
const SizedBox(height: 16),
],
),
floatingActionButton: keyboardIsOpen || !narrowView
? null
: SaveFilterFABButton(initFilter: initFilter),
),
const FilterPrioritiesItem(),
FilterProjectTagsItem(availableTags: projects),
FilterContextTagsItem(availableTags: contexts),
const SizedBox(height: 16),
],
),
floatingActionButton: keyboardIsOpen || !narrowView
? null
: SaveFilterFABButton(initFilter: initFilter),
),
),
);
Expand Down
15 changes: 4 additions & 11 deletions lib/presentation/filter/pages/filter_list_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import 'package:ntodotxt/common_widgets/scroll_to_top.dart';
import 'package:ntodotxt/constants/app.dart';
import 'package:ntodotxt/domain/filter/filter_model.dart';
import 'package:ntodotxt/domain/todo/todo_model.dart' show Priority;
import 'package:ntodotxt/misc.dart' show PopScopeDrawer, SnackBarHandler;
import 'package:ntodotxt/misc.dart' show PopScopeDrawer;
import 'package:ntodotxt/presentation/filter/states/filter_list_bloc.dart';
import 'package:ntodotxt/presentation/filter/states/filter_list_state.dart';

Expand All @@ -18,16 +18,9 @@ class FilterListPage extends StatelessWidget {
Widget build(BuildContext context) {
final bool isNarrowLayout =
MediaQuery.of(context).size.width < maxScreenWidthCompact;
return BlocListener<FilterListBloc, FilterListState>(
listener: (BuildContext context, FilterListState state) {
if (state is FilterListError) {
SnackBarHandler.error(context, state.message);
}
},
child: isNarrowLayout
? const FilterListViewNarrow()
: const FilterListViewWide(),
);
return isNarrowLayout
? const FilterListViewNarrow()
: const FilterListViewWide();
}
}

Expand Down
Loading

0 comments on commit 873b151

Please sign in to comment.