This commit is contained in:
Mo Bitar
2018-07-10 09:04:57 -05:00
parent 134e295475
commit 40108faa28
20 changed files with 170 additions and 410 deletions

View File

@@ -4,8 +4,7 @@ import ModelManager from "./lib/sfjs/modelManager"
import Server from "./lib/sfjs/httpManager"
import Sync from './lib/sfjs/syncManager'
import Storage from "./lib/sfjs/storageManager"
import Auth from "./lib/authManager"
import Theme from "./models/subclass/theme"
import Auth from "./lib/sfjs/authManager"
import KeysManager from './lib/keysManager'
export default class GlobalStyles {
@@ -54,7 +53,7 @@ export default class GlobalStyles {
let content = Object.assign({}, parsedTheme);
parsedTheme.content = content;
var theme = new Theme(parsedTheme);
var theme = new SNTheme(parsedTheme);
if(needsMigration) {
theme.setMobileRules(parsedTheme.mobileRules);
theme.mobileRules = null;
@@ -78,7 +77,7 @@ export default class GlobalStyles {
constructor() {
KeysManager.get().registerAccountRelatedStorageKeys(["activeTheme"]);
ModelManager.get().addItemSyncObserver("themes", "SN|Theme", function(items){
ModelManager.get().addItemSyncObserver("themes", "SN|Theme", function(allItems, validItems, deletedItems, source){
if(this.activeTheme && this.activeTheme.isSwapIn) {
var matchingTheme = _.find(this.themes(), {uuid: this.activeTheme.uuid});
if(matchingTheme) {
@@ -136,7 +135,7 @@ export default class GlobalStyles {
var constants = this.defaultConstants();
this._systemTheme = new Theme({
this._systemTheme = new SNTheme({
uuid: 0,
content: {
isDefault: true,

View File

@@ -9,7 +9,9 @@ import {Navigation, ScreenVisibilityListener} from 'react-native-navigation';
import {registerScreens} from './screens';
import KeysManager from './lib/keysManager'
import Auth from './lib/authManager'
import Auth from './lib/sfjs/authManager'
import ModelManager from './lib/sfjs/modelManager'
import Sync from './lib/sfjs/syncManager'
import ReviewManager from './lib/reviewManager';
import GlobalStyles from "./Styles"
import Icons from "./Icons"
@@ -73,11 +75,14 @@ export default class App {
this.listener.register();
// Listen to sign out event
this.signoutObserver = Auth.getInstance().addEventObserver([Auth.DidSignOutEvent, Auth.WillSignInEvent], function(event){
if(event == Auth.DidSignOutEvent) {
this.signoutObserver = Auth.get().addEventHandler((event) => {
if(event == SFAuthManager.DidSignOutEvent) {
this.optionsState.reset();
KeysManager.get().clearAccountKeysAndData();
ModelManager.get().handleSignout();
Sync.get().handleSignout();
}
}.bind(this));
});
}

View File

@@ -144,7 +144,6 @@ export default class NoteCell extends React.PureComponent {
// We want to show "Deleting.." on top of note cell after the user confirms the dialogue
this.forceUpdate();
});
}
render() {
@@ -171,7 +170,7 @@ export default class NoteCell extends React.PureComponent {
{this.props.renderTags && note.tags.length > 0 &&
<View style={this.styles.noteTags}>
<Text numberOfLines={1} style={this.aggregateStyles(this.styles.noteTag)}>
{SNTag.arrayToDisplayString(note.tags)}
{this.props.tagsString}
</Text>
</View>
}

View File

@@ -62,6 +62,7 @@ export default class NoteList extends Component {
title={item.title}
text={item.text}
tags={item.tags}
tagsString={item.tagsString()}
pinned={item.pinned}
deleted={item.deleted}
archived={item.archived}

View File

@@ -7,7 +7,8 @@ import {
SFItemParams,
SFAlertManager,
SFStorageManager,
SFHttpManager
SFHttpManager,
SFAuthManager
} from 'standard-file-js';
global.SFItem = SFItem;
@@ -17,6 +18,7 @@ global.SFSyncManager = SFSyncManager;
global.SFAlertManager = SFAlertManager;
global.SFStorageManager = SFStorageManager;
global.SFHttpManager = SFHttpManager;
global.SFAuthManager = SFAuthManager;
import SF from "./lib/sfjs/sfjs"
global.SFJS = SF.get();

View File

@@ -1,274 +0,0 @@
import SF from './sfjs/sfjs'
import Server from './sfjs/httpManager'
import Storage from './sfjs/storageManager'
import Sync from './sfjs/syncManager'
import ModelManager from './sfjs/modelManager'
import AlertManager from './sfjs/alertManager'
import {Platform} from 'react-native';
import Keychain from "./keychain"
import KeysManager from "./keysManager"
let accountRelatedStorageKeys = [];
export default class Auth {
static instance = null;
static DidSignOutEvent = "DidSignOutEvent";
static WillSignInEvent = "WillSignInEvent";
static DidSignInEvent = "DidSignInEvent";
static getInstance() {
if (this.instance == null) {
this.instance = new Auth();
}
return this.instance;
}
static _defaultServer() {
if (__DEV__) {
if(Platform.OS === "android") {
return "http://10.0.2.2:3000"
} else {
return "http://localhost:3000"
}
} else {
return "https://sync.standardnotes.org";
}
}
constructor() {
this.eventSubscribers = [];
}
addEventObserver(events, callback) {
var observer = {key: new Date(), events, callback: callback};
this.eventSubscribers.push(observer);
return observer;
}
removeEventObserver(observer) {
_.pull(this.eventSubscribers, observer);
}
serverUrl() {
var user = KeysManager.get().user;
return (user && user.server) || Auth._defaultServer();
}
urlForPath(path, serverUrl) {
function joinPaths(parts){
var separator = "/";
var replace = new RegExp(separator+'{1,}', 'g');
return parts.join(separator).replace(replace, separator);
}
if(!serverUrl) {
serverUrl = this.serverUrl();
}
return joinPaths([serverUrl, path]);
}
offline() {
// an offline user could have keys saved if using passcode lock
var keys = KeysManager.get().activeKeys() || {};
return !keys.jwt;
}
protocolVersion() {
var authParams = KeysManager.get().activeAuthParams();
if(authParams && authParams.version) {
return authParams.version;
}
var keys = KeysManager.get().activeKeys();
if(keys && keys.ak) {
// If there's no version stored, and there's an ak, it has to be 002. Newer versions would have thier version stored in authParams.
return "002";
} else {
return "001";
}
}
validateLogin = (authParams, strictSignIn, callback) => {
if(!SF.get().supportedVersions().includes(authParams.version)) {
var message;
if(SF.get().isVersionNewerThanLibraryVersion(authParams.version)) {
// The user has a new account type, but is signing in to an older client.
message = "This version of the application does not support your newer account type. Please upgrade to the latest version of Standard Notes to sign in.";
} else {
// The user has a very old account type, which is no longer supported by this client
message = "The protocol version associated with your account is outdated and no longer supported by this application. Please visit standardnotes.org/help/security for more information.";
}
callback({message: message});
return;
}
// need to put this in a block due to asyncrounous confirm alert
var otherValidation = () => {
var minimum = SF.get().costMinimumForVersion(authParams.version);
if(authParams.pw_cost < minimum) {
let message = "Unable to login due to insecure password parameters. Please visit standardnotes.org/help/security for more information.";
callback({message: message});
return;
}
if(strictSignIn) {
// Refuse sign in if authParams.version is anything but the latest version
var latestVersion = SF.get().version();
if(authParams.version !== latestVersion) {
let message = `Strict sign in refused server sign in parameters. The latest security version is ${latestVersion}, but your account is reported to have version ${authParams.version}. If you'd like to proceed with sign in anyway, please disable strict sign in and try again.`;
callback({message: message});
return;
}
}
callback(null);
}
if(SF.get().isProtocolVersionOutdated(authParams.version)) {
let message = `The encryption version for your account, ${authParams.version}, is outdated and requires upgrade. You may proceed with login, but are advised to perform a security update using the web or desktop application. Please visit standardnotes.org/help/security for more information.`
AlertManager.get().confirm({
title: "Update Needed",
text: message,
confirmButtonText: "Sign In",
onConfirm: () => {
otherValidation();
},
onCancel: () => {
callback({});
}
})
return;
} else {
otherValidation();
}
}
login = (email, inputtedPassword, server, strictSignIn, extraParams, callback) => {
this.postEvent(Auth.WillSignInEvent);
var root = this;
this.getAuthParams(email, server, extraParams, (authParams, error) => {
if(error) {
callback(null, error);
return;
}
authParams.identifier = email;
this.validateLogin(authParams, strictSignIn, (validationError) => {
if(validationError) {
callback(null, validationError);
return;
}
SF.get().crypto.computeEncryptionKeysForUser(inputtedPassword, authParams).then((keys) => {
root.performLoginRequest(email, keys.pw, server, extraParams, async (response, error) => {
if(error) {
callback(null, error);
return;
}
if(response.user) {
await root.saveAuthParameters({token: response.token, email: email, server: server, authParams: authParams, keys: keys});
root.postEvent(Auth.DidSignInEvent);
}
callback(response.user, error);
})
});
});
})
}
register = async (email, inputtedPassword, server, callback) => {
var root = this;
// if(__DEV__) { authParams.pw_cost = 3000; }
SF.get().crypto.generateInitialKeysAndAuthParamsForUser(email, inputtedPassword).then((results) => {
let keys = results.keys;
let authParams = results.authParams;
root.performRegistrationRequest(email, keys.pw, authParams, server, async function(response, error) {
if(error) {
callback(null, error);
return;
}
if(response.user) {
await root.saveAuthParameters({token: response.token, email: email, server: server, authParams: authParams, keys: keys});
}
callback(response.user, error);
})
});
}
async performLoginRequest(email, pw, server, extraParams, callback) {
var url = this.urlForPath("auth/sign_in", server);
Server.get().postAbsolute(url, _.merge({email: email, password: pw}, extraParams), function(response){
callback(response, null);
}, function(error){
callback(null, error.error);
})
}
async performRegistrationRequest(email, pw, authParams, server, callback) {
var url = this.urlForPath("auth/", server);
Server.get().postAbsolute(url, _.merge({email: email, password: pw}, authParams), function(response){
callback(response, null);
}, function(error){
callback(null, error.error);
})
}
async saveAuthParameters({token, email, server, authParams, keys} = {}) {
try {
this._keys = keys;
return await Promise.all([
KeysManager.get().persistAccountKeys(_.merge(keys, {jwt: token})),
KeysManager.get().setAccountAuthParams(authParams),
KeysManager.get().saveUser({server: server, email: email})
]);
} catch(e) {
console.log("Error saving auth paramters", e);
return null;
}
}
async getAuthParams(email, server, extraParams, callback) {
var url = this.urlForPath("auth/params", server);
Server.get().getAbsolute(url, _.merge({email: email}, extraParams), (response) => {
callback(response, null);
}, (response) => {
console.log("Error getting auth params", response);
var error = response.error || {message: response};
callback(null, error);
})
}
signout() {
ModelManager.get().handleSignout();
KeysManager.get().clearAccountKeysAndData();
return Storage.get().clearAllModels().then(() => {
Sync.get().handleSignout();
this.postEvent(Auth.DidSignOutEvent);
});
}
postEvent(event) {
this.eventSubscribers.forEach(function(observer){
if(observer.events.includes(event)) {
observer.callback(event);
}
})
}
}

View File

@@ -28,13 +28,13 @@ export default class ComponentManager {
this.handlers = [];
ModelManager.get().addItemSyncObserver("component-manager", "*", (allItems, source) => {
ModelManager.get().addItemSyncObserver("component-manager", "*", (allItems, validItems, deletedItems, source) => {
/* If the source of these new or updated items is from a Component itself saving items, we don't need to notify
components again of the same item. Regarding notifying other components than the issuing component, other mapping sources
will take care of that, like ModelManager.MappingSourceRemoteSaved
*/
if(source == ModelManager.MappingSourceComponentRetrieved) {
if(source == SFModelManager.MappingSourceComponentRetrieved) {
return;
}
@@ -78,8 +78,7 @@ export default class ComponentManager {
jsonForItem(item, component, source) {
var params = {uuid: item.uuid, content_type: item.content_type, created_at: item.created_at, updated_at: item.updated_at, deleted: item.deleted};
params.content = item.createContentJSONFromProperties();
/* Legacy is using component.url key, so if it's present, use it, otherwise use uuid */
params.clientData = item.getDomainDataItem(component.url || component.uuid, ClientDataDomain) || {};
params.clientData = item.getDomainDataItem(component.getClientDataKey(), ClientDataDomain) || {};
/* This means the this function is being triggered through a remote Saving response, which should not update
actual local content values. The reason is, Save responses may be delayed, and a user may have changed some values
@@ -88,7 +87,7 @@ export default class ComponentManager {
3/7/18: Add MappingSourceLocalSaved as well to handle fully offline saving. github.com/standardnotes/forum/issues/169
*/
if(source && (source == ModelManager.MappingSourceRemoteSaved || source == ModelManager.MappingSourceLocalSaved)) {
if(source && (source == SFModelManager.MappingSourceRemoteSaved || source == SFModelManager.MappingSourceLocalSaved)) {
params.isMetadataUpdate = true;
}
this.removePrivatePropertiesFromResponseItems([params], component);
@@ -191,7 +190,7 @@ export default class ComponentManager {
*/
if(message.action === "stream-context-item") {
if(message.action === "stream-context-item") {
this.handleStreamContextItemMessage(component, message);
} else if(message.action === "set-component-data") {
this.handleSetComponentDataMessage(component, message);
@@ -274,7 +273,7 @@ export default class ComponentManager {
We map the items here because modelManager is what updates the UI. If you were to instead get the items directly,
this would update them server side via sync, but would never make its way back to the UI.
*/
var localItems = ModelManager.get().mapResponseItemsToLocalModels(responseItems, ModelManager.MappingSourceComponentRetrieved);
var localItems = ModelManager.get().mapResponseItemsToLocalModels(responseItems, SFModelManager.MappingSourceComponentRetrieved);
for(var item of localItems) {
var responseItem = _.find(responseItems, {uuid: item.uuid});
@@ -288,7 +287,7 @@ export default class ComponentManager {
Sync.get().sync().then((response) => {
// Allow handlers to be notified when a save begins and ends, to update the UI
var saveMessage = Object.assign({}, message);
saveMessage.action = response && response.error ? "save-error" : "save-success";
saveMessage.action = (response && response.error) ? "save-error" : "save-success";
this.replyToMessage(component, message, {error: response.error})
this.handleMessage(component, saveMessage);
});

View File

@@ -13,18 +13,21 @@ export default class AlertManager extends SFAlertManager {
return this.instance;
}
confirm({title, text, confirmButtonText = "OK", onConfirm, onCancel} = {}) {
// On iOS, confirm should go first. On Android, cancel should go first.
let buttons = [
{text: 'Cancel', onPress: onCancel},
{text: confirmButtonText, onPress: onConfirm},
];
Alert.alert(
title,
text,
buttons,
{ cancelable: true }
)
async confirm({title, text, confirmButtonText = "OK", onConfirm, onCancel} = {}) {
return new Promise((resolve, reject) => {
// On iOS, confirm should go first. On Android, cancel should go first.
let buttons = [
{text: 'Cancel', onPress: () => {
reject();
onCancel && onCancel();
}},
{text: confirmButtonText, onPress: () => {
resolve();
onConfirm && onConfirm();
}},
];
Alert.alert(title, text, buttons, { cancelable: true })
})
}
}

View File

@@ -0,0 +1,58 @@
import {Platform} from 'react-native';
import KeysManager from "../keysManager"
import Storage from './storageManager'
import Server from './httpManager'
import AlertManager from './alertManager'
export default class Auth extends SFAuthManager {
static instance = null;
static get() {
if(this.instance == null) {
this.instance = new Auth(Storage.get(), Server.get(), AlertManager.get());
}
return this.instance;
}
defaultServer() {
if (__DEV__) {
if(Platform.OS === "android") {
return "http://10.0.2.2:3000"
} else {
return "http://localhost:3000"
}
} else {
return "https://sync.standardnotes.org";
}
}
serverUrl() {
var user = KeysManager.get().user;
return (user && user.server) || this.defaultServer();
}
offline() {
// an offline user could have keys saved if using passcode lock
var keys = KeysManager.get().activeKeys() || {};
return !keys.jwt;
}
async getAuthParams() {
return KeysManager.get().activeAuthParams();
}
async handleAuthResponse(response, email, url, authParams, keys) {
super.handleAuthResponse(response, email, url, authParams, keys);
try {
this._keys = keys;
return await Promise.all([
KeysManager.get().persistAccountKeys(_.merge(keys, {jwt: response.token})),
KeysManager.get().setAccountAuthParams(authParams),
KeysManager.get().saveUser({server: url, email: email})
]);
} catch(e) {
console.log("Error saving auth paramters", e);
return null;
}
}
}

View File

@@ -1,11 +1,10 @@
import Storage from "./storageManager"
import Theme from "../../models/subclass/theme"
import "../../models/extend/item.js";
SFModelManager.ContentTypeClassMapping = {
"Note" : SNNote,
"Tag" : SNTag,
"SN|Theme" : Theme,
"SN|Theme" : SNTheme,
"SN|Component" : SNComponent
};

View File

@@ -16,6 +16,10 @@ export default class SF extends StandardFile {
}
return this.instance;
}
supportsPasswordDerivationCost(cost) {
return true;
}
}
class SFReactNativeCrypto extends SFAbstractCrypto {

View File

@@ -12,6 +12,13 @@ export default class Storage extends SFStorageManager {
return this.instance;
}
constructor() {
super();
AsyncStorage.getAllKeys().then((keys) => {
console.log("all model keys", keys);
})
}
async getItem(key) {
try {
return AsyncStorage.getItem(key);
@@ -102,9 +109,7 @@ export default class Storage extends SFStorageManager {
return;
}
return Promise.all(items.map((item) => {
console.log("Saving model", JSON.stringify(item));
return AsyncStorage.setItem(this.keyForItem(item), JSON.stringify(item));
}));
}

View File

@@ -4,7 +4,7 @@ import ModelManager from './modelManager'
import Storage from './storageManager'
import AlertManager from './alertManager'
import Auth from '../authManager'
import Auth from '../sfjs/authManager'
import KeysManager from '../keysManager'
export default class Sync extends SFSyncManager {
@@ -24,7 +24,6 @@ export default class Sync extends SFSyncManager {
KeysManager.get().registerAccountRelatedStorageKeys(["syncToken", "cursorToken"]);
this.setKeyRequestHandler((request) => {
console.log(request);
var keys;
if(request == SFSyncManager.KeyRequestLoadSaveAccount || request == SFSyncManager.KeyRequestLoadLocal) {
keys = KeysManager.get().activeKeys();
@@ -34,7 +33,7 @@ export default class Sync extends SFSyncManager {
}
let auth_params = KeysManager.get().activeAuthParams();
let offline = Auth.getInstance().offline();
let offline = Auth.get().offline();
return {keys, auth_params, offline}
})
@@ -46,7 +45,7 @@ export default class Sync extends SFSyncManager {
}
async getServerURL() {
return Auth.getInstance().serverUrl();
return Auth.get().serverUrl();
}
handleSignout() {

View File

@@ -1,34 +0,0 @@
import SF from "../../lib/sfjs/sfjs"
export default class Theme extends SNTheme {
setMobileRules(rules) {
this.setAppDataItem("mobileRules", rules);
}
getMobileRules() {
return this.getAppDataItem("mobileRules") || {constants: {}, rules: {}};
}
// Same as getMobileRules but without default value
hasMobileRules() {
return this.getAppDataItem("mobileRules");
}
setNotAvailOnMobile(na) {
this.setAppDataItem("notAvailableOnMobile", na);
}
getNotAvailOnMobile() {
return this.getAppDataItem("notAvailableOnMobile");
}
/* We must not use .active because if you set that to true, it will also activate that theme on desktop/web */
setMobileActive(active) {
this.setAppDataItem("mobileActive", active);
}
isMobileActive() {
return this.getAppDataItem("mobileActive");
}
}

View File

@@ -106,6 +106,25 @@ export default class Abstract extends Component {
}
setNavBarSubtitle(title) {
if(!this.visible || !this.willBeVisible) {
return;
}
this.props.navigator.setSubTitle({
subtitle: title
});
if(!this.didSetNavBarStyle) {
this.didSetNavBarStyle = true;
var color = GlobalStyles.constantForKey(App.isIOS ? "mainTextColor" : "navBarTextColor");
this.props.navigator.setStyle({
navBarSubtitleColor: GlobalStyles.hexToRGBA(color, 0.5),
navBarSubtitleFontSize: 12
});
}
}
dismissModal() {
this.props.navigator.dismissModal({animationType: "slide-down"})
}

View File

@@ -5,7 +5,7 @@ import Sync from '../lib/sfjs/syncManager'
import ModelManager from '../lib/sfjs/modelManager'
import AlertManager from '../lib/sfjs/alertManager'
import Auth from '../lib/authManager'
import Auth from '../lib/sfjs/authManager'
import KeysManager from '../lib/keysManager'
import SectionHeader from "../components/SectionHeader";
@@ -42,7 +42,7 @@ export default class Account extends Abstract {
loadInitialState() {
super.loadInitialState();
this.mergeState({params: {server: Auth.getInstance().serverUrl()}})
this.mergeState({params: {server: Auth.get().serverUrl()}})
this.syncEventHandler = Sync.get().addEventHandler((event, data) => {
if(event == "local-data-loaded") {
@@ -50,17 +50,18 @@ export default class Account extends Abstract {
} else if(event == "sync-session-invalid") {
if(!this.didShowSessionInvalidAlert) {
this.didShowSessionInvalidAlert = true;
AlertManager.get().confirm(
"Session Expired",
"Your session has expired. New changes will not be pulled in. Please sign out and sign back in to refresh your session.", "Sign Out",
() => {
AlertManager.get().confirm({
title: "Session Expired",
text: "Your session has expired. New changes will not be pulled in. Please sign out and sign back in to refresh your session.",
confirmButtonText: "Sign Out",
onConfirm: () => {
this.didShowSessionInvalidAlert = false;
Auth.getInstance().signout();
Auth.get().signout();
},
() => {
onCancel: () => {
this.didShowSessionInvalidAlert = false;
}
)
})
}
}
})
@@ -157,9 +158,11 @@ export default class Account extends Abstract {
// Unlock sync after all sign in processes are complete.
Sync.get().lockSyncing();
Auth.getInstance().login(email, password, params.server, strict, extraParams, (user, error) => {
Auth.get().login(params.server, email, password, strict, extraParams).then((response) => {
if(!response || response.error) {
var error = response ? response.error : {message: "An unknown error occured."}
if(error) {
Sync.get().unlockSyncing();
if(error.tag == "mfa-required" || error.tag == "mfa-invalid") {
@@ -207,10 +210,11 @@ export default class Account extends Abstract {
var params = this.state.params;
Auth.getInstance().register(params.email, params.password, params.server, (user, error) => {
Auth.get().register( params.server, params.email, params.password).then((response) => {
this.mergeState({registering: false, confirmRegistration: false});
if(error) {
if(!response || response.error) {
var error = response ? response.error : {message: "An unknown error occured."}
Alert.alert('Oops', error.message, [{text: 'OK'}])
return;
}
@@ -258,7 +262,7 @@ export default class Account extends Abstract {
text: "Signing out will remove all data from this device, including notes and tags. Make sure your data is synced before proceeding.",
confirmButtonText: "Sign Out",
onConfirm: () => {
Auth.getInstance().signout().then(() => {
Auth.get().signout().then(() => {
this.forceUpdate();
})
}
@@ -266,13 +270,13 @@ export default class Account extends Abstract {
}
async onExportPress(encrypted, callback) {
var version = Auth.getInstance().protocolVersion();
var auth_params = await Auth.get().getAuthParams();
var keys = encrypted ? KeysManager.get().activeKeys() : null;
var items = [];
for(var item of ModelManager.get().allItems) {
var itemParams = new SFItemParams(item, keys, version);
var itemParams = new SFItemParams(item, keys, auth_params);
var params = await itemParams.paramsForExportFile();
items.push(params);
}
@@ -443,7 +447,7 @@ export default class Account extends Abstract {
return (<LockedView />);
}
let signedIn = !Auth.getInstance().offline();
let signedIn = !Auth.get().offline();
var themes = GlobalStyles.get().themes();
return (

View File

@@ -2,7 +2,7 @@ import React, { Component } from 'react';
import App from "../app"
import SF from '../lib/sfjs/sfjs'
import Storage from '../lib/sfjs/storageManager'
import Auth from '../lib/authManager'
import Auth from '../lib/sfjs/authManager'
import KeysManager from '../lib/keysManager'
import Abstract from "./Abstract"

View File

@@ -2,7 +2,7 @@ import React, { Component } from 'react';
import App from '../app'
import Sync from '../lib/sfjs/syncManager'
import ModelManager from '../lib/sfjs/modelManager'
import Auth from '../lib/authManager'
import Auth from '../lib/sfjs/authManager'
import Abstract from "./Abstract"
import ComponentManager from '../lib/componentManager'
@@ -220,6 +220,7 @@ export default class Compose extends Abstract {
if(!newTags.includes(oldTag)) {
oldTag.removeItemAsRelationship(note);
oldTag.setDirty(true);
// Notes don't have tags as relationships anymore, but we'll keep this to clean up old notes.
note.removeItemAsRelationship(oldTag);
}
}
@@ -254,7 +255,6 @@ export default class Compose extends Abstract {
this.note.initUUID().then(() => {
if(this.props.selectedTagId) {
var tag = ModelManager.get().findItem(this.props.selectedTagId);
this.note.addItemAsRelationship(tag);
tag.addItemAsRelationship(this.note);
}
this.save();
@@ -298,7 +298,7 @@ export default class Compose extends Abstract {
if(this.statusTimeout) clearTimeout(this.statusTimeout);
this.statusTimeout = setTimeout(function(){
var status = "All changes saved"
if(Auth.getInstance().offline()) {
if(Auth.get().offline()) {
status += " (offline)";
}
this.saveError = false;
@@ -316,25 +316,6 @@ export default class Compose extends Abstract {
}.bind(this));
}
setNavBarSubtitle(title) {
if(!this.visible || !this.willBeVisible) {
return;
}
this.props.navigator.setSubTitle({
subtitle: title
});
if(!this.didSetNavBarStyle) {
this.didSetNavBarStyle = true;
var color = GlobalStyles.constantForKey(App.isIOS ? "mainTextColor" : "navBarTextColor");
this.props.navigator.setStyle({
navBarSubtitleColor: GlobalStyles.hexToRGBA(color, 0.5),
navBarSubtitleFontSize: 12
});
}
}
render() {
if(this.state.lockContent) {
return (<LockedView />);

View File

@@ -7,7 +7,7 @@ import Storage from '../lib/sfjs/storageManager'
import Sync from '../lib/sfjs/syncManager'
import AlertManager from '../lib/sfjs/alertManager'
import Auth from '../lib/authManager'
import Auth from '../lib/sfjs/authManager'
import KeysManager from '../lib/keysManager'
import Keychain from "../lib/keychain"
@@ -82,7 +82,7 @@ export default class Notes extends Abstract {
Sync.get().removeEventHandler(this.syncObserver);
Sync.get().removeSyncStatusObserver(this.syncStatusObserver);
Auth.getInstance().removeEventObserver(this.signoutObserver);
Auth.get().removeEventHandler(this.signoutObserver);
if(this.options) {
this.options.removeChangeObserver(this.optionsObserver);
}
@@ -107,7 +107,12 @@ export default class Notes extends Abstract {
this.syncObserver = Sync.get().addEventHandler((event, data) => {
if(event == "sync:completed") {
if(_.find(data.retrievedItems, {content_type: "Note"}) || _.find(data.unsavedItems, {content_type: "Note"})) {
// We want to reload the list of the retrieved items contains notes or tags.
// Since Notes no longer have relationships on tags, if a note's tags change, only the tag will be synced.
var retrievedHasNoteOrTag = data.retrievedItems && data.retrievedItems.find((item) => {
return ["Note", "Tag"].includes(item.content_type);
})
if(retrievedHasNoteOrTag || _.find(data.unsavedItems, {content_type: "Note"})) {
this.reloadList();
}
this.mergeState({refreshing: false, loading: false});
@@ -137,21 +142,22 @@ export default class Notes extends Abstract {
}
})
this.signoutObserver = Auth.getInstance().addEventObserver([Auth.DidSignOutEvent, Auth.WillSignInEvent, Auth.DidSignInEvent], (event) => {
if(event == Auth.WillSignInEvent) {
this.signoutObserver = Auth.get().addEventHandler((event) => {
if(event == SFAuthManager.WillSignInEvent) {
this.mergeState({loading: true})
} else if(event == Auth.DidSignInEvent) {
} else if(event == SFAuthManager.DidSignInEvent) {
// Check if there are items that are errorDecrypting and try decrypting with new keys
Sync.get().refreshErroredItems().then(() => {
this.reloadList();
})
} else if(event == Auth.DidSignOutEvent) {
} else if(event == SFAuthManager.DidSignOutEvent) {
this.setStatusBarText(null);
}
});
}
setStatusBarText(text) {
// this.setNavBarSubtitle(text);
this.mergeState({showSyncBar: text != null, syncBarText: text});
}

View File

@@ -104,21 +104,6 @@ export default class Webview extends Abstract {
this.setNavBarSubtitle("Error saving");
}
setNavBarSubtitle(title) {
this.props.navigator.setSubTitle({
subtitle: title
});
if(!this.didSetNavBarStyle) {
this.didSetNavBarStyle = true;
var color = GlobalStyles.constantForKey(App.isIOS ? "mainTextColor" : "navBarTextColor");
this.props.navigator.setStyle({
navBarSubtitleColor: GlobalStyles.hexToRGBA(color, 0.5),
navBarSubtitleFontSize: 12
});
}
}
onNavigatorEvent(event) {
super.onNavigatorEvent(event);
if (event.type == 'NavBarButtonPress') { // this is the event type for button presses