Add a sync button to the AppBar

This somewhat shows what's going on with the network. It isn't ideal, as
is a bit ugly. But it's a start to show the network status, and what's
going on in the background.
This commit is contained in:
Vishesh Handa
2019-12-26 20:41:30 +01:00
parent f5558803c5
commit 6af3b03210
4 changed files with 140 additions and 17 deletions

@ -1,7 +1,15 @@
import 'package:shared_preferences/shared_preferences.dart';
import 'package:fimber/fimber.dart';
import 'package:gitjournal/core/notes_folder.dart';
enum SyncStatus {
Unknown,
Done,
Loading,
Error,
}
class AppState {
//
// Saved on Disk
@ -15,6 +23,8 @@ class AppState {
bool onBoardingCompleted = false;
SyncStatus syncStatus = SyncStatus.Unknown;
//
// Temporary
//

@ -102,7 +102,7 @@ class GitNoteRepository {
return _addNote(note, "Edited Note");
}
Future<bool> sync() async {
Future<void> sync() async {
try {
await _gitRepo.pull();
} on GitException catch (e, stacktrace) {
@ -120,8 +120,6 @@ class GitNoteRepository {
}
throw e;
}
return true;
}
}

@ -1,8 +1,10 @@
import 'package:flutter/material.dart';
import 'package:git_bindings/git_bindings.dart';
import 'package:gitjournal/appstate.dart';
import 'package:gitjournal/core/note.dart';
import 'package:gitjournal/core/notes_folder.dart';
import 'package:gitjournal/utils.dart';
import 'package:git_bindings/git_bindings.dart';
import 'package:gitjournal/screens/journal_editor.dart';
import 'package:gitjournal/screens/journal_browsing.dart';
import 'package:gitjournal/state_container.dart';
@ -19,6 +21,7 @@ class JournalListingScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final container = StateContainer.of(context);
final appState = container.appState;
var createButton = FloatingActionButton(
key: const ValueKey("FAB"),
@ -50,6 +53,7 @@ class JournalListingScreen extends StatelessWidget {
title: Text(title),
leading: GJAppBarMenuButton(),
actions: <Widget>[
if (appState.remoteGitRepoConfigured) SyncButton(),
IconButton(
icon: Icon(Icons.search),
onPressed: () {
@ -58,25 +62,29 @@ class JournalListingScreen extends StatelessWidget {
delegate: NoteSearch(allNotes),
);
},
)
),
],
),
floatingActionButton: createButton,
body: Center(
child: RefreshIndicator(
child: Scrollbar(child: journalList),
onRefresh: () async {
try {
await container.syncNotes();
} on GitException catch (exp) {
showSnackbar(context, exp.cause);
}
}),
child: Scrollbar(child: journalList),
onRefresh: () async => _syncRepo(context),
),
),
drawer: AppDrawer(),
);
}
void _syncRepo(BuildContext context) async {
final container = StateContainer.of(context);
try {
await container.syncNotes();
} on GitException catch (exp) {
showSnackbar(context, exp.cause);
}
}
void _newPost(BuildContext context) {
var route = MaterialPageRoute(
builder: (context) => JournalEditor.newNote(notesFolder));
@ -157,3 +165,95 @@ class NoteSearch extends SearchDelegate<Note> {
return journalList;
}
}
class SyncButton extends StatefulWidget {
@override
_SyncButtonState createState() => _SyncButtonState();
}
class _SyncButtonState extends State<SyncButton> {
@override
Widget build(BuildContext context) {
final container = StateContainer.of(context);
final appState = container.appState;
if (appState.syncStatus == SyncStatus.Loading) {
return RotatingIcon();
}
return IconButton(
icon: Icon(_syncStatusIcon()),
onPressed: () async {
_syncRepo();
},
);
}
void _syncRepo() async {
final container = StateContainer.of(context);
try {
await container.syncNotes();
} on GitException catch (exp) {
showSnackbar(context, exp.cause);
}
}
IconData _syncStatusIcon() {
final container = StateContainer.of(context);
final appState = container.appState;
switch (appState.syncStatus) {
case SyncStatus.Error:
return Icons.cloud_off;
case SyncStatus.Unknown:
case SyncStatus.Done:
default:
return Icons.cloud_done;
}
}
}
class RotatingIcon extends StatefulWidget {
@override
_RotatingIconState createState() => _RotatingIconState();
}
class _RotatingIconState extends State<RotatingIcon>
with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 1800),
);
_animation = CurvedAnimation(
parent: _controller,
curve: Curves.linear,
);
_controller.repeat(reverse: true);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
var button = IconButton(
icon: const Icon(Icons.loop),
onPressed: () {},
);
return RotationTransition(
child: button,
turns: _animation,
);
}
}

@ -85,16 +85,31 @@ class StateContainerState extends State<StateContainer> {
await appState.notesFolder.loadRecursively();
}
Future syncNotes() async {
Future<void> syncNotes() async {
if (!appState.remoteGitRepoConfigured) {
Fimber.d("Not syncing because RemoteRepo not configured");
return true;
}
await _gitRepo.sync();
await _loadNotes();
setState(() {
appState.syncStatus = SyncStatus.Loading;
});
return true;
try {
await _gitRepo.sync();
setState(() {
Fimber.d("Synced!");
appState.syncStatus = SyncStatus.Done;
});
} catch (Exeception) {
setState(() {
Fimber.d("Failed to Sync");
appState.syncStatus = SyncStatus.Error;
});
rethrow;
}
await _loadNotes();
}
void createFolder(NotesFolder parent, String folderName) async {