commit
c517f25803
@ -0,0 +1,28 @@ |
||||
import {MultiState, initStates} from "../helpers/reactive-fassade"; |
||||
import {WorkPackageResource} from "./api/api-v3/hal-resources/work-package-resource.service"; |
||||
import {opServicesModule} from "../angular-modules"; |
||||
|
||||
export class States { |
||||
|
||||
workPackages = new MultiState<WorkPackageResource>(); |
||||
|
||||
constructor() { |
||||
initStates(this, function (msg: any) { |
||||
if (~location.hostname.indexOf("localhost")) { |
||||
(console.trace as any)(msg); // RR: stupid hack to avoid compiler error
|
||||
} |
||||
}); |
||||
} |
||||
|
||||
} |
||||
|
||||
opServicesModule.service('states', States); |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,188 @@ |
||||
import {scopedObservable} from "./angular-rx-utils"; |
||||
import Observable = Rx.Observable; |
||||
import IScope = angular.IScope; |
||||
import IPromise = Rx.IPromise; |
||||
|
||||
export abstract class StoreElement { |
||||
|
||||
public pathInStore: string = null; |
||||
|
||||
public logFn: (msg: any) => any = null; |
||||
|
||||
log(msg: string, reason?: string) { |
||||
reason = reason === undefined ? "" : " // " + reason; |
||||
if (this.pathInStore && this.logFn) { |
||||
this.logFn("[" + this.pathInStore + "] " + msg + reason); |
||||
} |
||||
} |
||||
} |
||||
|
||||
interface PromiseLike<T> { |
||||
then(successCallback: (value: T) => any, errorCallback: (value: T) => any): any; |
||||
} |
||||
|
||||
export class State<T> extends StoreElement { |
||||
|
||||
private timestampOfLastValue = -1; |
||||
|
||||
private timestampOfLastPromise = -1; |
||||
|
||||
private subject = new Rx.BehaviorSubject<T>(null); |
||||
|
||||
private lastValue: T = null; |
||||
|
||||
private cleared = new Rx.Subject(); |
||||
|
||||
private observable: Observable<T>; |
||||
|
||||
constructor() { |
||||
super(); |
||||
this.observable = this.subject.filter(val => val !== null && val !== undefined); |
||||
} |
||||
|
||||
/** |
||||
* Returns true if this state either has a value of if |
||||
* a value is awaited from a promise (via putFromPromise). |
||||
*/ |
||||
public isPristine(): boolean { |
||||
return this.timestampOfLastValue === -1 && this.timestampOfLastPromise === -1; |
||||
} |
||||
|
||||
public isValueOrPromiseOlderThan(timeoutInMs: number) { |
||||
const ageValue = Date.now() - this.timestampOfLastValue; |
||||
const agePromise = Date.now() - this.timestampOfLastPromise; |
||||
return ageValue > timeoutInMs && agePromise > timeoutInMs; |
||||
} |
||||
|
||||
public hasValue(): boolean { |
||||
return this.lastValue !== null && this.lastValue !== undefined; |
||||
} |
||||
|
||||
/** |
||||
* Returns the current value or 'null', if no value is present. |
||||
* Therefore, calls to this method should always be guarded by State#hasValue(). |
||||
* |
||||
* However, it is usually better to use State#get()/State#observe(). |
||||
*/ |
||||
public getCurrentValue(): T { |
||||
return this.lastValue; |
||||
} |
||||
|
||||
public clear(reason?: string): this { |
||||
this.log("State#clear()", reason); |
||||
this.setState(null); |
||||
return this; |
||||
} |
||||
|
||||
public put(value: T, reason?: string): this { |
||||
this.log("State#put(...)", reason); |
||||
this.setState(value); |
||||
return this; |
||||
} |
||||
|
||||
public putFromPromise(promise: PromiseLike<T>): this { |
||||
this.clear(); |
||||
this.timestampOfLastPromise = Date.now(); |
||||
promise.then( |
||||
// success
|
||||
(value: T) => { |
||||
this.log("State#putFromPromise(...)"); |
||||
this.setState(value); |
||||
}, |
||||
// error
|
||||
() => { |
||||
this.log("State#putFromPromise ERROR"); |
||||
this.timestampOfLastPromise = -1; |
||||
} |
||||
); |
||||
return this; |
||||
} |
||||
|
||||
public putFromPromiseIfPristine(calledIfPristine: () => PromiseLike<T>): this { |
||||
if (this.isPristine()) { |
||||
this.putFromPromise(calledIfPristine()); |
||||
} |
||||
return this; |
||||
} |
||||
|
||||
public get(): IPromise<T> { |
||||
return this.observable.take(1).toPromise(); |
||||
} |
||||
|
||||
public observe(scope: IScope): Observable<T> { |
||||
return this.scopedObservable(scope); |
||||
} |
||||
|
||||
public observeCleared(scope: IScope): Observable<any> { |
||||
return scope ? scopedObservable(scope, this.cleared.asObservable()) : this.cleared.asObservable(); |
||||
} |
||||
|
||||
private setState(val: T) { |
||||
this.lastValue = val; |
||||
this.subject.onNext(val); |
||||
|
||||
if (val === null || val === undefined) { |
||||
this.timestampOfLastValue = -1; |
||||
this.timestampOfLastPromise = -1; |
||||
this.cleared.onNext(null); |
||||
} else { |
||||
this.timestampOfLastValue = Date.now(); |
||||
} |
||||
} |
||||
|
||||
private scopedObservable(scope: IScope): Observable<T> { |
||||
return scope ? scopedObservable(scope, this.observable) : this.observable; |
||||
} |
||||
|
||||
} |
||||
|
||||
export class MultiState<T> extends StoreElement { |
||||
|
||||
private states: {[id: string]: State<T>} = {}; |
||||
|
||||
constructor() { |
||||
super(); |
||||
} |
||||
|
||||
clearAll() { |
||||
this.states = {}; |
||||
} |
||||
|
||||
put(id: string, value: T): State<T> { |
||||
this.log("MultiState#put(" + id + ")"); |
||||
const state = this.get(id); |
||||
state.put(value); |
||||
return state; |
||||
} |
||||
|
||||
get(id: string): State<T> { |
||||
if (this.states[id] === undefined) { |
||||
this.states[id] = new State<T>(); |
||||
} |
||||
return this.states[id]; |
||||
} |
||||
|
||||
} |
||||
|
||||
function traverse(elem: any, path: string, logFn: (msg: any) => any) { |
||||
|
||||
for (const key in elem) { |
||||
if (!elem.hasOwnProperty(key)) { |
||||
continue; |
||||
} |
||||
const value = elem[key]; |
||||
|
||||
let location = path.length > 0 ? path + "." + key : key; |
||||
if (value instanceof StoreElement) { |
||||
value.pathInStore = location; |
||||
value.logFn = logFn; |
||||
} else { |
||||
traverse(value, location, logFn); |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
export function initStates(states: any, logFn?: (msg: any) => any) { |
||||
return traverse(states, "", logFn); |
||||
} |
@ -1,23 +0,0 @@ |
||||
0 info it worked if it ends with ok |
||||
1 verbose cli [ '/home/roman/.nodenv/versions/4.2.1/bin/node', |
||||
1 verbose cli '/home/roman/.nodenv/versions/4.2.1/bin/npm', |
||||
1 verbose cli 'bin' ] |
||||
2 info using npm@2.14.7 |
||||
3 info using node@v4.2.1 |
||||
4 verbose exit [ 0, true ] |
||||
5 verbose stack Error: write EPIPE |
||||
5 verbose stack at Object.exports._errnoException (util.js:874:11) |
||||
5 verbose stack at exports._exceptionWithHostPort (util.js:897:20) |
||||
5 verbose stack at WriteWrap.afterWrite (net.js:763:14) |
||||
6 verbose cwd /home/roman/prjs/openproject/repo/frontend |
||||
7 error Linux 3.13.0-24-generic |
||||
8 error argv "/home/roman/.nodenv/versions/4.2.1/bin/node" "/home/roman/.nodenv/versions/4.2.1/bin/npm" "bin" |
||||
9 error node v4.2.1 |
||||
10 error npm v2.14.7 |
||||
11 error code EPIPE |
||||
12 error errno EPIPE |
||||
13 error syscall write |
||||
14 error write EPIPE |
||||
15 error If you need help, you may report this error at: |
||||
15 error <https://github.com/npm/npm/issues> |
||||
16 verbose exit [ 1, true ] |
Loading…
Reference in new issue