Merge pull request #6571 from isocolsky/copay-v4

Support file storage
This commit is contained in:
Gustavo Maximiliano Cortez 2017-08-15 12:30:30 -03:00 committed by GitHub
commit c3df40862c
8 changed files with 220 additions and 18 deletions

View File

@ -30,6 +30,7 @@
"@ionic-native/clipboard": "4.1.0",
"@ionic-native/core": "3.12.1",
"@ionic-native/social-sharing": "4.1.0",
"@ionic-native/file": "^4.1.0",
"@ionic-native/splash-screen": "3.12.1",
"@ionic-native/status-bar": "3.12.1",
"@ionic-native/toast": "4.1.0",

View File

@ -1,11 +1,13 @@
import { TestBed, inject } from '@angular/core/testing';
import { Logger, Level as LoggerLevel } from '@nsalaun/ng-logger';
import { Platform } from 'ionic-angular';
import { PersistenceProvider } from './persistence';
import { IStorage, ISTORAGE } from './storage/istorage';
import { RamStorage } from './storage/ram-storage';
import { LocalStorage } from './storage/local-storage';
import { Logger, Level as LoggerLevel } from '@nsalaun/ng-logger';
import { PlatformProvider } from '../platform/platform';
import { Platform } from 'ionic-angular';
import { ChromeStorage } from './storage/chrome-storage';
import { FileStorage } from './storage/file-storage';
describe('Storage Service', () => {
beforeEach(() => {
@ -13,8 +15,8 @@ describe('Storage Service', () => {
providers: [
PersistenceProvider,
{ provide: Logger, useValue: new Logger(LoggerLevel.DEBUG) },
{ provide: PlatformProvider },
{ provide: ISTORAGE, useClass: RamStorage, deps: [PlatformProvider, Logger] },
{ provide: ISTORAGE, useClass: RamStorage, deps: [Logger, Platform] },
Platform,
]
});
});
@ -24,7 +26,7 @@ describe('Storage Service', () => {
beforeEach(inject([PersistenceProvider], (pp: PersistenceProvider) => {
service = pp;
}));
it('should correctly perform a profile roundtrip', () => {
it('should correctly perform a profile roundtrip', (done) => {
let p = { name: 'My profile' };
service.storeNewProfile(p)
.catch((err) => expect(err).toBeNull)
@ -34,7 +36,8 @@ describe('Storage Service', () => {
.then((profile) => {
expect(typeof profile).toEqual('object');
expect(profile.name).toEqual('My profile');
});
})
.then(done);
});
it('should fail to create a profile when one already exists', () => {

View File

@ -38,7 +38,8 @@ const Keys = {
};
export let persistenceProviderFactory = (platform: PlatformProvider, log: Logger) => {
let storage = new RamStorage(platform, log);
// TODO: select appropriate storage service based on platform
let storage = new RamStorage(log);
return new PersistenceProvider(storage, log);
};

View File

@ -0,0 +1,57 @@
import { Injectable } from '@angular/core';
import { Logger } from '@nsalaun/ng-logger';
import * as _ from 'lodash';
import { IStorage, KeyAlreadyExistsError } from './istorage';
@Injectable()
export class ChromeStorage implements IStorage {
ls: chrome.storage.StorageArea;
constructor(private log: Logger) {
if (!chrome.storage || !chrome.storage.local) throw new Error('Chrome storage not supported');
this.ls = chrome.storage.local;
}
get(k: string): Promise<any> {
return new Promise(resolve => {
let v = this.ls.get(k, (v) => {
if (!v) return resolve(null);
if (!_.isString(v)) return resolve(v);
let parsed: any;
try {
parsed = JSON.parse(v);
} catch (e) {
}
resolve(parsed || v);
});
});
}
set(k: string, v: any): Promise<void> {
if (_.isObject(v)) {
v = JSON.stringify(v);
}
if (v && !_.isString(v)) {
v = v.toString();
}
let obj = {};
obj[k] = v;
return new Promise<void>(resolve => {
this.ls.set(obj, resolve);
});
}
remove(k: string): Promise<void> {
return new Promise<void>(resolve => {
this.ls.remove(k, resolve);
});
}
create(k: string, v: any): Promise<void> {
return this.get(k).then((data) => {
if (data) throw new KeyAlreadyExistsError();
this.set(k, v);
});
}
}

View File

@ -0,0 +1,135 @@
import { Injectable } from '@angular/core';
import { Logger } from '@nsalaun/ng-logger';
import * as _ from 'lodash';
import { File, DirectoryEntry, FileEntry } from '@ionic-native/file';
import { Platform } from 'ionic-angular';
import { IStorage, KeyAlreadyExistsError } from './istorage';
@Injectable()
export class FileStorage implements IStorage {
fs: FileSystem;
dir: DirectoryEntry;
constructor(private file: File, private platform: Platform, private log: Logger) {
}
init(): Promise<void> {
if (this.fs && this.dir) return Promise.resolve();
let onSuccess = (fs: FileSystem): Promise<void> => {
console.log('File system started: ', fs.name, fs.root.name);
this.fs = fs;
return this.getDir().then(dir => {
if (!dir.nativeURL) return;
this.dir = dir;
this.log.debug("Got main dir:", dir.nativeURL);
});
};
function onFailure(err: Error): Promise<void> {
this.log.error('Could not init file system: ' + err.message);
return Promise.reject(err);
};
return this.platform.ready().then(() => {
window.requestFileSystem(1, 0, onSuccess, onFailure);
});
}
// See https://github.com/apache/cordova-plugin-file/#where-to-store-files
getDir(): Promise<DirectoryEntry> {
if (!this.file) {
return Promise.reject(new Error('Could not write on device storage'));
}
var url = this.file.dataDirectory;
return this.file.resolveDirectoryUrl(url)
.catch(err => {
let msg = 'Could not resolve filesystem ' + url;
this.log.warn(msg, err);
throw err || new Error(msg);
});
};
get(k: string): Promise<any> {
let parseResult = (v: any): any => {
if (!v) return null;
if (!_.isString(v)) return v;
let parsed: any;
try {
parsed = JSON.parse(v);
} catch (e) {
}
return parsed || v;
};
return this.init()
.then(() => {
return this.file.getFile(this.dir, k, { create: false });
})
.then(fileEntry => {
if (!fileEntry) return;
return new Promise((resolve) => {
fileEntry.file(file => {
var reader = new FileReader();
reader.onloadend = () => {
resolve(parseResult(reader.result));
}
reader.readAsText(file);
});
});
})
.catch(err => {
// Not found
if (err.code == 1) return;
else throw err;
});
}
set(k: string, v: any): Promise<void> {
return this.init()
.then(() => {
return this.file.getFile(this.dir, k, { create: true });
})
.then(fileEntry => {
// Create a FileWriter object for our FileEntry (log.txt).
return new Promise<void>((resolve, reject) => {
fileEntry.createWriter(fileWriter => {
fileWriter.onwriteend = (e) => {
this.log.info('Write completed:' + k);
resolve();
};
fileWriter.onerror = (e) => {
this.log.error('Write failed', e);
reject(e);
};
if (_.isObject(v))
v = JSON.stringify(v);
if (v && !_.isString(v)) {
v = v.toString();
}
this.log.debug('Writing:', k, v);
fileWriter.write(v);
}, err => {
this.log.error('Could not create writer', err);
reject(err);
});
});
});
};
remove(k: string): Promise<void> {
return Promise.resolve();
}
create(k: string, v: any): Promise<void> {
return this.get(k).then((data) => {
if (data) throw new KeyAlreadyExistsError();
this.set(k, v);
});
}
}

View File

@ -1,5 +1,4 @@
import { Injectable } from '@angular/core';
import { PlatformProvider } from '../../platform/platform';
import { Logger } from '@nsalaun/ng-logger';
import * as _ from 'lodash';
@ -8,13 +7,13 @@ import { IStorage, KeyAlreadyExistsError } from './istorage';
@Injectable()
export class LocalStorage implements IStorage {
ls: Storage;
constructor(private platform: PlatformProvider, private log: Logger) {
constructor(private log: Logger) {
this.ls = (typeof window.localStorage !== "undefined") ? window.localStorage : null;
if (!this.ls) throw new Error('localstorage not available');
}
get(k: string): Promise<any> {
return new Promise((resolve) => {
return new Promise(resolve => {
let v = this.ls.getItem(k);
if (!v) return resolve(null);
if (!_.isString(v)) return resolve(v);
@ -28,7 +27,7 @@ export class LocalStorage implements IStorage {
}
set(k: string, v: any): Promise<void> {
return Promise.resolve().then(() => {
return new Promise<void>(resolve => {
if (_.isObject(v)) {
v = JSON.stringify(v);
}
@ -37,12 +36,14 @@ export class LocalStorage implements IStorage {
}
this.ls.setItem(k, v);
resolve();
});
}
remove(k: string): Promise<void> {
return Promise.resolve().then(() => {
return new Promise<void>(resolve => {
this.ls.removeItem(k);
resolve();
});
}

View File

@ -1,20 +1,25 @@
import { IStorage, KeyAlreadyExistsError } from './istorage';
import { PlatformProvider } from '../../platform/platform';
import { Logger } from '@nsalaun/ng-logger';
export class RamStorage implements IStorage {
hash = {};
constructor(private platform: PlatformProvider, private log: Logger) { }
constructor(private log: Logger) { }
get(k: string): Promise<any> {
return Promise.resolve(this.hash[k]);
};
set(k: string, v: any): Promise<void> {
return Promise.resolve().then(() => { this.hash[k] = v });
return new Promise<void>(resolve => {
this.hash[k] = v;
resolve();
});
};
remove(k: string): Promise<void> {
return Promise.resolve().then(() => { delete this.hash[k]; });
return new Promise<void>(resolve => {
delete this.hash[k];
resolve();
});
};
create(k: string, v: any): Promise<void> {
return this.get(k).then((data) => {

View File

@ -25,7 +25,6 @@
"rewriteTsconfig": false
},
"types": [
"chrome",
"lodash",
"node"
]