Files
GitJournal/lib/settings.dart
Vishesh Handa f30c52f595 Move all persistent state from AppState to Settings
This way all the persistant state of the app is managed from the same
place. It makes everything much easier. Also, it's required for when
GitJournal supports multiple repositories.
2020-09-24 23:45:37 +02:00

756 lines
23 KiB
Dart

import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:uuid/uuid.dart';
import 'package:gitjournal/core/sorting_mode.dart';
import 'package:gitjournal/features.dart';
import 'package:gitjournal/folder_views/common.dart';
import 'package:gitjournal/screens/note_editor.dart';
class Settings extends ChangeNotifier {
// singleton
static final Settings _singleton = Settings._internal();
factory Settings() => _singleton;
Settings._internal();
static Settings get instance => _singleton;
// Properties
bool onBoardingCompleted = false;
String gitAuthor = "GitJournal";
String gitAuthorEmail = "app@gitjournal.io";
NoteFileNameFormat noteFileNameFormat = NoteFileNameFormat.Default;
bool collectUsageStatistics = true;
bool collectCrashReports = true;
String yamlModifiedKey = "modified";
String yamlCreatedKey = "created";
String yamlTagsKey = "tags";
String customMetaData = "";
bool yamlHeaderEnabled = true;
String defaultNewNoteFolderSpec = "";
String journalEditordefaultNewNoteFolderSpec = "";
bool journalEditorSingleNote = false;
RemoteSyncFrequency remoteSyncFrequency = RemoteSyncFrequency.Default;
SortingField sortingField = SortingField.Default;
SortingOrder sortingOrder = SortingOrder.Default;
SettingsEditorType defaultEditor = SettingsEditorType.Default;
SettingsFolderViewType defaultView = SettingsFolderViewType.Default;
bool showNoteSummary = true;
String folderViewHeaderType = "TitleGenerated";
int version = 0;
bool proMode = Features.alwaysPro;
String proExpirationDate = "";
String _pseudoId;
String get pseudoId => _pseudoId;
SettingsHomeScreen homeScreen = SettingsHomeScreen.Default;
SettingsMarkdownDefaultView markdownDefaultView =
SettingsMarkdownDefaultView.Default;
SettingsMarkdownDefaultView markdownLastUsedView =
SettingsMarkdownDefaultView.Edit;
String imageLocationSpec = "."; // . means the same folder
String debugLogLevel = 'v';
bool experimentalBacklinks = true;
bool experimentalFs = false;
bool experimentalMarkdownToolbar = false;
bool experimentalGraphView = false;
bool zenMode = false;
bool saveTitleInH1 = true;
bool swipeToDelete = true;
bool emojiParser = true;
Set<String> inlineTagPrefixes = {'#'};
// From AppState
String localGitRepoFolderName = "";
bool localGitRepoConfigured = false;
String remoteGitRepoFolderName = "";
bool remoteGitRepoConfigured = false;
String gitBaseDirectory = "";
void load(SharedPreferences pref) {
onBoardingCompleted = pref.getBool("onBoardingCompleted") ?? false;
gitAuthor = pref.getString("gitAuthor") ?? gitAuthor;
gitAuthorEmail = pref.getString("gitAuthorEmail") ?? gitAuthorEmail;
noteFileNameFormat = NoteFileNameFormat.fromInternalString(
pref.getString("noteFileNameFormat"));
collectUsageStatistics =
pref.getBool("collectUsageStatistics") ?? collectUsageStatistics;
collectCrashReports =
pref.getBool("collectCrashReports") ?? collectCrashReports;
yamlModifiedKey = pref.getString("yamlModifiedKey") ?? yamlModifiedKey;
yamlCreatedKey = pref.getString("yamlCreatedKey") ?? yamlCreatedKey;
yamlTagsKey = pref.getString("yamlTagsKey") ?? yamlTagsKey;
customMetaData = pref.getString("customMetaData") ?? customMetaData;
yamlHeaderEnabled = pref.getBool("yamlHeaderEnabled") ?? yamlHeaderEnabled;
defaultNewNoteFolderSpec =
pref.getString("defaultNewNoteFolderSpec") ?? defaultNewNoteFolderSpec;
journalEditordefaultNewNoteFolderSpec =
pref.getString("journalEditordefaultNewNoteFolderSpec") ??
journalEditordefaultNewNoteFolderSpec;
journalEditorSingleNote =
pref.getBool("journalEditorSingleNote") ?? journalEditorSingleNote;
remoteSyncFrequency = RemoteSyncFrequency.fromInternalString(
pref.getString("remoteSyncFrequency"));
sortingField =
SortingField.fromInternalString(pref.getString("sortingField"));
sortingOrder =
SortingOrder.fromInternalString(pref.getString("sortingOrder"));
defaultEditor =
SettingsEditorType.fromInternalString(pref.getString("defaultEditor"));
defaultView = SettingsFolderViewType.fromInternalString(
pref.getString("defaultView"));
markdownDefaultView = SettingsMarkdownDefaultView.fromInternalString(
pref.getString("markdownDefaultView"));
markdownLastUsedView = SettingsMarkdownDefaultView.fromInternalString(
pref.getString("markdownLastUsedView"));
if (markdownLastUsedView == SettingsMarkdownDefaultView.LastUsed) {
markdownLastUsedView = SettingsMarkdownDefaultView.Edit;
}
showNoteSummary = pref.getBool("showNoteSummary") ?? showNoteSummary;
folderViewHeaderType =
pref.getString("folderViewHeaderType") ?? folderViewHeaderType;
version = pref.getInt("settingsVersion") ?? version;
proMode = pref.getBool("proMode") ?? proMode;
emojiParser = pref.getBool("emojiParser") ?? emojiParser;
proExpirationDate =
pref.getString("proExpirationDate") ?? proExpirationDate;
_pseudoId = pref.getString("pseudoId");
if (_pseudoId == null) {
_pseudoId = Uuid().v4();
pref.setString("pseudoId", _pseudoId);
}
homeScreen =
SettingsHomeScreen.fromInternalString(pref.getString("homeScreen"));
imageLocationSpec =
pref.getString("imageLocationSpec") ?? imageLocationSpec;
debugLogLevel = pref.getString("debugLogLevel") ?? debugLogLevel;
experimentalBacklinks =
pref.getBool("experimentalBacklinks") ?? experimentalBacklinks;
experimentalFs = pref.getBool("experimentalFs") ?? experimentalFs;
experimentalMarkdownToolbar = pref.getBool("experimentalMarkdownToolbar") ??
experimentalMarkdownToolbar;
zenMode = pref.getBool("zenMode") ?? zenMode;
saveTitleInH1 = pref.getBool("saveTitleInH1") ?? saveTitleInH1;
swipeToDelete = pref.getBool("swipeToDelete") ?? swipeToDelete;
inlineTagPrefixes =
pref.getStringList("inlineTagPrefixes")?.toSet() ?? inlineTagPrefixes;
// From AppState
localGitRepoConfigured = pref.getBool("localGitRepoConfigured") ?? false;
remoteGitRepoConfigured = pref.getBool("remoteGitRepoConfigured") ?? false;
localGitRepoFolderName = pref.getString("localGitRepoPath") ?? "";
remoteGitRepoFolderName = pref.getString("remoteGitRepoPath") ?? "";
gitBaseDirectory = pref.getString("gitBaseDirectory") ?? "";
}
Future save() async {
var pref = await SharedPreferences.getInstance();
var defaultSet = Settings._internal();
await pref.setBool("onBoardingCompleted", onBoardingCompleted);
_setString(pref, "gitAuthor", gitAuthor, defaultSet.gitAuthor);
_setString(
pref, "gitAuthorEmail", gitAuthorEmail, defaultSet.gitAuthorEmail);
_setString(
pref,
"noteFileNameFormat",
noteFileNameFormat.toInternalString(),
defaultSet.noteFileNameFormat.toInternalString());
_setBool(pref, "collectUsageStatistics", collectUsageStatistics,
defaultSet.collectUsageStatistics);
_setBool(pref, "collectCrashReports", collectCrashReports,
defaultSet.collectCrashReports);
_setString(
pref, "yamlModifiedKey", yamlModifiedKey, defaultSet.yamlModifiedKey);
_setString(
pref, "yamlCreatedKey", yamlCreatedKey, defaultSet.yamlCreatedKey);
_setString(pref, "yamlTagsKey", yamlTagsKey, defaultSet.yamlTagsKey);
_setString(
pref, "customMetaData", customMetaData, defaultSet.customMetaData);
_setBool(pref, "yamlHeaderEnabled", yamlHeaderEnabled,
defaultSet.yamlHeaderEnabled);
_setString(pref, "defaultNewNoteFolderSpec", defaultNewNoteFolderSpec,
defaultSet.defaultNewNoteFolderSpec);
_setString(
pref,
"journalEditordefaultNewNoteFolderSpec",
journalEditordefaultNewNoteFolderSpec,
defaultSet.journalEditordefaultNewNoteFolderSpec);
_setBool(pref, "journalEditorSingleNote", journalEditorSingleNote,
defaultSet.journalEditorSingleNote);
_setString(
pref,
"remoteSyncFrequency",
remoteSyncFrequency.toInternalString(),
defaultSet.remoteSyncFrequency.toInternalString());
_setString(pref, "sortingField", sortingField.toInternalString(),
defaultSet.sortingField.toInternalString());
_setString(pref, "sortingOrder", sortingOrder.toInternalString(),
defaultSet.sortingOrder.toInternalString());
_setString(pref, "defaultEditor", defaultEditor.toInternalString(),
defaultSet.defaultEditor.toInternalString());
_setString(pref, "defaultView", defaultView.toInternalString(),
defaultSet.defaultView.toInternalString());
_setString(
pref,
"markdownDefaultView",
markdownDefaultView.toInternalString(),
defaultSet.markdownDefaultView.toInternalString());
_setString(
pref,
"markdownLastUsedView",
markdownLastUsedView.toInternalString(),
defaultSet.markdownLastUsedView.toInternalString());
_setBool(
pref, "showNoteSummary", showNoteSummary, defaultSet.showNoteSummary);
_setString(pref, "folderViewHeaderType", folderViewHeaderType,
defaultSet.folderViewHeaderType);
_setString(pref, "proExpirationDate", proExpirationDate,
defaultSet.proExpirationDate);
_setBool(pref, "proMode", proMode, defaultSet.proMode);
_setBool(pref, "emojiParser", emojiParser, defaultSet.emojiParser);
_setString(pref, "homeScreen", homeScreen.toInternalString(),
defaultSet.homeScreen.toInternalString());
_setString(pref, "imageLocationSpec", imageLocationSpec,
defaultSet.imageLocationSpec);
_setString(pref, "debugLogLevel", debugLogLevel, defaultSet.debugLogLevel);
_setBool(pref, "experimentalBacklinks", experimentalBacklinks,
defaultSet.experimentalBacklinks);
_setBool(pref, "experimentalFs", experimentalFs, defaultSet.experimentalFs);
_setBool(pref, "experimentalMarkdownToolbar", experimentalMarkdownToolbar,
defaultSet.experimentalMarkdownToolbar);
_setBool(pref, "zenMode", zenMode, defaultSet.zenMode);
_setBool(pref, "saveTitleInH1", saveTitleInH1, defaultSet.saveTitleInH1);
_setBool(pref, "swipeToDelete", swipeToDelete, defaultSet.swipeToDelete);
_setStringSet(pref, "inlineTagPrefixes", inlineTagPrefixes,
defaultSet.inlineTagPrefixes);
pref.setInt("settingsVersion", version);
pref.setBool("localGitRepoConfigured", localGitRepoConfigured);
pref.setBool("remoteGitRepoConfigured", remoteGitRepoConfigured);
pref.setString("localGitRepoPath", localGitRepoFolderName);
pref.setString("remoteGitRepoPath", remoteGitRepoFolderName);
pref.setString("gitBaseDirectory", gitBaseDirectory);
notifyListeners();
}
Future<void> _setString(
SharedPreferences pref,
String key,
String value,
String defaultValue,
) async {
if (value == defaultValue) {
await pref.remove(key);
} else {
await pref.setString(key, value);
}
}
Future<void> _setBool(
SharedPreferences pref,
String key,
bool value,
bool defaultValue,
) async {
if (value == defaultValue) {
await pref.remove(key);
} else {
await pref.setBool(key, value);
}
}
Future<void> _setStringSet(
SharedPreferences pref,
String key,
Set<String> value,
Set<String> defaultValue,
) async {
final eq = const SetEquality().equals;
if (eq(value, defaultValue)) {
await pref.remove(key);
} else {
await pref.setStringList(key, value.toList());
}
}
Map<String, String> toMap() {
return <String, String>{
"onBoardingCompleted": onBoardingCompleted.toString(),
"gitAuthor": gitAuthor,
"gitAuthorEmail": gitAuthorEmail,
"noteFileNameFormat": noteFileNameFormat.toInternalString(),
"collectUsageStatistics": collectUsageStatistics.toString(),
"collectCrashReports": collectCrashReports.toString(),
"yamlModifiedKey": yamlModifiedKey,
"yamlCreatedKey": yamlCreatedKey,
"yamlTagsKey": yamlTagsKey,
"customMetaData": customMetaData,
"yamlHeaderEnabled": yamlHeaderEnabled.toString(),
"defaultNewNoteFolderSpec": defaultNewNoteFolderSpec,
"journalEditordefaultNewNoteFolderSpec":
journalEditordefaultNewNoteFolderSpec,
'journalEditorSingleNote': journalEditorSingleNote.toString(),
"defaultEditor": defaultEditor.toInternalString(),
"defaultView": defaultView.toInternalString(),
"sortingField": sortingField.toInternalString(),
"sortingOrder": sortingOrder.toInternalString(),
"remoteSyncFrequency": remoteSyncFrequency.toInternalString(),
"showNoteSummary": showNoteSummary.toString(),
"folderViewHeaderType": folderViewHeaderType,
"version": version.toString(),
"proMode": proMode.toString(),
'proExpirationDate': proExpirationDate,
'pseudoId': pseudoId,
'markdownDefaultView': markdownDefaultView.toInternalString(),
'markdownLastUsedView': markdownLastUsedView.toInternalString(),
'homeScreen': homeScreen.toInternalString(),
'imageLocationSpec': imageLocationSpec,
'debugLogLevel': debugLogLevel,
'experimentalBacklinks': experimentalBacklinks.toString(),
'experimentalFs': experimentalFs.toString(),
'experimentalMarkdownToolbar': experimentalMarkdownToolbar.toString(),
'zenMode': zenMode.toString(),
'saveTitleInH1': saveTitleInH1.toString(),
'swipeToDelete': swipeToDelete.toString(),
'inlineTagPrefixes': inlineTagPrefixes.join(' '),
'emojiParser': emojiParser.toString(),
'localGitRepoConfigured': localGitRepoConfigured.toString(),
'remoteGitRepoConfigured': remoteGitRepoConfigured.toString(),
'localGitRepoFolderName': localGitRepoFolderName.toString(),
'remoteGitRepoFolderName': remoteGitRepoFolderName.toString(),
'gitBaseDirectory': gitBaseDirectory.toString(),
};
}
Map<String, String> toLoggableMap() {
var m = toMap();
m.remove("gitAuthor");
m.remove("gitAuthorEmail");
m.remove("defaultNewNoteFolderSpec");
return m;
}
}
class NoteFileNameFormat {
static const Iso8601WithTimeZone = NoteFileNameFormat(
"Iso8601WithTimeZone", 'settings.NoteFileNameFormat.iso8601WithTimeZone');
static const Iso8601 =
NoteFileNameFormat("Iso8601", 'settings.NoteFileNameFormat.iso8601');
static const Iso8601WithTimeZoneWithoutColon = NoteFileNameFormat(
"Iso8601WithTimeZoneWithoutColon",
'settings.NoteFileNameFormat.iso8601WithoutColon');
static const FromTitle =
NoteFileNameFormat("FromTitle", 'settings.NoteFileNameFormat.title');
static const SimpleDate =
NoteFileNameFormat("SimpleDate", 'settings.NoteFileNameFormat.simmple');
static const UuidV4 =
NoteFileNameFormat("uuidv4", 'settings.NoteFileNameFormat.uuid');
static const Zettelkasten = NoteFileNameFormat(
"Zettelkasten", 'settings.NoteFileNameFormat.zettelkasten');
static const Default = FromTitle;
static const options = <NoteFileNameFormat>[
SimpleDate,
FromTitle,
Iso8601,
Iso8601WithTimeZone,
Iso8601WithTimeZoneWithoutColon,
UuidV4,
Zettelkasten,
];
static NoteFileNameFormat fromInternalString(String str) {
for (var opt in options) {
if (opt.toInternalString() == str) {
return opt;
}
}
return Default;
}
static NoteFileNameFormat fromPublicString(String str) {
for (var opt in options) {
if (opt.toPublicString() == str) {
return opt;
}
}
return Default;
}
final String _str;
final String _publicStr;
const NoteFileNameFormat(this._str, this._publicStr);
String toInternalString() {
return _str;
}
String toPublicString() {
return tr(_publicStr);
}
@override
String toString() {
assert(false, "NoteFileNameFormat toString should never be called");
return "";
}
}
class RemoteSyncFrequency {
static const Automatic =
RemoteSyncFrequency("settings.remoteSync.auto", "automatic");
static const Manual =
RemoteSyncFrequency("settings.remoteSync.manual", "manual");
static const Default = Automatic;
final String _str;
final String _publicString;
const RemoteSyncFrequency(this._publicString, this._str);
String toInternalString() {
return _str;
}
String toPublicString() {
return tr(_publicString);
}
static const options = <RemoteSyncFrequency>[
Automatic,
Manual,
];
static RemoteSyncFrequency fromInternalString(String str) {
for (var opt in options) {
if (opt.toInternalString() == str) {
return opt;
}
}
return Default;
}
static RemoteSyncFrequency fromPublicString(String str) {
for (var opt in options) {
if (opt.toPublicString() == str) {
return opt;
}
}
return Default;
}
@override
String toString() {
assert(false, "RemoteSyncFrequency toString should never be called");
return "";
}
}
class SettingsEditorType {
static const Markdown =
SettingsEditorType('settings.editors.markdownEditor', "Markdown");
static const Raw = SettingsEditorType('settings.editors.rawEditor', "Raw");
static const Journal =
SettingsEditorType('settings.editors.journalEditor', "Journal");
static const Checklist =
SettingsEditorType('settings.editors.checklistEditor', "Checklist");
static const Default = Markdown;
final String _str;
final String _publicString;
const SettingsEditorType(this._publicString, this._str);
String toInternalString() {
return _str;
}
String toPublicString() {
return tr(_publicString);
}
EditorType toEditorType() {
switch (this) {
case Markdown:
return EditorType.Markdown;
case Raw:
return EditorType.Raw;
case Journal:
return EditorType.Journal;
case Checklist:
return EditorType.Checklist;
default:
return EditorType.Markdown;
}
}
static SettingsEditorType fromEditorType(EditorType editorType) {
switch (editorType) {
case EditorType.Checklist:
return SettingsEditorType.Checklist;
case EditorType.Raw:
return SettingsEditorType.Raw;
case EditorType.Markdown:
return SettingsEditorType.Markdown;
case EditorType.Journal:
return SettingsEditorType.Journal;
}
return SettingsEditorType.Default;
}
static const options = <SettingsEditorType>[
Markdown,
Raw,
Journal,
Checklist,
];
static SettingsEditorType fromInternalString(String str) {
for (var opt in options) {
if (opt.toInternalString() == str) {
return opt;
}
}
return Default;
}
static SettingsEditorType fromPublicString(String str) {
for (var opt in options) {
if (opt.toPublicString() == str) {
return opt;
}
}
return Default;
}
@override
String toString() {
assert(false, "EditorType toString should never be called");
return "";
}
}
class SettingsFolderViewType {
static const Standard =
SettingsFolderViewType('widgets.FolderView.views.standard', "Standard");
static const Journal =
SettingsFolderViewType('widgets.FolderView.views.journal', "Journal");
static const Card =
SettingsFolderViewType('widgets.FolderView.views.card', "Card");
static const Grid =
SettingsFolderViewType('widgets.FolderView.views.grid', "Grid");
static const Default = Standard;
final String _str;
final String _publicString;
const SettingsFolderViewType(this._publicString, this._str);
String toInternalString() {
return _str;
}
String toPublicString() {
return tr(_publicString);
}
static const options = <SettingsFolderViewType>[
Standard,
Journal,
Card,
Grid,
];
static SettingsFolderViewType fromInternalString(String str) {
for (var opt in options) {
if (opt.toInternalString() == str) {
return opt;
}
}
return Default;
}
static SettingsFolderViewType fromPublicString(String str) {
for (var opt in options) {
if (opt.toPublicString() == str) {
return opt;
}
}
return Default;
}
@override
String toString() {
assert(false, "FolderViewType toString should never be called");
return "";
}
FolderViewType toFolderViewType() {
switch (this) {
case Standard:
return FolderViewType.Standard;
case Journal:
return FolderViewType.Journal;
case Card:
return FolderViewType.Card;
case Grid:
return FolderViewType.Grid;
}
return FolderViewType.Standard;
}
static SettingsFolderViewType fromFolderViewType(FolderViewType viewType) {
switch (viewType) {
case FolderViewType.Standard:
return SettingsFolderViewType.Standard;
case FolderViewType.Journal:
return SettingsFolderViewType.Journal;
case FolderViewType.Card:
return SettingsFolderViewType.Card;
case FolderViewType.Grid:
return SettingsFolderViewType.Grid;
}
return SettingsFolderViewType.Default;
}
}
class SettingsMarkdownDefaultView {
static const Edit =
SettingsMarkdownDefaultView('settings.EditorDefaultView.edit', "Edit");
static const View =
SettingsMarkdownDefaultView('settings.EditorDefaultView.view', "View");
static const LastUsed = SettingsMarkdownDefaultView(
'settings.EditorDefaultView.lastUsed', "Last Used");
static const Default = LastUsed;
final String _str;
final String _publicStr;
const SettingsMarkdownDefaultView(this._publicStr, this._str);
String toInternalString() {
return _str;
}
String toPublicString() {
return tr(_publicStr);
}
static const options = <SettingsMarkdownDefaultView>[
Edit,
View,
LastUsed,
];
static SettingsMarkdownDefaultView fromInternalString(String str) {
for (var opt in options) {
if (opt.toInternalString() == str) {
return opt;
}
}
return Default;
}
static SettingsMarkdownDefaultView fromPublicString(String str) {
for (var opt in options) {
if (opt.toPublicString() == str) {
return opt;
}
}
return Default;
}
@override
String toString() {
assert(
false, "SettingsMarkdownDefaultView toString should never be called");
return "";
}
}
class SettingsHomeScreen {
static const AllNotes =
SettingsHomeScreen("settings.HomeScreen.allNotes", "all_notes");
static const AllFolders =
SettingsHomeScreen("settings.HomeScreen.allFolders", "all_folders");
static const Default = AllNotes;
final String _str;
final String _publicString;
const SettingsHomeScreen(this._publicString, this._str);
String toInternalString() {
return _str;
}
String toPublicString() {
return tr(_publicString);
}
static const options = <SettingsHomeScreen>[
AllNotes,
AllFolders,
];
static SettingsHomeScreen fromInternalString(String str) {
for (var opt in options) {
if (opt.toInternalString() == str) {
return opt;
}
}
return Default;
}
static SettingsHomeScreen fromPublicString(String str) {
for (var opt in options) {
if (opt.toPublicString() == str) {
return opt;
}
}
return Default;
}
@override
String toString() {
assert(false, "SettingsHomeScreen toString should never be called");
return "";
}
}