Merge AppState into Repository

The 'AppState' was more of a Repository state, and it's easier if it
lives inside the Repository class.
This commit is contained in:
Vishesh Handa
2020-10-23 02:01:45 +02:00
parent 770394a8f9
commit 43206219da
10 changed files with 140 additions and 129 deletions

View File

@ -4,7 +4,6 @@ import 'dart:io';
import 'package:flutter/foundation.dart' as foundation;
import 'package:flutter/material.dart';
import 'package:dart_git/git.dart';
import 'package:device_info/device_info.dart';
import 'package:dynamic_theme/dynamic_theme.dart';
import 'package:easy_localization/easy_localization.dart';
@ -20,7 +19,6 @@ import 'package:shared_preferences/shared_preferences.dart';
import 'package:gitjournal/analytics.dart';
import 'package:gitjournal/app_router.dart';
import 'package:gitjournal/app_settings.dart';
import 'package:gitjournal/appstate.dart';
import 'package:gitjournal/iap.dart';
import 'package:gitjournal/repository.dart';
import 'package:gitjournal/settings.dart';
@ -28,12 +26,9 @@ import 'package:gitjournal/themes.dart';
import 'package:gitjournal/utils/logger.dart';
class JournalApp extends StatefulWidget {
final AppState appState;
static Future main(SharedPreferences pref) async {
await Log.init();
var appState = AppState();
var settings = Settings();
settings.load(pref);
@ -46,41 +41,20 @@ class JournalApp extends StatefulWidget {
}
_sendAppUpdateEvent(appSettings);
var dir = await getApplicationDocumentsDirectory();
appState.gitBaseDirectory = dir.path;
final gitBaseDirectory = (await getApplicationDocumentsDirectory()).path;
final cacheDir = (await getApplicationSupportDirectory()).path;
await settings.migrate(pref, appState.gitBaseDirectory);
await settings.migrate(pref, gitBaseDirectory);
var gitRepoDir = settings.buildRepoPath(appState.gitBaseDirectory);
var repoDirStat = File(gitRepoDir).statSync();
if (repoDirStat.type != FileSystemEntityType.directory) {
settings.folderName = "journal";
Log.i("Calling GitInit at: $gitRepoDir");
await GitRepository.init(gitRepoDir);
settings.save();
} else {
var gitRepo = await GitRepository.load(gitRepoDir);
var remotes = gitRepo.config.remotes;
appState.remoteGitRepoConfigured = remotes.isNotEmpty;
}
appState.cacheDir = (await getApplicationSupportDirectory()).path;
var repo = await Repository.load(gitBaseDirectory, cacheDir, settings);
Widget app = ChangeNotifierProvider.value(
value: settings,
child: ChangeNotifierProvider(
create: (_) {
return Repository(appState: appState, settings: settings);
},
child: ChangeNotifierProvider(
child: JournalApp(appState),
create: (_) {
assert(appState.notesFolder != null);
return appState.notesFolder;
},
child: ChangeNotifierProvider.value(
value: repo,
child: ChangeNotifierProvider.value(
child: JournalApp(),
value: repo.notesFolder,
),
),
);
@ -169,7 +143,7 @@ class JournalApp extends StatefulWidget {
static final analytics = Analytics();
static bool isInDebugMode = false;
JournalApp(this.appState);
JournalApp();
@override
_JournalAppState createState() => _JournalAppState();

View File

@ -39,7 +39,7 @@ class AppRouter {
Route<dynamic> generateRoute(
RouteSettings routeSettings,
Repository stateContainer,
Repository repository,
String sharedText,
List<String> sharedImages,
) {
@ -49,7 +49,7 @@ class AppRouter {
settings: routeSettings,
pageBuilder: (_, __, ___) => _screenForRoute(
route,
stateContainer,
repository,
settings,
sharedText,
sharedImages,
@ -64,7 +64,7 @@ class AppRouter {
settings: routeSettings,
builder: (context) => _screenForRoute(
route,
stateContainer,
repository,
settings,
sharedText,
sharedImages,
@ -74,7 +74,7 @@ class AppRouter {
Widget _screenForRoute(
String route,
Repository stateContainer,
Repository repository,
Settings settings,
String sharedText,
List<String> sharedImages,
@ -96,7 +96,7 @@ class AppRouter {
return GitHostSetupScreen(
repoFolderName: settings.folderName,
remoteName: "origin",
onCompletedFunction: stateContainer.completeGitHostSetup,
onCompletedFunction: repository.completeGitHostSetup,
);
case '/onBoarding':
return OnBoardingScreen();
@ -113,7 +113,7 @@ class AppRouter {
Log.i("New Note - $route");
Log.i("EditorType: $et");
var rootFolder = stateContainer.appState.notesFolder;
var rootFolder = repository.notesFolder;
sharedText = null;
sharedImages = null;

View File

@ -1,25 +0,0 @@
import 'package:gitjournal/core/notes_folder_fs.dart';
enum SyncStatus {
Unknown,
Done,
Pulling,
Pushing,
Error,
}
class AppState {
SyncStatus syncStatus = SyncStatus.Unknown;
int numChanges = 0;
bool get hasJournalEntries {
return notesFolder.hasNotes;
}
NotesFolderFS notesFolder;
var gitBaseDirectory = "";
var cacheDir = "";
bool remoteGitRepoConfigured = false;
}

View File

@ -9,7 +9,6 @@ import 'package:path/path.dart' as p;
import 'package:synchronized/synchronized.dart';
import 'package:gitjournal/analytics.dart';
import 'package:gitjournal/appstate.dart';
import 'package:gitjournal/core/git_repo.dart';
import 'package:gitjournal/core/note.dart';
import 'package:gitjournal/core/notes_cache.dart';
@ -20,8 +19,15 @@ import 'package:gitjournal/features.dart';
import 'package:gitjournal/settings.dart';
import 'package:gitjournal/utils/logger.dart';
enum SyncStatus {
Unknown,
Done,
Pulling,
Pushing,
Error,
}
class Repository with ChangeNotifier {
final AppState appState;
final Settings settings;
final _opLock = Lock();
@ -35,19 +41,78 @@ class Repository with ChangeNotifier {
String repoPath;
Repository({@required this.appState, @required this.settings}) {
repoPath = settings.buildRepoPath(appState.gitBaseDirectory);
SyncStatus syncStatus = SyncStatus.Unknown;
int numChanges = 0;
bool get hasJournalEntries {
return notesFolder.hasNotes;
}
NotesFolderFS notesFolder;
final String gitBaseDirectory;
final String cacheDir;
bool remoteGitRepoConfigured = false;
static Future<Repository> load(
String gitBaseDir, String cacheDir, Settings settings) async {
var repoPath = settings.buildRepoPath(gitBaseDir);
var repoDirStat = File(repoPath).statSync();
/*
if (Platform.isIOS &&
repoDirStat.type == FileSystemEntityType.notFound &&
gitRepoDir.contains("iCloud~io~gitjournal~gitjournal")) {
Log.e("Cannot access iCloud Dir any more");
Log.e("Reverting back to internal dir");
settings.storeInternally = true;
settings.save();
gitRepoDir = settings.buildRepoPath(appState.gitBaseDirectory);
repoDirStat = File(gitRepoDir).statSync();
}*/
var remoteConfigured = false;
if (repoDirStat.type != FileSystemEntityType.directory) {
settings.folderName = "journal";
Log.i("Calling GitInit at: $repoPath");
await GitRepository.init(repoPath);
settings.save();
} else {
var gitRepo = await GitRepository.load(repoPath);
var remotes = gitRepo.config.remotes;
remoteConfigured = remotes.isNotEmpty;
}
return Repository._internal(
repoPath: repoPath,
gitBaseDirectory: gitBaseDir,
cacheDir: cacheDir,
remoteGitRepoConfigured: remoteConfigured,
settings: settings,
);
}
Repository._internal({
@required this.repoPath,
@required this.gitBaseDirectory,
@required this.cacheDir,
@required this.settings,
@required this.remoteGitRepoConfigured,
}) {
_gitRepo = GitNoteRepository(gitDirPath: repoPath, settings: settings);
appState.notesFolder = NotesFolderFS(null, _gitRepo.gitDirPath, settings);
notesFolder = NotesFolderFS(null, _gitRepo.gitDirPath, settings);
// Makes it easier to filter the analytics
getAnalytics().firebase.setUserProperty(
name: 'onboarded',
value: appState.remoteGitRepoConfigured.toString(),
value: remoteGitRepoConfigured.toString(),
);
var cachePath = p.join(appState.cacheDir, "cache.json");
var cachePath = p.join(cacheDir, "cache.json");
_notesCache = NotesCache(
filePath: cachePath,
notesBasePath: _gitRepo.gitDirPath,
@ -59,7 +124,7 @@ class Repository with ChangeNotifier {
}
void _loadFromCache() async {
await _notesCache.load(appState.notesFolder);
await _notesCache.load(notesFolder);
Log.i("Finished loading the notes cache");
await _loadNotes();
@ -69,22 +134,22 @@ class Repository with ChangeNotifier {
Future<void> _loadNotes() async {
// FIXME: We should report the notes that failed to load
return _loadLock.synchronized(() async {
await appState.notesFolder.loadRecursively();
await _notesCache.buildCache(appState.notesFolder);
await notesFolder.loadRecursively();
await _notesCache.buildCache(notesFolder);
appState.numChanges = await _gitRepo.numChanges();
numChanges = await _gitRepo.numChanges();
notifyListeners();
});
}
Future<void> syncNotes({bool doNotThrow = false}) async {
if (!appState.remoteGitRepoConfigured) {
if (!remoteGitRepoConfigured) {
Log.d("Not syncing because RemoteRepo not configured");
return true;
}
logEvent(Event.RepoSynced);
appState.syncStatus = SyncStatus.Pulling;
syncStatus = SyncStatus.Pulling;
notifyListeners();
Future noteLoadingFuture;
@ -92,7 +157,7 @@ class Repository with ChangeNotifier {
await _gitRepo.fetch();
await _gitRepo.merge();
appState.syncStatus = SyncStatus.Pushing;
syncStatus = SyncStatus.Pushing;
notifyListeners();
noteLoadingFuture = _loadNotes();
@ -100,12 +165,12 @@ class Repository with ChangeNotifier {
await _gitRepo.push();
Log.d("Synced!");
appState.syncStatus = SyncStatus.Done;
appState.numChanges = 0;
syncStatus = SyncStatus.Done;
numChanges = 0;
notifyListeners();
} catch (e, stacktrace) {
Log.e("Failed to Sync", ex: e, stacktrace: stacktrace);
appState.syncStatus = SyncStatus.Error;
syncStatus = SyncStatus.Error;
notifyListeners();
if (shouldLogGitException(e)) {
await logException(e, stacktrace);
@ -138,7 +203,7 @@ class Repository with ChangeNotifier {
_gitRepo.addFolder(newFolder).then((NoteRepoResult _) {
_syncNotes();
appState.numChanges += 1;
numChanges += 1;
notifyListeners();
});
});
@ -154,7 +219,7 @@ class Repository with ChangeNotifier {
folder.parentFS.removeFolder(folder);
_gitRepo.removeFolder(folder).then((NoteRepoResult _) {
_syncNotes();
appState.numChanges += 1;
numChanges += 1;
notifyListeners();
});
});
@ -174,7 +239,7 @@ class Repository with ChangeNotifier {
.renameFolder(oldFolderPath, folder.folderPath)
.then((NoteRepoResult _) {
_syncNotes();
appState.numChanges += 1;
numChanges += 1;
notifyListeners();
});
});
@ -191,7 +256,7 @@ class Repository with ChangeNotifier {
_gitRepo.renameNote(oldNotePath, note.filePath).then((NoteRepoResult _) {
_syncNotes();
appState.numChanges += 1;
numChanges += 1;
notifyListeners();
});
});
@ -209,7 +274,7 @@ class Repository with ChangeNotifier {
_gitRepo.renameFile(oldPath, newPath).then((NoteRepoResult _) {
_syncNotes();
appState.numChanges += 1;
numChanges += 1;
notifyListeners();
});
});
@ -229,7 +294,7 @@ class Repository with ChangeNotifier {
_gitRepo.moveNote(oldNotePath, note.filePath).then((NoteRepoResult _) {
_syncNotes();
appState.numChanges += 1;
numChanges += 1;
notifyListeners();
});
});
@ -248,7 +313,7 @@ class Repository with ChangeNotifier {
_gitRepo.addNote(note).then((NoteRepoResult _) {
_syncNotes();
appState.numChanges += 1;
numChanges += 1;
notifyListeners();
});
});
@ -263,7 +328,7 @@ class Repository with ChangeNotifier {
// FIXME: What if the Note hasn't yet been saved?
note.parent.remove(note);
_gitRepo.removeNote(note).then((NoteRepoResult _) async {
appState.numChanges += 1;
numChanges += 1;
notifyListeners();
// FIXME: Is there a way of figuring this amount dynamically?
// The '4 seconds' is taken from snack_bar.dart -> _kSnackBarDisplayDuration
@ -284,7 +349,7 @@ class Repository with ChangeNotifier {
note.parent.add(note);
_gitRepo.resetLastCommit().then((NoteRepoResult _) {
_syncNotes();
appState.numChanges -= 1;
numChanges -= 1;
notifyListeners();
});
});
@ -301,7 +366,7 @@ class Repository with ChangeNotifier {
_gitRepo.updateNote(note).then((NoteRepoResult _) {
_syncNotes();
appState.numChanges += 1;
numChanges += 1;
notifyListeners();
});
});
@ -319,7 +384,7 @@ class Repository with ChangeNotifier {
await config.saveToFS();
_gitRepo.addFolderConfig(config).then((NoteRepoResult _) {
_syncNotes();
appState.numChanges += 1;
numChanges += 1;
notifyListeners();
});
});
@ -327,7 +392,7 @@ class Repository with ChangeNotifier {
void completeGitHostSetup(String repoFolderName, String remoteName) {
() async {
var repoPath = p.join(appState.gitBaseDirectory, repoFolderName);
var repoPath = p.join(gitBaseDirectory, repoFolderName);
Log.i("completeGitHostSetup repoPath: $repoPath");
_gitRepo = GitNoteRepository(gitDirPath: repoPath, settings: settings);
@ -374,8 +439,8 @@ class Repository with ChangeNotifier {
this.repoPath = repoPath;
_notesCache.clear();
appState.remoteGitRepoConfigured = true;
appState.notesFolder.reset(repoPath);
remoteGitRepoConfigured = true;
notesFolder.reset(repoPath);
settings.folderName = repoFolderName;
settings.save();
@ -393,7 +458,7 @@ class Repository with ChangeNotifier {
}
Future<void> moveRepoToPath() async {
var newRepoPath = settings.buildRepoPath(appState.gitBaseDirectory);
var newRepoPath = settings.buildRepoPath(gitBaseDirectory);
if (newRepoPath != repoPath) {
Log.i("Old Path: $repoPath");
@ -407,7 +472,7 @@ class Repository with ChangeNotifier {
_gitRepo = GitNoteRepository(gitDirPath: repoPath, settings: settings);
_notesCache.clear();
appState.notesFolder.reset(repoPath);
notesFolder.reset(repoPath);
notifyListeners();
_loadNotes();

View File

@ -407,7 +407,7 @@ class _FolderViewState extends State<FolderView> {
}
List<Widget> _buildNoteActions() {
final appState = Provider.of<Repository>(context).appState;
final repo = Provider.of<Repository>(context);
var extraActions = PopupMenuButton<DropDownChoices>(
key: const ValueKey("PopupMenu"),
@ -443,7 +443,7 @@ class _FolderViewState extends State<FolderView> {
onPressed: _folderViewChooserSelected,
key: const ValueKey("FolderViewSelector"),
),
if (appState.remoteGitRepoConfigured) SyncButton(),
if (repo.remoteGitRepoConfigured) SyncButton(),
IconButton(
icon: const Icon(Icons.search),
onPressed: () {

View File

@ -164,8 +164,8 @@ class _GitRemoteSettingsScreenState extends State<GitRemoteSettingsScreen> {
return;
}
var stateContainer = Provider.of<Repository>(context, listen: false);
var gitDir = stateContainer.appState.gitBaseDirectory;
var repo = Provider.of<Repository>(context, listen: false);
var gitDir = repo.gitBaseDirectory;
// Figure out the next available folder
String repoFolderName = "journal_";
@ -184,7 +184,7 @@ class _GitRemoteSettingsScreenState extends State<GitRemoteSettingsScreen> {
builder: (context) => GitHostSetupScreen(
repoFolderName: repoFolderName,
remoteName: 'origin',
onCompletedFunction: stateContainer.completeGitHostSetup,
onCompletedFunction: repo.completeGitHostSetup,
),
settings: const RouteSettings(name: '/setupRemoteGit'),
);

View File

@ -65,8 +65,7 @@ class SettingsListState extends State<SettingsList> {
Widget build(BuildContext context) {
var settings = Provider.of<Settings>(context);
var appSettings = Provider.of<AppSettings>(context);
final stateContainer = Provider.of<Repository>(context);
final appState = stateContainer.appState;
final repo = Provider.of<Repository>(context);
var saveGitAuthor = (String gitAuthor) {
settings.gitAuthor = gitAuthor;
@ -228,7 +227,7 @@ class SettingsListState extends State<SettingsList> {
);
Navigator.of(context).push(route);
},
enabled: appState.remoteGitRepoConfigured,
enabled: repo.remoteGitRepoConfigured,
),
const SizedBox(height: 16.0),
ListTile(
@ -302,7 +301,7 @@ class SettingsListState extends State<SettingsList> {
settings.save();
setState(() {});
stateContainer.moveRepoToPath();
repo.moveRepoToPath();
} else {
var req = await Permission.storage.request();
if (req.isDenied) {
@ -311,7 +310,7 @@ class SettingsListState extends State<SettingsList> {
settings.save();
setState(() {});
stateContainer.moveRepoToPath();
repo.moveRepoToPath();
return;
}
settings.storeInternally = true;
@ -323,7 +322,7 @@ class SettingsListState extends State<SettingsList> {
settings.save();
setState(() {});
stateContainer.moveRepoToPath();
repo.moveRepoToPath();
return;
}
@ -332,7 +331,7 @@ class SettingsListState extends State<SettingsList> {
settings.save();
setState(() {});
stateContainer.moveRepoToPath();
repo.moveRepoToPath();
return;
}
},
@ -359,7 +358,7 @@ class SettingsListState extends State<SettingsList> {
}
}
settings.save();
stateContainer.moveRepoToPath();
repo.moveRepoToPath();
setState(() {});
},

View File

@ -492,8 +492,8 @@ class GitHostSetupScreenState extends State<GitHostSetupScreen> {
gitCloneErrorMessage = "";
});
var stateContainer = Provider.of<Repository>(context, listen: false);
var basePath = stateContainer.appState.gitBaseDirectory;
var repo = Provider.of<Repository>(context, listen: false);
var basePath = repo.gitBaseDirectory;
var settings = Provider.of<Settings>(context, listen: false);
var repoPath = p.join(basePath, widget.repoFolderName);

View File

@ -21,12 +21,12 @@ class AppDrawer extends StatelessWidget {
@override
Widget build(BuildContext context) {
Widget setupGitButton;
var appState = Provider.of<Repository>(context).appState;
var repo = Provider.of<Repository>(context);
var appSettings = Provider.of<AppSettings>(context);
var textStyle = Theme.of(context).textTheme.bodyText1;
var currentRoute = ModalRoute.of(context).settings.name;
if (!appState.remoteGitRepoConfigured) {
if (!repo.remoteGitRepoConfigured) {
setupGitButton = ListTile(
leading: Icon(Icons.sync, color: textStyle.color),
title: Text(tr('drawer.setup'), style: textStyle),

View File

@ -8,7 +8,6 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:git_bindings/git_bindings.dart';
import 'package:provider/provider.dart';
import 'package:gitjournal/appstate.dart';
import 'package:gitjournal/repository.dart';
import 'package:gitjournal/utils.dart';
@ -41,7 +40,7 @@ class _SyncButtonState extends State<SyncButton> {
@override
Widget build(BuildContext context) {
final appState = Provider.of<Repository>(context).appState;
final repo = Provider.of<Repository>(context);
if (_connectivity == ConnectivityResult.none) {
return GitPendingChangesBadge(
@ -53,7 +52,7 @@ class _SyncButtonState extends State<SyncButton> {
),
);
}
if (appState.syncStatus == SyncStatus.Pulling) {
if (repo.syncStatus == SyncStatus.Pulling) {
return BlinkingIcon(
child: GitPendingChangesBadge(
child: IconButton(
@ -64,7 +63,7 @@ class _SyncButtonState extends State<SyncButton> {
);
}
if (appState.syncStatus == SyncStatus.Pushing) {
if (repo.syncStatus == SyncStatus.Pushing) {
return BlinkingIcon(
child: GitPendingChangesBadge(
child: IconButton(
@ -87,8 +86,8 @@ class _SyncButtonState extends State<SyncButton> {
void _syncRepo() async {
try {
final container = Provider.of<Repository>(context, listen: false);
await container.syncNotes();
final repo = Provider.of<Repository>(context, listen: false);
await repo.syncNotes();
} on GitException catch (e) {
showSnackbar(context, tr('widgets.SyncButton.error', args: [e.cause]));
} catch (e) {
@ -97,9 +96,8 @@ class _SyncButtonState extends State<SyncButton> {
}
IconData _syncStatusIcon() {
final container = Provider.of<Repository>(context);
final appState = container.appState;
switch (appState.syncStatus) {
final repo = Provider.of<Repository>(context);
switch (repo.syncStatus) {
case SyncStatus.Error:
return Icons.cloud_off;
@ -172,11 +170,11 @@ class GitPendingChangesBadge extends StatelessWidget {
color: darkMode ? Colors.black : Colors.white,
);
final appState = Provider.of<Repository>(context).appState;
final repo = Provider.of<Repository>(context);
return Badge(
badgeContent: Text(appState.numChanges.toString(), style: style),
showBadge: appState.numChanges != 0,
badgeContent: Text(repo.numChanges.toString(), style: style),
showBadge: repo.numChanges != 0,
badgeColor: theme.iconTheme.color,
position: BadgePosition.topRight(top: 10.0, right: 4.0),
child: child,