mirror of
https://github.com/GitJournal/GitJournal.git
synced 2025-07-15 07:56:11 +08:00
Dart: Add analysis_options
For now I've mostly tried to follow the same style guide as the flutter repository, with many options disabled. Eventually, maybe it would make sense to be far stricter.
This commit is contained in:
124
analysis_options.yaml
Normal file
124
analysis_options.yaml
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
# Specify analysis options.
|
||||||
|
#
|
||||||
|
# Until there are meta linter rules, each desired lint must be explicitly enabled.
|
||||||
|
# See: https://github.com/dart-lang/linter/issues/288
|
||||||
|
#
|
||||||
|
# For a list of lints, see: http://dart-lang.github.io/linter/lints/
|
||||||
|
#
|
||||||
|
analyzer:
|
||||||
|
strong-mode:
|
||||||
|
implicit-dynamic: true
|
||||||
|
errors:
|
||||||
|
missing_required_param: error
|
||||||
|
missing_return: error
|
||||||
|
todo: ignore
|
||||||
|
exclude:
|
||||||
|
- "bin/cache/**"
|
||||||
|
|
||||||
|
linter:
|
||||||
|
rules:
|
||||||
|
# these rules are documented on and in the same order as
|
||||||
|
# the Dart Lint rules page to make maintenance easier
|
||||||
|
# http://dart-lang.github.io/linter/lints/
|
||||||
|
|
||||||
|
# === error rules ===
|
||||||
|
- avoid_empty_else
|
||||||
|
- avoid_slow_async_io
|
||||||
|
- cancel_subscriptions
|
||||||
|
# - close_sinks # https://github.com/flutter/flutter/issues/5789
|
||||||
|
# - comment_references # blocked on https://github.com/dart-lang/dartdoc/issues/1153
|
||||||
|
- control_flow_in_finally
|
||||||
|
- empty_statements
|
||||||
|
- hash_and_equals
|
||||||
|
# - invariant_booleans # https://github.com/flutter/flutter/issues/5790
|
||||||
|
- iterable_contains_unrelated_type
|
||||||
|
- list_remove_unrelated_type
|
||||||
|
# - literal_only_boolean_expressions # https://github.com/flutter/flutter/issues/5791
|
||||||
|
- no_adjacent_strings_in_list
|
||||||
|
- no_duplicate_case_values
|
||||||
|
- test_types_in_equals
|
||||||
|
- throw_in_finally
|
||||||
|
- unrelated_type_equality_checks
|
||||||
|
- valid_regexps
|
||||||
|
|
||||||
|
# === style rules ===
|
||||||
|
- always_declare_return_types
|
||||||
|
# - always_put_control_body_on_new_line
|
||||||
|
- always_require_non_null_named_parameters
|
||||||
|
# - always_specify_types
|
||||||
|
- annotate_overrides
|
||||||
|
# - avoid_annotating_with_dynamic # not yet tested
|
||||||
|
# - avoid_as
|
||||||
|
# - avoid_catches_without_on_clauses # not yet tested
|
||||||
|
# - avoid_catching_errors # not yet tested
|
||||||
|
# - avoid_classes_with_only_static_members # not yet tested
|
||||||
|
# - avoid_function_literals_in_foreach_calls # not yet tested
|
||||||
|
- avoid_init_to_null
|
||||||
|
- avoid_null_checks_in_equality_operators
|
||||||
|
# - avoid_positional_boolean_parameters # not yet tested
|
||||||
|
- avoid_return_types_on_setters
|
||||||
|
# - avoid_returning_null # not yet tested
|
||||||
|
# - avoid_returning_this # not yet tested
|
||||||
|
# - avoid_setters_without_getters # not yet tested
|
||||||
|
# - avoid_types_on_closure_parameters # not yet tested
|
||||||
|
- await_only_futures
|
||||||
|
- camel_case_types
|
||||||
|
# - cascade_invocations # not yet tested
|
||||||
|
# - constant_identifier_names # https://github.com/dart-lang/linter/issues/204
|
||||||
|
- directives_ordering
|
||||||
|
- empty_catches
|
||||||
|
- empty_constructor_bodies
|
||||||
|
- implementation_imports
|
||||||
|
# - join_return_with_assignment # not yet tested
|
||||||
|
- library_names
|
||||||
|
- library_prefixes
|
||||||
|
- non_constant_identifier_names
|
||||||
|
# - omit_local_variable_types # opposite of always_specify_types
|
||||||
|
# - one_member_abstracts # too many false positives
|
||||||
|
# - only_throw_errors # https://github.com/flutter/flutter/issues/5792
|
||||||
|
- overridden_fields
|
||||||
|
- package_api_docs
|
||||||
|
- package_prefixed_library_names
|
||||||
|
# - parameter_assignments # we do this commonly
|
||||||
|
- prefer_adjacent_string_concatenation
|
||||||
|
# - prefer_collection_literals
|
||||||
|
# - prefer_conditional_assignment # not yet tested
|
||||||
|
# - prefer_const_constructors
|
||||||
|
# - prefer_constructors_over_static_methods # not yet tested
|
||||||
|
- prefer_contains
|
||||||
|
- prefer_equal_for_default_values
|
||||||
|
# - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods
|
||||||
|
# - prefer_final_fields # https://github.com/dart-lang/linter/issues/506
|
||||||
|
# - prefer_final_locals
|
||||||
|
# - prefer_foreach # not yet tested
|
||||||
|
# - prefer_function_declarations_over_variables # not yet tested
|
||||||
|
- prefer_initializing_formals
|
||||||
|
# - prefer_interpolation_to_compose_strings # not yet tested
|
||||||
|
- prefer_is_empty
|
||||||
|
- prefer_is_not_empty
|
||||||
|
- prefer_void_to_null
|
||||||
|
# - recursive_getters # https://github.com/dart-lang/linter/issues/452
|
||||||
|
- slash_for_doc_comments
|
||||||
|
# - sort_constructors_first
|
||||||
|
# - sort_unnamed_constructors_first
|
||||||
|
- super_goes_last
|
||||||
|
# - type_annotate_public_apis # subset of always_specify_types
|
||||||
|
- type_init_formals
|
||||||
|
# - unawaited_futures # https://github.com/flutter/flutter/issues/5793
|
||||||
|
- unnecessary_brace_in_string_interps
|
||||||
|
# - unnecessary_const
|
||||||
|
- unnecessary_getters_setters
|
||||||
|
# - unnecessary_lambdas # https://github.com/dart-lang/linter/issues/498
|
||||||
|
- unnecessary_new
|
||||||
|
- unnecessary_null_aware_assignments
|
||||||
|
- unnecessary_null_in_if_null_operators
|
||||||
|
# - unnecessary_overrides # https://github.com/dart-lang/linter/issues/626 and https://github.com/dart-lang/linter/issues/627
|
||||||
|
- unnecessary_statements
|
||||||
|
# - unnecessary_this
|
||||||
|
- use_rethrow_when_possible
|
||||||
|
# - use_setters_to_change_properties # not yet tested
|
||||||
|
# - use_string_buffers # https://github.com/dart-lang/linter/pull/664
|
||||||
|
# - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review
|
||||||
|
|
||||||
|
# === pub rules ===
|
||||||
|
- package_names
|
@ -11,7 +11,7 @@ Future<Directory> getGitBaseDirectory() async {
|
|||||||
if (path == null) {
|
if (path == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new Directory(path);
|
return Directory(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> gitClone(String cloneUrl, String folderName) async {
|
Future<void> gitClone(String cloneUrl, String folderName) async {
|
||||||
@ -43,7 +43,7 @@ Future<String> generateSSHKeys({@required String comment}) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String publicKey = await _platform.invokeMethod('getSSHPublicKey', {});
|
String publicKey = await _platform.invokeMethod('getSSHPublicKey');
|
||||||
print("Public Key " + publicKey);
|
print("Public Key " + publicKey);
|
||||||
return publicKey;
|
return publicKey;
|
||||||
} on PlatformException catch (e) {
|
} on PlatformException catch (e) {
|
||||||
@ -65,7 +65,7 @@ Future<void> setSshKeys({
|
|||||||
});
|
});
|
||||||
} on PlatformException catch (e) {
|
} on PlatformException catch (e) {
|
||||||
print("Failed to generateSSHKeys: '${e.message}'.");
|
print("Failed to generateSSHKeys: '${e.message}'.");
|
||||||
throw e;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,13 +18,13 @@ Future migrateGitRepo({
|
|||||||
var fromBasePath = p.join(gitBasePath, fromGitBasePath);
|
var fromBasePath = p.join(gitBasePath, fromGitBasePath);
|
||||||
var toBasePath = p.join(gitBasePath, toGitBasePath);
|
var toBasePath = p.join(gitBasePath, toGitBasePath);
|
||||||
|
|
||||||
final dir = new Directory(fromBasePath);
|
final dir = Directory(fromBasePath);
|
||||||
var lister = dir.list(recursive: false);
|
var lister = dir.list(recursive: false);
|
||||||
await for (var fileEntity in lister) {
|
await for (var fileEntity in lister) {
|
||||||
if (fileEntity is! File) {
|
if (fileEntity is! File) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var file = fileEntity as File;
|
File file = fileEntity;
|
||||||
var fileName = p.basename(file.path);
|
var fileName = p.basename(file.path);
|
||||||
var toPath = p.join(toBasePath, fileName);
|
var toPath = p.join(toBasePath, fileName);
|
||||||
|
|
||||||
|
@ -13,10 +13,10 @@ enum GitHostType {
|
|||||||
GitHost createGitHost(GitHostType type) {
|
GitHost createGitHost(GitHostType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case GitHostType.GitHub:
|
case GitHostType.GitHub:
|
||||||
return new GitHub();
|
return GitHub();
|
||||||
|
|
||||||
case GitHostType.GitLab:
|
case GitHostType.GitLab:
|
||||||
return new GitLab();
|
return GitLab();
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
|
@ -25,7 +25,7 @@ class GitHub implements GitHost {
|
|||||||
|
|
||||||
print("GitHub: Called onUrl with " + call.arguments.toString());
|
print("GitHub: Called onUrl with " + call.arguments.toString());
|
||||||
|
|
||||||
var url = call.arguments["URL"];
|
String url = call.arguments["URL"];
|
||||||
var uri = Uri.parse(url);
|
var uri = Uri.parse(url);
|
||||||
var authCode = uri.queryParameters['code'];
|
var authCode = uri.queryParameters['code'];
|
||||||
if (authCode == null) {
|
if (authCode == null) {
|
||||||
@ -88,7 +88,7 @@ class GitHub implements GitHost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<dynamic> list = jsonDecode(response.body);
|
List<dynamic> list = jsonDecode(response.body);
|
||||||
var repos = new List<GitRepo>();
|
var repos = List<GitRepo>();
|
||||||
list.forEach((dynamic d) {
|
list.forEach((dynamic d) {
|
||||||
var map = Map<String, dynamic>.from(d);
|
var map = Map<String, dynamic>.from(d);
|
||||||
var repo = _repoFromJson(map);
|
var repo = _repoFromJson(map);
|
||||||
@ -107,7 +107,7 @@ class GitHub implements GitHost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var url = "https://api.github.com/user/repos?access_token=$_accessCode";
|
var url = "https://api.github.com/user/repos?access_token=$_accessCode";
|
||||||
Map<String, dynamic> data = {
|
var data = <String, dynamic>{
|
||||||
'name': name,
|
'name': name,
|
||||||
'private': true,
|
'private': true,
|
||||||
};
|
};
|
||||||
@ -134,11 +134,12 @@ class GitHub implements GitHost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
print("GitHub createRepo: " + response.body);
|
print("GitHub createRepo: " + response.body);
|
||||||
var map = json.decode(response.body);
|
Map<String, dynamic> map = json.decode(response.body);
|
||||||
return _repoFromJson(map);
|
return _repoFromJson(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Proper error when the repo exists!
|
// FIXME: Proper error when the repo exists!
|
||||||
|
@override
|
||||||
Future addDeployKey(String sshPublicKey, String repo) async {
|
Future addDeployKey(String sshPublicKey, String repo) async {
|
||||||
if (_accessCode.isEmpty) {
|
if (_accessCode.isEmpty) {
|
||||||
throw GitHostException.MissingAccessCode;
|
throw GitHostException.MissingAccessCode;
|
||||||
@ -147,7 +148,7 @@ class GitHub implements GitHost {
|
|||||||
var url =
|
var url =
|
||||||
"https://api.github.com/repos/$repo/keys?access_token=$_accessCode";
|
"https://api.github.com/repos/$repo/keys?access_token=$_accessCode";
|
||||||
|
|
||||||
Map<String, dynamic> data = {
|
var data = <String, dynamic>{
|
||||||
'title': "GitJournal",
|
'title': "GitJournal",
|
||||||
'key': sshPublicKey,
|
'key': sshPublicKey,
|
||||||
'read_only': false,
|
'read_only': false,
|
||||||
@ -172,7 +173,7 @@ class GitHub implements GitHost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
GitRepo _repoFromJson(Map<String, dynamic> parsedJson) {
|
GitRepo _repoFromJson(Map<String, dynamic> parsedJson) {
|
||||||
return new GitRepo(
|
return GitRepo(
|
||||||
fullName: parsedJson['full_name'],
|
fullName: parsedJson['full_name'],
|
||||||
cloneUrl: parsedJson['ssh_url'],
|
cloneUrl: parsedJson['ssh_url'],
|
||||||
);
|
);
|
||||||
|
@ -80,7 +80,7 @@ class GitLab implements GitHost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<dynamic> list = jsonDecode(response.body);
|
List<dynamic> list = jsonDecode(response.body);
|
||||||
var repos = new List<GitRepo>();
|
var repos = List<GitRepo>();
|
||||||
list.forEach((dynamic d) {
|
list.forEach((dynamic d) {
|
||||||
var map = Map<String, dynamic>.from(d);
|
var map = Map<String, dynamic>.from(d);
|
||||||
var repo = _repoFromJson(map);
|
var repo = _repoFromJson(map);
|
||||||
@ -99,7 +99,7 @@ class GitLab implements GitHost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var url = "https://gitlab.com/api/v4/projects?access_token=$_accessCode";
|
var url = "https://gitlab.com/api/v4/projects?access_token=$_accessCode";
|
||||||
Map<String, dynamic> data = {
|
var data = <String, dynamic>{
|
||||||
'name': name,
|
'name': name,
|
||||||
'visibility': 'private',
|
'visibility': 'private',
|
||||||
};
|
};
|
||||||
@ -126,7 +126,7 @@ class GitLab implements GitHost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
print("GitLab createRepo: " + response.body);
|
print("GitLab createRepo: " + response.body);
|
||||||
var map = json.decode(response.body);
|
Map<String, dynamic> map = json.decode(response.body);
|
||||||
return _repoFromJson(map);
|
return _repoFromJson(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,7 +140,7 @@ class GitLab implements GitHost {
|
|||||||
var url =
|
var url =
|
||||||
"https://gitlab.com/api/v4/projects/$repo/deploy_keys?access_token=$_accessCode";
|
"https://gitlab.com/api/v4/projects/$repo/deploy_keys?access_token=$_accessCode";
|
||||||
|
|
||||||
Map<String, dynamic> data = {
|
var data = {
|
||||||
'title': "GitJournal",
|
'title': "GitJournal",
|
||||||
'key': sshPublicKey,
|
'key': sshPublicKey,
|
||||||
'can_push': true,
|
'can_push': true,
|
||||||
@ -165,7 +165,7 @@ class GitLab implements GitHost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
GitRepo _repoFromJson(Map<String, dynamic> parsedJson) {
|
GitRepo _repoFromJson(Map<String, dynamic> parsedJson) {
|
||||||
return new GitRepo(
|
return GitRepo(
|
||||||
fullName: parsedJson['path_with_namespace'],
|
fullName: parsedJson['path_with_namespace'],
|
||||||
cloneUrl: parsedJson['ssh_url_to_repo'],
|
cloneUrl: parsedJson['ssh_url_to_repo'],
|
||||||
);
|
);
|
||||||
@ -202,10 +202,10 @@ class GitLab implements GitHost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _randomString(int length) {
|
String _randomString(int length) {
|
||||||
var rand = new Random();
|
var rand = Random();
|
||||||
var codeUnits = new List.generate(length, (index) {
|
var codeUnits = List.generate(length, (index) {
|
||||||
return rand.nextInt(33) + 89;
|
return rand.nextInt(33) + 89;
|
||||||
});
|
});
|
||||||
|
|
||||||
return new String.fromCharCodes(codeUnits);
|
return String.fromCharCodes(codeUnits);
|
||||||
}
|
}
|
||||||
|
@ -28,9 +28,9 @@ class JournalApp extends StatelessWidget {
|
|||||||
stateContainer.completeGitHostSetup();
|
stateContainer.completeGitHostSetup();
|
||||||
};
|
};
|
||||||
|
|
||||||
return new MaterialApp(
|
return MaterialApp(
|
||||||
title: 'GitJournal',
|
title: 'GitJournal',
|
||||||
theme: new ThemeData(
|
theme: ThemeData(
|
||||||
brightness: Brightness.light,
|
brightness: Brightness.light,
|
||||||
primaryColor: Color(0xFF66bb6a),
|
primaryColor: Color(0xFF66bb6a),
|
||||||
primaryColorLight: Color(0xFF98ee99),
|
primaryColorLight: Color(0xFF98ee99),
|
||||||
|
@ -6,11 +6,11 @@ const basePath = "journal";
|
|||||||
String cloneUrl = "git@github.com:GitJournal/journal_test.git";
|
String cloneUrl = "git@github.com:GitJournal/journal_test.git";
|
||||||
|
|
||||||
class GitApp extends StatelessWidget {
|
class GitApp extends StatelessWidget {
|
||||||
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return new MaterialApp(
|
return MaterialApp(
|
||||||
title: 'Git App',
|
title: 'Git App',
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
key: _scaffoldKey,
|
key: _scaffoldKey,
|
||||||
@ -30,16 +30,16 @@ class GitApp extends StatelessWidget {
|
|||||||
var text = "Success";
|
var text = "Success";
|
||||||
this._scaffoldKey.currentState
|
this._scaffoldKey.currentState
|
||||||
..removeCurrentSnackBar()
|
..removeCurrentSnackBar()
|
||||||
..showSnackBar(new SnackBar(content: new Text(text)));
|
..showSnackBar(SnackBar(content: Text(text)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _sendError(String text) {
|
void _sendError(String text) {
|
||||||
this._scaffoldKey.currentState
|
this._scaffoldKey.currentState
|
||||||
..removeCurrentSnackBar()
|
..removeCurrentSnackBar()
|
||||||
..showSnackBar(new SnackBar(content: new Text("ERROR: " + text)));
|
..showSnackBar(SnackBar(content: Text("ERROR: " + text)));
|
||||||
}
|
}
|
||||||
|
|
||||||
_buildGitButtons() {
|
List<Widget> _buildGitButtons() {
|
||||||
return <Widget>[
|
return <Widget>[
|
||||||
RaisedButton(
|
RaisedButton(
|
||||||
child: Text("Generate Keys"),
|
child: Text("Generate Keys"),
|
||||||
|
@ -21,9 +21,9 @@ void main() async {
|
|||||||
|
|
||||||
await FlutterCrashlytics().initialize();
|
await FlutterCrashlytics().initialize();
|
||||||
|
|
||||||
runZoned<Future<Null>>(() async {
|
runZoned<Future<void>>(() async {
|
||||||
await runJournalApp();
|
await runJournalApp();
|
||||||
}, onError: (error, stackTrace) async {
|
}, onError: (Error error, StackTrace stackTrace) async {
|
||||||
await FlutterCrashlytics()
|
await FlutterCrashlytics()
|
||||||
.reportCrash(error, stackTrace, forceCrash: false);
|
.reportCrash(error, stackTrace, forceCrash: false);
|
||||||
});
|
});
|
||||||
@ -60,7 +60,7 @@ Future runJournalApp() async {
|
|||||||
|
|
||||||
await Settings.instance.load();
|
await Settings.instance.load();
|
||||||
|
|
||||||
runApp(new StateContainer(
|
runApp(StateContainer(
|
||||||
localGitRepoConfigured: localGitRepoConfigured,
|
localGitRepoConfigured: localGitRepoConfigured,
|
||||||
remoteGitRepoConfigured: remoteGitRepoConfigured,
|
remoteGitRepoConfigured: remoteGitRepoConfigured,
|
||||||
localGitRepoPath: localGitRepoPath,
|
localGitRepoPath: localGitRepoPath,
|
||||||
|
@ -1,22 +1,18 @@
|
|||||||
import 'package:journal/datetime_utils.dart';
|
import 'package:journal/datetime_utils.dart';
|
||||||
|
|
||||||
typedef NoteAdder(Note note);
|
class Note implements Comparable<Note> {
|
||||||
typedef NoteRemover(Note note);
|
|
||||||
typedef NoteUpdator(Note note);
|
|
||||||
|
|
||||||
class Note implements Comparable {
|
|
||||||
String fileName;
|
String fileName;
|
||||||
DateTime created;
|
DateTime created;
|
||||||
String body;
|
String body;
|
||||||
|
|
||||||
Map<String, dynamic> extraProperties = new Map<String, dynamic>();
|
Map<String, dynamic> extraProperties = Map<String, dynamic>();
|
||||||
|
|
||||||
Note({this.created, this.body, this.fileName, this.extraProperties}) {
|
Note({this.created, this.body, this.fileName, this.extraProperties}) {
|
||||||
if (created == null) {
|
if (created == null) {
|
||||||
created = DateTime(0, 0, 0, 0, 0, 0, 0, 0);
|
created = DateTime(0, 0, 0, 0, 0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
if (extraProperties == null) {
|
if (extraProperties == null) {
|
||||||
extraProperties = new Map<String, dynamic>();
|
extraProperties = Map<String, dynamic>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,10 +28,12 @@ class Note implements Comparable {
|
|||||||
var createdStr = json['created'].toString();
|
var createdStr = json['created'].toString();
|
||||||
try {
|
try {
|
||||||
created = DateTime.parse(json['created']).toLocal();
|
created = DateTime.parse(json['created']).toLocal();
|
||||||
} catch (ex) {}
|
} catch (ex) {
|
||||||
|
// Ignore it
|
||||||
|
}
|
||||||
|
|
||||||
if (created == null) {
|
if (created == null) {
|
||||||
var regex = new RegExp(
|
var regex = RegExp(
|
||||||
r"(\d{4})-(\d{2})-(\d{2})T(\d{2})\:(\d{2})\:(\d{2})\+(\d{2})\:(\d{2})");
|
r"(\d{4})-(\d{2})-(\d{2})T(\d{2})\:(\d{2})\:(\d{2})\+(\d{2})\:(\d{2})");
|
||||||
if (regex.hasMatch(createdStr)) {
|
if (regex.hasMatch(createdStr)) {
|
||||||
// FIXME: Handle the timezone!
|
// FIXME: Handle the timezone!
|
||||||
@ -57,7 +55,7 @@ class Note implements Comparable {
|
|||||||
json.remove("body");
|
json.remove("body");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Note(
|
return Note(
|
||||||
fileName: fileName,
|
fileName: fileName,
|
||||||
created: created,
|
created: created,
|
||||||
body: body,
|
body: body,
|
||||||
@ -96,7 +94,8 @@ class Note implements Comparable {
|
|||||||
|
|
||||||
static bool _equalMaps(Map a, Map b) {
|
static bool _equalMaps(Map a, Map b) {
|
||||||
if (a.length != b.length) return false;
|
if (a.length != b.length) return false;
|
||||||
return a.keys.every((key) => b.containsKey(key) && a[key] == b[key]);
|
return a.keys
|
||||||
|
.every((dynamic key) => b.containsKey(key) && a[key] == b[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -105,7 +104,7 @@ class Note implements Comparable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int compareTo(other) {
|
int compareTo(Note other) {
|
||||||
if (other == null) {
|
if (other == null) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:journal/note.dart';
|
import 'package:journal/note.dart';
|
||||||
import 'package:journal/state_container.dart';
|
import 'package:journal/state_container.dart';
|
||||||
import 'package:journal/widgets/note_header.dart';
|
import 'package:journal/widgets/note_header.dart';
|
||||||
@ -15,25 +14,24 @@ class NoteEditor extends StatefulWidget {
|
|||||||
@override
|
@override
|
||||||
NoteEditorState createState() {
|
NoteEditorState createState() {
|
||||||
if (note == null) {
|
if (note == null) {
|
||||||
return new NoteEditorState();
|
return NoteEditorState();
|
||||||
} else {
|
} else {
|
||||||
return new NoteEditorState.fromNote(note);
|
return NoteEditorState.fromNote(note);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NoteEditorState extends State<NoteEditor> {
|
class NoteEditorState extends State<NoteEditor> {
|
||||||
Note note = new Note();
|
Note note = Note();
|
||||||
final bool newNote;
|
final bool newNote;
|
||||||
TextEditingController _textController = new TextEditingController();
|
TextEditingController _textController = TextEditingController();
|
||||||
|
|
||||||
NoteEditorState() : newNote = true {
|
NoteEditorState() : newNote = true {
|
||||||
note.created = new DateTime.now();
|
note.created = DateTime.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
NoteEditorState.fromNote(Note n) : newNote = false {
|
NoteEditorState.fromNote(this.note) : newNote = false {
|
||||||
note = n;
|
_textController = TextEditingController(text: note.body);
|
||||||
_textController = new TextEditingController(text: note.body);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -62,7 +60,7 @@ class NoteEditorState extends State<NoteEditor> {
|
|||||||
autofocus: true,
|
autofocus: true,
|
||||||
keyboardType: TextInputType.multiline,
|
keyboardType: TextInputType.multiline,
|
||||||
maxLines: null,
|
maxLines: null,
|
||||||
decoration: new InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: 'Write here',
|
hintText: 'Write here',
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
),
|
),
|
||||||
@ -71,10 +69,10 @@ class NoteEditorState extends State<NoteEditor> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
var title = newNote ? "New Journal Entry" : "Edit Journal Entry";
|
var title = newNote ? "Journal Entry" : "Edit Journal Entry";
|
||||||
var newJournalScreen = new Scaffold(
|
var newJournalScreen = Scaffold(
|
||||||
appBar: new AppBar(
|
appBar: AppBar(
|
||||||
title: new Text(title),
|
title: Text(title),
|
||||||
),
|
),
|
||||||
body: Padding(
|
body: Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
@ -108,18 +106,18 @@ class NoteEditorState extends State<NoteEditor> {
|
|||||||
? "Do you want to discard the entry"
|
? "Do you want to discard the entry"
|
||||||
: "Do you want to discard the changes?";
|
: "Do you want to discard the changes?";
|
||||||
|
|
||||||
return new AlertDialog(
|
return AlertDialog(
|
||||||
// FIXME: Change this to 'Save' vs 'Discard'
|
// FIXME: Change this to 'Save' vs 'Discard'
|
||||||
title: new Text('Are you sure?'),
|
title: Text('Are you sure?'),
|
||||||
content: new Text(title),
|
content: Text(title),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
new FlatButton(
|
FlatButton(
|
||||||
onPressed: () => Navigator.of(context).pop(false),
|
onPressed: () => Navigator.of(context).pop(false),
|
||||||
child: new Text('No'),
|
child: Text('No'),
|
||||||
),
|
),
|
||||||
new FlatButton(
|
FlatButton(
|
||||||
onPressed: () => Navigator.of(context).pop(true),
|
onPressed: () => Navigator.of(context).pop(true),
|
||||||
child: new Text('Yes'),
|
child: Text('Yes'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -16,7 +16,7 @@ class NoteBrowsingScreen extends StatefulWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
NoteBrowsingScreenState createState() {
|
NoteBrowsingScreenState createState() {
|
||||||
return new NoteBrowsingScreenState(noteIndex: noteIndex);
|
return NoteBrowsingScreenState(noteIndex: noteIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,28 +24,28 @@ class NoteBrowsingScreenState extends State<NoteBrowsingScreen> {
|
|||||||
PageController pageController;
|
PageController pageController;
|
||||||
|
|
||||||
NoteBrowsingScreenState({@required int noteIndex}) {
|
NoteBrowsingScreenState({@required int noteIndex}) {
|
||||||
pageController = new PageController(initialPage: noteIndex);
|
pageController = PageController(initialPage: noteIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var pageView = new PageView.builder(
|
var pageView = PageView.builder(
|
||||||
controller: pageController,
|
controller: pageController,
|
||||||
itemCount: widget.notes.length,
|
itemCount: widget.notes.length,
|
||||||
itemBuilder: (BuildContext context, int pos) {
|
itemBuilder: (BuildContext context, int pos) {
|
||||||
return new NoteViewer(note: widget.notes[pos]);
|
return NoteViewer(note: widget.notes[pos]);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return new Scaffold(
|
return Scaffold(
|
||||||
appBar: new AppBar(
|
appBar: AppBar(
|
||||||
title: new Text('TIMELINE'),
|
title: Text('TIMELINE'),
|
||||||
),
|
),
|
||||||
body: pageView,
|
body: pageView,
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
child: Icon(Icons.edit),
|
child: Icon(Icons.edit),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
var route = new MaterialPageRoute(builder: (context) {
|
var route = MaterialPageRoute<Widget>(builder: (context) {
|
||||||
int currentIndex = pageController.page.toInt();
|
int currentIndex = pageController.page.toInt();
|
||||||
assert(currentIndex >= 0);
|
assert(currentIndex >= 0);
|
||||||
assert(currentIndex < widget.notes.length);
|
assert(currentIndex < widget.notes.length);
|
||||||
@ -68,8 +68,8 @@ class NoteViewer extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var view = new SingleChildScrollView(
|
var view = SingleChildScrollView(
|
||||||
child: new Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
NoteHeader(note),
|
NoteHeader(note),
|
||||||
Text(note.body, style: _biggerFont),
|
Text(note.body, style: _biggerFont),
|
||||||
@ -85,21 +85,21 @@ class NoteViewer extends StatelessWidget {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
Widget _buildFooter(BuildContext context) {
|
Widget _buildFooter(BuildContext context) {
|
||||||
return new Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(top: 8.0, bottom: 8.0),
|
padding: const EdgeInsets.only(top: 8.0, bottom: 8.0),
|
||||||
child: new Row(
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
new IconButton(
|
IconButton(
|
||||||
icon: new Icon(Icons.arrow_left),
|
icon: Icon(Icons.arrow_left),
|
||||||
tooltip: 'Previous Entry',
|
tooltip: 'Previous Entry',
|
||||||
onPressed: showPrevNoteFunc,
|
onPressed: showPrevNoteFunc,
|
||||||
),
|
),
|
||||||
new Expanded(
|
Expanded(
|
||||||
flex: 10,
|
flex: 10,
|
||||||
child: new Text(''),
|
child: Text(''),
|
||||||
),
|
),
|
||||||
new IconButton(
|
IconButton(
|
||||||
icon: new Icon(Icons.arrow_right),
|
icon: Icon(Icons.arrow_right),
|
||||||
tooltip: 'Next Entry',
|
tooltip: 'Next Entry',
|
||||||
onPressed: showNextNoteFunc,
|
onPressed: showNextNoteFunc,
|
||||||
),
|
),
|
||||||
|
@ -5,7 +5,7 @@ import 'apis/githost_factory.dart';
|
|||||||
class OAuthApp extends StatefulWidget {
|
class OAuthApp extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
OAuthAppState createState() {
|
OAuthAppState createState() {
|
||||||
return new OAuthAppState();
|
return OAuthAppState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -15,6 +15,7 @@ var key =
|
|||||||
class OAuthAppState extends State<OAuthApp> {
|
class OAuthAppState extends State<OAuthApp> {
|
||||||
GitHost githost;
|
GitHost githost;
|
||||||
|
|
||||||
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
@ -26,7 +27,7 @@ class OAuthAppState extends State<OAuthApp> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return new MaterialApp(
|
return MaterialApp(
|
||||||
title: 'OAuth App',
|
title: 'OAuth App',
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
|
@ -14,7 +14,7 @@ class GitHostSetupAutoConfigure extends StatefulWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
GitHostSetupAutoConfigureState createState() {
|
GitHostSetupAutoConfigureState createState() {
|
||||||
return new GitHostSetupAutoConfigureState();
|
return GitHostSetupAutoConfigureState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ class GitHostSetupScreen extends StatefulWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
GitHostSetupScreenState createState() {
|
GitHostSetupScreenState createState() {
|
||||||
return new GitHostSetupScreenState();
|
return GitHostSetupScreenState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ class GitHostSetupScreenState extends State<GitHostSetupScreen> {
|
|||||||
var pageController = PageController();
|
var pageController = PageController();
|
||||||
int _currentPageIndex = 0;
|
int _currentPageIndex = 0;
|
||||||
|
|
||||||
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
|
|
||||||
String publicKey = "";
|
String publicKey = "";
|
||||||
bool _canLaunchDeployKeyPage = false;
|
bool _canLaunchDeployKeyPage = false;
|
||||||
@ -283,16 +283,16 @@ class GitHostSetupScreenState extends State<GitHostSetupScreen> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
var scaffold = new Scaffold(
|
var scaffold = Scaffold(
|
||||||
key: _scaffoldKey,
|
key: _scaffoldKey,
|
||||||
body: new Container(
|
body: Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: double.infinity,
|
height: double.infinity,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
alignment: FractionalOffset.bottomCenter,
|
alignment: FractionalOffset.bottomCenter,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
pageView,
|
pageView,
|
||||||
new DotsIndicator(
|
DotsIndicator(
|
||||||
numberOfDot: pageCount,
|
numberOfDot: pageCount,
|
||||||
position: _currentPageIndex,
|
position: _currentPageIndex,
|
||||||
dotActiveColor: Theme.of(context).primaryColorDark,
|
dotActiveColor: Theme.of(context).primaryColorDark,
|
||||||
@ -303,7 +303,7 @@ class GitHostSetupScreenState extends State<GitHostSetupScreen> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return new WillPopScope(
|
return WillPopScope(
|
||||||
onWillPop: () {
|
onWillPop: () {
|
||||||
if (_currentPageIndex != 0) {
|
if (_currentPageIndex != 0) {
|
||||||
pageController.previousPage(
|
pageController.previousPage(
|
||||||
@ -348,7 +348,7 @@ class GitHostSetupScreenState extends State<GitHostSetupScreen> {
|
|||||||
var text = "Public Key copied to Clipboard";
|
var text = "Public Key copied to Clipboard";
|
||||||
this._scaffoldKey.currentState
|
this._scaffoldKey.currentState
|
||||||
..removeCurrentSnackBar()
|
..removeCurrentSnackBar()
|
||||||
..showSnackBar(new SnackBar(content: new Text(text)));
|
..showSnackBar(SnackBar(content: Text(text)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _launchDeployKeyPage() async {
|
void _launchDeployKeyPage() async {
|
||||||
@ -423,8 +423,8 @@ class GitHostSetupScreenState extends State<GitHostSetupScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future _removeExistingClone(String baseDirPath) async {
|
Future _removeExistingClone(String baseDirPath) async {
|
||||||
var baseDir = new Directory(p.join(baseDirPath, "journal"));
|
var baseDir = Directory(p.join(baseDirPath, "journal"));
|
||||||
var dotGitDir = new Directory(p.join(baseDir.path, ".git"));
|
var dotGitDir = Directory(p.join(baseDir.path, ".git"));
|
||||||
bool exists = await dotGitDir.exists();
|
bool exists = await dotGitDir.exists();
|
||||||
if (exists) {
|
if (exists) {
|
||||||
print("Removing " + baseDir.path);
|
print("Removing " + baseDir.path);
|
||||||
@ -498,7 +498,7 @@ class GitHostSetupCreateRepo extends StatefulWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
GitHostSetupCreateRepoState createState() {
|
GitHostSetupCreateRepoState createState() {
|
||||||
return new GitHostSetupCreateRepoState();
|
return GitHostSetupCreateRepoState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ class GitHostSetupUrl extends StatefulWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
GitHostSetupUrlState createState() {
|
GitHostSetupUrlState createState() {
|
||||||
return new GitHostSetupUrlState();
|
return GitHostSetupUrlState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ class GitHostSetupUrlState extends State<GitHostSetupUrl> {
|
|||||||
return 'Only SSH urls are currently accepted';
|
return 'Only SSH urls are currently accepted';
|
||||||
}
|
}
|
||||||
|
|
||||||
RegExp regExp = new RegExp(r"[a-zA-Z0-9.]+@[a-zA-Z0-9.]+:.+");
|
RegExp regExp = RegExp(r"[a-zA-Z0-9.]+@[a-zA-Z0-9.]+:.+");
|
||||||
if (!regExp.hasMatch(value)) {
|
if (!regExp.hasMatch(value)) {
|
||||||
return "Invalid Input";
|
return "Invalid Input";
|
||||||
}
|
}
|
||||||
|
@ -8,23 +8,23 @@ import 'package:journal/widgets/app_drawer.dart';
|
|||||||
import 'package:journal/widgets/journal_list.dart';
|
import 'package:journal/widgets/journal_list.dart';
|
||||||
|
|
||||||
class HomeScreen extends StatelessWidget {
|
class HomeScreen extends StatelessWidget {
|
||||||
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final container = StateContainer.of(context);
|
final container = StateContainer.of(context);
|
||||||
final appState = container.appState;
|
final appState = container.appState;
|
||||||
|
|
||||||
var createButton = new FloatingActionButton(
|
var createButton = FloatingActionButton(
|
||||||
onPressed: () => _newPost(context),
|
onPressed: () => _newPost(context),
|
||||||
child: new Icon(Icons.add),
|
child: Icon(Icons.add),
|
||||||
);
|
);
|
||||||
|
|
||||||
var journalList = JournalList(
|
var journalList = JournalList(
|
||||||
notes: appState.notes,
|
notes: appState.notes,
|
||||||
noteSelectedFunction: (noteIndex) {
|
noteSelectedFunction: (noteIndex) {
|
||||||
var route = new MaterialPageRoute(
|
var route = MaterialPageRoute(
|
||||||
builder: (context) => new NoteBrowsingScreen(
|
builder: (context) => NoteBrowsingScreen(
|
||||||
notes: appState.notes,
|
notes: appState.notes,
|
||||||
noteIndex: noteIndex,
|
noteIndex: noteIndex,
|
||||||
),
|
),
|
||||||
@ -41,10 +41,10 @@ class HomeScreen extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return new Scaffold(
|
return Scaffold(
|
||||||
key: _scaffoldKey,
|
key: _scaffoldKey,
|
||||||
appBar: new AppBar(
|
appBar: AppBar(
|
||||||
title: new Text('GitJournal'),
|
title: Text('GitJournal'),
|
||||||
leading: appBarMenuButton,
|
leading: appBarMenuButton,
|
||||||
),
|
),
|
||||||
floatingActionButton: createButton,
|
floatingActionButton: createButton,
|
||||||
@ -57,16 +57,16 @@ class HomeScreen extends StatelessWidget {
|
|||||||
} on GitException catch (exp) {
|
} on GitException catch (exp) {
|
||||||
_scaffoldKey.currentState
|
_scaffoldKey.currentState
|
||||||
..removeCurrentSnackBar()
|
..removeCurrentSnackBar()
|
||||||
..showSnackBar(new SnackBar(content: new Text(exp.cause)));
|
..showSnackBar(SnackBar(content: Text(exp.cause)));
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
drawer: new AppDrawer(),
|
drawer: AppDrawer(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _newPost(BuildContext context) {
|
void _newPost(BuildContext context) {
|
||||||
var route = new MaterialPageRoute(builder: (context) => new NoteEditor());
|
var route = MaterialPageRoute(builder: (context) => NoteEditor());
|
||||||
Navigator.of(context).push(route);
|
Navigator.of(context).push(route);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import 'package:package_info/package_info.dart';
|
|||||||
class SettingsScreen extends StatelessWidget {
|
class SettingsScreen extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return new Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text('Settings'),
|
title: Text('Settings'),
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
@ -24,7 +24,7 @@ class SettingsScreen extends StatelessWidget {
|
|||||||
class SettingsList extends StatefulWidget {
|
class SettingsList extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
SettingsListState createState() {
|
SettingsListState createState() {
|
||||||
return new SettingsListState();
|
return SettingsListState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +142,7 @@ class SettingsHeader extends StatelessWidget {
|
|||||||
class VersionNumberTile extends StatefulWidget {
|
class VersionNumberTile extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
VersionNumberTileState createState() {
|
VersionNumberTileState createState() {
|
||||||
return new VersionNumberTileState();
|
return VersionNumberTileState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,12 +59,12 @@ class StateContainerState extends State<StateContainer> {
|
|||||||
assert(appState.localGitRepoConfigured);
|
assert(appState.localGitRepoConfigured);
|
||||||
|
|
||||||
if (appState.remoteGitRepoConfigured) {
|
if (appState.remoteGitRepoConfigured) {
|
||||||
noteRepo = new GitNoteRepository(
|
noteRepo = GitNoteRepository(
|
||||||
baseDirectory: appState.gitBaseDirectory,
|
baseDirectory: appState.gitBaseDirectory,
|
||||||
dirName: appState.remoteGitRepoPath,
|
dirName: appState.remoteGitRepoPath,
|
||||||
);
|
);
|
||||||
} else if (appState.localGitRepoConfigured) {
|
} else if (appState.localGitRepoConfigured) {
|
||||||
noteRepo = new GitNoteRepository(
|
noteRepo = GitNoteRepository(
|
||||||
baseDirectory: appState.gitBaseDirectory,
|
baseDirectory: appState.gitBaseDirectory,
|
||||||
dirName: appState.localGitRepoPath,
|
dirName: appState.localGitRepoPath,
|
||||||
);
|
);
|
||||||
@ -80,9 +80,9 @@ class StateContainerState extends State<StateContainer> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void removeExistingRemoteClone() async {
|
void removeExistingRemoteClone() async {
|
||||||
var remoteGitDir = new Directory(
|
var remoteGitDir = Directory(
|
||||||
p.join(appState.gitBaseDirectory, appState.remoteGitRepoPath));
|
p.join(appState.gitBaseDirectory, appState.remoteGitRepoPath));
|
||||||
var dotGitDir = new Directory(p.join(remoteGitDir.path, ".git"));
|
var dotGitDir = Directory(p.join(remoteGitDir.path, ".git"));
|
||||||
|
|
||||||
bool exists = await dotGitDir.exists();
|
bool exists = await dotGitDir.exists();
|
||||||
if (exists) {
|
if (exists) {
|
||||||
@ -194,7 +194,7 @@ class StateContainerState extends State<StateContainer> {
|
|||||||
gitBasePath: appState.gitBaseDirectory,
|
gitBasePath: appState.gitBaseDirectory,
|
||||||
);
|
);
|
||||||
|
|
||||||
noteRepo = new GitNoteRepository(
|
noteRepo = GitNoteRepository(
|
||||||
baseDirectory: appState.gitBaseDirectory,
|
baseDirectory: appState.gitBaseDirectory,
|
||||||
dirName: appState.remoteGitRepoPath,
|
dirName: appState.remoteGitRepoPath,
|
||||||
);
|
);
|
||||||
|
@ -26,9 +26,9 @@ class FileStorage implements NoteRepository {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<Note>> listNotes() async {
|
Future<List<Note>> listNotes() async {
|
||||||
final dir = new Directory(baseDirectory);
|
final dir = Directory(baseDirectory);
|
||||||
|
|
||||||
var notes = new List<Note>();
|
var notes = List<Note>();
|
||||||
var lister = dir.list(recursive: false);
|
var lister = dir.list(recursive: false);
|
||||||
await for (var fileEntity in lister) {
|
await for (var fileEntity in lister) {
|
||||||
Note note = await _loadNote(fileEntity);
|
Note note = await _loadNote(fileEntity);
|
||||||
@ -63,7 +63,7 @@ class FileStorage implements NoteRepository {
|
|||||||
var filePath = p.join(baseDirectory, note.fileName);
|
var filePath = p.join(baseDirectory, note.fileName);
|
||||||
print("FileStorage: Adding note in " + filePath);
|
print("FileStorage: Adding note in " + filePath);
|
||||||
|
|
||||||
var file = new File(filePath);
|
var file = File(filePath);
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
return NoteRepoResult(error: true);
|
return NoteRepoResult(error: true);
|
||||||
}
|
}
|
||||||
@ -77,7 +77,7 @@ class FileStorage implements NoteRepository {
|
|||||||
Future<NoteRepoResult> removeNote(Note note) async {
|
Future<NoteRepoResult> removeNote(Note note) async {
|
||||||
var filePath = p.join(baseDirectory, note.fileName);
|
var filePath = p.join(baseDirectory, note.fileName);
|
||||||
|
|
||||||
var file = new File(filePath);
|
var file = File(filePath);
|
||||||
await file.delete();
|
await file.delete();
|
||||||
|
|
||||||
return NoteRepoResult(noteFilePath: filePath, error: false);
|
return NoteRepoResult(noteFilePath: filePath, error: false);
|
||||||
@ -94,12 +94,12 @@ class FileStorage implements NoteRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<Directory> saveNotes(List<Note> notes) async {
|
Future<Directory> saveNotes(List<Note> notes) async {
|
||||||
final dir = new Directory(baseDirectory);
|
final dir = Directory(baseDirectory);
|
||||||
|
|
||||||
for (var note in notes) {
|
for (var note in notes) {
|
||||||
var filePath = p.join(dir.path, note.fileName);
|
var filePath = p.join(dir.path, note.fileName);
|
||||||
|
|
||||||
var file = new File(filePath);
|
var file = File(filePath);
|
||||||
var contents = noteSerializer.encode(note);
|
var contents = noteSerializer.encode(note);
|
||||||
await file.writeAsString(contents);
|
await file.writeAsString(contents);
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ class GitNoteRepository implements NoteRepository {
|
|||||||
@required this.dirName,
|
@required this.dirName,
|
||||||
@required String baseDirectory,
|
@required String baseDirectory,
|
||||||
}) : _fileStorage = FileStorage(
|
}) : _fileStorage = FileStorage(
|
||||||
noteSerializer: new MarkdownYAMLSerializer(),
|
noteSerializer: MarkdownYAMLSerializer(),
|
||||||
baseDirectory: p.join(baseDirectory, dirName),
|
baseDirectory: p.join(baseDirectory, dirName),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -92,8 +92,8 @@ class GitNoteRepository implements NoteRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!checkForCloned) {
|
if (!checkForCloned) {
|
||||||
var baseDir = new Directory(_fileStorage.baseDirectory);
|
var baseDir = Directory(_fileStorage.baseDirectory);
|
||||||
var dotGitDir = new Directory(p.join(baseDir.path, ".git"));
|
var dotGitDir = Directory(p.join(baseDir.path, ".git"));
|
||||||
cloned = await dotGitDir.exists();
|
cloned = await dotGitDir.exists();
|
||||||
checkForCloned = true;
|
checkForCloned = true;
|
||||||
}
|
}
|
||||||
@ -116,7 +116,7 @@ class GitNoteRepository implements NoteRepository {
|
|||||||
await gitPush(this.dirName);
|
await gitPush(this.dirName);
|
||||||
} on GitException catch (ex) {
|
} on GitException catch (ex) {
|
||||||
print(ex);
|
print(ex);
|
||||||
throw ex;
|
rethrow;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:yaml/yaml.dart';
|
|
||||||
import 'package:journal/note.dart';
|
import 'package:journal/note.dart';
|
||||||
|
import 'package:yaml/yaml.dart';
|
||||||
|
|
||||||
abstract class NoteSerializer {
|
abstract class NoteSerializer {
|
||||||
String encode(Note note);
|
String encode(Note note);
|
||||||
@ -12,7 +12,7 @@ class JsonNoteSerializer implements NoteSerializer {
|
|||||||
@override
|
@override
|
||||||
Note decode(String str) {
|
Note decode(String str) {
|
||||||
final json = JsonDecoder().convert(str);
|
final json = JsonDecoder().convert(str);
|
||||||
return new Note.fromJson(json);
|
return Note.fromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -28,18 +28,18 @@ class MarkdownYAMLSerializer implements NoteSerializer {
|
|||||||
var parts = str.split("---\n");
|
var parts = str.split("---\n");
|
||||||
|
|
||||||
var yamlMap = loadYaml(parts[1]);
|
var yamlMap = loadYaml(parts[1]);
|
||||||
var map = new Map<String, dynamic>();
|
var map = Map<String, dynamic>();
|
||||||
yamlMap.forEach((key, value) {
|
yamlMap.forEach((key, value) {
|
||||||
map[key] = value;
|
map[key] = value;
|
||||||
});
|
});
|
||||||
map['body'] = parts[2].trimLeft();
|
map['body'] = parts[2].trimLeft();
|
||||||
|
|
||||||
return new Note.fromJson(map);
|
return Note.fromJson(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
var map = new Map<String, dynamic>();
|
var map = Map<String, dynamic>();
|
||||||
map['body'] = str;
|
map['body'] = str;
|
||||||
return new Note.fromJson(map);
|
return Note.fromJson(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -4,13 +4,13 @@ import 'package:journal/state_container.dart';
|
|||||||
class AppDrawer extends StatelessWidget {
|
class AppDrawer extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Widget setupGitButton = new Container();
|
Widget setupGitButton = Container();
|
||||||
var appState = StateContainer.of(context).appState;
|
var appState = StateContainer.of(context).appState;
|
||||||
|
|
||||||
if (!appState.remoteGitRepoConfigured) {
|
if (!appState.remoteGitRepoConfigured) {
|
||||||
setupGitButton = ListTile(
|
setupGitButton = ListTile(
|
||||||
title: new Text('Setup Git Host'),
|
title: Text('Setup Git Host'),
|
||||||
trailing: new Icon(
|
trailing: Icon(
|
||||||
Icons.priority_high,
|
Icons.priority_high,
|
||||||
color: Colors.red,
|
color: Colors.red,
|
||||||
),
|
),
|
||||||
@ -21,13 +21,13 @@ class AppDrawer extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Drawer(
|
return Drawer(
|
||||||
child: new ListView(
|
child: ListView(
|
||||||
// Important: Remove any padding from the ListView.
|
// Important: Remove any padding from the ListView.
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
new DrawerHeader(
|
DrawerHeader(
|
||||||
decoration: new BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).buttonColor,
|
color: Theme.of(context).buttonColor,
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@ -42,16 +42,16 @@ class AppDrawer extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
setupGitButton,
|
setupGitButton,
|
||||||
new ListTile(
|
ListTile(
|
||||||
title: new Text('Share App'),
|
title: Text('Share App'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
// Update the state of the app
|
// Update the state of the app
|
||||||
// ...
|
// ...
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
new ListTile(
|
ListTile(
|
||||||
title: new Text('Settings'),
|
title: Text('Settings'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
Navigator.pushNamed(context, "/settings");
|
Navigator.pushNamed(context, "/settings");
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
import 'package:journal/note.dart';
|
import 'package:journal/note.dart';
|
||||||
import 'package:journal/state_container.dart';
|
import 'package:journal/state_container.dart';
|
||||||
|
|
||||||
@ -20,24 +19,24 @@ class JournalList extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return new ListView.builder(
|
return ListView.builder(
|
||||||
itemBuilder: (context, i) {
|
itemBuilder: (context, i) {
|
||||||
if (i >= notes.length) {
|
if (i >= notes.length) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var note = notes[i];
|
var note = notes[i];
|
||||||
return new Dismissible(
|
return Dismissible(
|
||||||
key: new Key(note.fileName),
|
key: Key(note.fileName),
|
||||||
child: _buildRow(context, note, i),
|
child: _buildRow(context, note, i),
|
||||||
background: new Container(color: Theme.of(context).accentColor),
|
background: Container(color: Theme.of(context).accentColor),
|
||||||
onDismissed: (direction) {
|
onDismissed: (direction) {
|
||||||
final stateContainer = StateContainer.of(context);
|
final stateContainer = StateContainer.of(context);
|
||||||
stateContainer.removeNote(note);
|
stateContainer.removeNote(note);
|
||||||
|
|
||||||
Scaffold.of(context).showSnackBar(new SnackBar(
|
Scaffold.of(context).showSnackBar(SnackBar(
|
||||||
content: new Text("Note deleted"),
|
content: Text("Note deleted"),
|
||||||
action: new SnackBarAction(
|
action: SnackBarAction(
|
||||||
label: 'Undo',
|
label: 'Undo',
|
||||||
onPressed: () => stateContainer.insertNote(i, note),
|
onPressed: () => stateContainer.insertNote(i, note),
|
||||||
),
|
),
|
||||||
@ -49,22 +48,22 @@ class JournalList extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildRow(BuildContext context, Note journal, int noteIndex) {
|
Widget _buildRow(BuildContext context, Note journal, int noteIndex) {
|
||||||
var formatter = new DateFormat('dd MMM, yyyy');
|
var formatter = DateFormat('dd MMM, yyyy');
|
||||||
var title = formatter.format(journal.created);
|
var title = formatter.format(journal.created);
|
||||||
|
|
||||||
var timeFormatter = new DateFormat('Hm');
|
var timeFormatter = DateFormat('Hm');
|
||||||
var time = timeFormatter.format(journal.created);
|
var time = timeFormatter.format(journal.created);
|
||||||
|
|
||||||
var body = journal.body;
|
var body = journal.body;
|
||||||
body = body.replaceAll("\n", " ");
|
body = body.replaceAll("\n", " ");
|
||||||
|
|
||||||
return new ListTile(
|
return ListTile(
|
||||||
isThreeLine: true,
|
isThreeLine: true,
|
||||||
title: new Text(
|
title: Text(
|
||||||
title,
|
title,
|
||||||
style: _biggerFont,
|
style: _biggerFont,
|
||||||
),
|
),
|
||||||
subtitle: new Text(
|
subtitle: Text(
|
||||||
time + "\n" + body,
|
time + "\n" + body,
|
||||||
maxLines: 3,
|
maxLines: 3,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
import 'package:journal/note.dart';
|
import 'package:journal/note.dart';
|
||||||
|
|
||||||
class NoteHeader extends StatelessWidget {
|
class NoteHeader extends StatelessWidget {
|
||||||
@ -13,22 +12,22 @@ class NoteHeader extends StatelessWidget {
|
|||||||
var dateStr = DateFormat('MMMM, yyyy').format(note.created);
|
var dateStr = DateFormat('MMMM, yyyy').format(note.created);
|
||||||
var timeStr = DateFormat('EEEE HH:mm').format(note.created);
|
var timeStr = DateFormat('EEEE HH:mm').format(note.created);
|
||||||
|
|
||||||
var bigNum = new Text(
|
var bigNum = Text(
|
||||||
note.created.day.toString(),
|
note.created.day.toString(),
|
||||||
style: TextStyle(fontSize: 40.0),
|
style: TextStyle(fontSize: 40.0),
|
||||||
);
|
);
|
||||||
|
|
||||||
var dateText = new Text(
|
var dateText = Text(
|
||||||
dateStr,
|
dateStr,
|
||||||
style: TextStyle(fontSize: 18.0),
|
style: TextStyle(fontSize: 18.0),
|
||||||
);
|
);
|
||||||
|
|
||||||
var timeText = new Text(
|
var timeText = Text(
|
||||||
timeStr,
|
timeStr,
|
||||||
style: TextStyle(fontSize: 18.0),
|
style: TextStyle(fontSize: 18.0),
|
||||||
);
|
);
|
||||||
|
|
||||||
var w = new Row(
|
var w = Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
bigNum,
|
bigNum,
|
||||||
SizedBox(width: 8.0),
|
SizedBox(width: 8.0),
|
||||||
@ -43,8 +42,8 @@ class NoteHeader extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
);
|
);
|
||||||
|
|
||||||
return new Padding(
|
return Padding(
|
||||||
padding: new EdgeInsets.only(top: 8.0, bottom: 18.0),
|
padding: EdgeInsets.only(top: 8.0, bottom: 18.0),
|
||||||
child: w,
|
child: w,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'package:test/test.dart';
|
|
||||||
import 'package:journal/datetime_utils.dart';
|
import 'package:journal/datetime_utils.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
main() {
|
void main() {
|
||||||
group('DateTime Utils', () {
|
group('DateTime Utils', () {
|
||||||
test('Test random date', () {
|
test('Test random date', () {
|
||||||
var dateTime = DateTime.utc(2011, 12, 23, 10, 15, 30);
|
var dateTime = DateTime.utc(2011, 12, 23, 10, 15, 30);
|
||||||
|
@ -1,18 +1,17 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:test/test.dart';
|
|
||||||
import 'package:path/path.dart' as p;
|
|
||||||
|
|
||||||
import 'package:journal/note.dart';
|
import 'package:journal/note.dart';
|
||||||
import 'package:journal/storage/file_storage.dart';
|
import 'package:journal/storage/file_storage.dart';
|
||||||
import 'package:journal/storage/serializers.dart';
|
import 'package:journal/storage/serializers.dart';
|
||||||
|
import 'package:path/path.dart' as p;
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
DateTime nowWithoutMicro() {
|
DateTime nowWithoutMicro() {
|
||||||
var dt = DateTime.now();
|
var dt = DateTime.now();
|
||||||
return DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second);
|
return DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
main() {
|
void main() {
|
||||||
group('FileStorage', () {
|
group('FileStorage', () {
|
||||||
var notes = [
|
var notes = [
|
||||||
Note(fileName: "1.md", body: "test", created: nowWithoutMicro()),
|
Note(fileName: "1.md", body: "test", created: nowWithoutMicro()),
|
||||||
@ -26,7 +25,7 @@ main() {
|
|||||||
tempDir = await Directory.systemTemp.createTemp('__storage_test__');
|
tempDir = await Directory.systemTemp.createTemp('__storage_test__');
|
||||||
storage = FileStorage(
|
storage = FileStorage(
|
||||||
baseDirectory: tempDir.path,
|
baseDirectory: tempDir.path,
|
||||||
noteSerializer: new JsonNoteSerializer(),
|
noteSerializer: JsonNoteSerializer(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import 'package:journal/note.dart';
|
import 'package:journal/note.dart';
|
||||||
import 'package:journal/storage/serializers.dart';
|
import 'package:journal/storage/serializers.dart';
|
||||||
|
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
DateTime nowWithoutMicro() {
|
DateTime nowWithoutMicro() {
|
||||||
@ -8,13 +7,13 @@ DateTime nowWithoutMicro() {
|
|||||||
return DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second);
|
return DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
main() {
|
void main() {
|
||||||
group('Serializers', () {
|
group('Serializers', () {
|
||||||
var note = Note(
|
var note = Note(
|
||||||
fileName: "2", body: "This is the body", created: nowWithoutMicro());
|
fileName: "2", body: "This is the body", created: nowWithoutMicro());
|
||||||
|
|
||||||
test('JSON Serializer', () {
|
test('JSON Serializer', () {
|
||||||
var serializer = new JsonNoteSerializer();
|
var serializer = JsonNoteSerializer();
|
||||||
var str = serializer.encode(note);
|
var str = serializer.encode(note);
|
||||||
var note2 = serializer.decode(str);
|
var note2 = serializer.decode(str);
|
||||||
|
|
||||||
@ -22,7 +21,7 @@ main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Markdown Serializer', () {
|
test('Markdown Serializer', () {
|
||||||
var serializer = new MarkdownYAMLSerializer();
|
var serializer = MarkdownYAMLSerializer();
|
||||||
var str = serializer.encode(note);
|
var str = serializer.encode(note);
|
||||||
var note2 = serializer.decode(str);
|
var note2 = serializer.decode(str);
|
||||||
|
|
||||||
@ -42,7 +41,7 @@ foo: bar
|
|||||||
|
|
||||||
Alright.""";
|
Alright.""";
|
||||||
|
|
||||||
var serializer = new MarkdownYAMLSerializer();
|
var serializer = MarkdownYAMLSerializer();
|
||||||
var note = serializer.decode(str);
|
var note = serializer.decode(str);
|
||||||
var actualStr = serializer.encode(note);
|
var actualStr = serializer.encode(note);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user