-
Notifications
You must be signed in to change notification settings - Fork 67
/
Copy pathAppStore.ts
119 lines (96 loc) · 2.88 KB
/
AppStore.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import { observable, computed, action, reaction } from 'mobx';
import { RawRuleOf, subject } from '@casl/ability';
import { User } from '../models/User';
import { Article } from '../models/Article';
import { createAbility, AppAbility } from './ability';
import { Http } from './http';
interface Session {
email: string
token: string
rules: RawRuleOf<AppAbility>[]
}
interface Storage {
getItem(key: string): string | null
}
export class BadCredentialsError extends Error {
}
const STORE_KEY = 'store.v1';
export default class AppStore {
@observable user?: User;
@observable token: string = '';
@observable pageTitle: string = '';
@observable rules: RawRuleOf<AppAbility>[] = [];
@computed get isLoggedIn() {
return this.token !== '';
}
public readonly ability = createAbility();
constructor(
private _http: Http
) {
reaction(() => this.token, (token) => {
if (token) {
this._http.defaults.headers.Authorization = token;
} else {
delete this._http.defaults.headers.Authorization;
}
});
reaction(() => this.rules, (rules) => this.ability.update(rules));
}
login(email: string, password: string) {
return this._http.post<Session>('/session', { email, password })
.then(response => this._setSession(response.data))
.catch((error) => {
if (error.response && error.response.status === 400) {
throw new BadCredentialsError();
}
throw error;
});
}
@action
logout() {
this.token = '';
this.user = undefined;
this.rules = [{ action: 'read', subject: 'Article' }];
}
@action
private _setSession(session: Session) {
this.token = session.token;
this.user = { email: session.email };
this.rules = session.rules;
}
findArticles() {
return this._http.get<{ items: Article[] }>('/articles')
.then(response => response.data.items.map(item => subject('Article', item)));
}
findArticleById(id: string) {
return this._http.get<{ item: Article }>(`/articles/${id}`)
.then(response => subject('Article', response.data.item));
}
saveArticle({ id, ...article }: Partial<Pick<Article, 'title' | 'body' | 'id' | 'published'>>) {
const save = id
? this._http.patch<{ item: Article }>(`/articles/${id}`, article)
: this._http.post<{ item: Article }>('/articles', article);
return save.then(response => subject('Article', response.data.item));
}
deleteArticle(article: Article) {
return this._http.delete(`/articles/${article.id}`);
}
extract() {
if (!this.token) {
return [STORE_KEY, null] as const;
}
return [STORE_KEY, {
token: this.token,
user: this.user,
rules: this.rules,
}] as const;
}
@action
hydrateFrom(storage: Storage) {
const state = storage.getItem(STORE_KEY);
if (state) {
Object.assign(this, JSON.parse(state));
}
return this;
}
}