Skip to content

Commit 39030ba

Browse files
committed
Merge branch 'main' into home-reload
2 parents 2998b36 + 176d5f3 commit 39030ba

File tree

9 files changed

+154
-54
lines changed

9 files changed

+154
-54
lines changed

src/application/services/useAuthRequired.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useAppState } from './useAppState.ts';
22
import useAuth from './useAuth.ts';
33
import { useRouter } from 'vue-router';
4+
import { until } from '@vueuse/core';
45

56
/**
67
* Function that is used in App for checking user authorization
@@ -14,19 +15,25 @@ export default function useAuthRequired(): void {
1415

1516
/**
1617
* Check if user is authorized
17-
* If authorization is in process we treat user as unauthorized
1818
* When oauth will work, it will be treated as he authorized manually
1919
* @returns true if user is authorized, false otherwise
2020
*/
21-
function isUserAuthorized(): boolean {
22-
return (user.value !== null && user.value !== undefined);
21+
async function isUserAuthorized(): Promise<boolean> {
22+
/**
23+
* Wait until authorization process is finished
24+
*/
25+
await until(user).not.toBe(undefined);
26+
27+
return user.value !== null;
2328
}
2429

2530
/**
2631
* For each route check if auth is required
2732
*/
28-
router.beforeEach((actualRoute, _, next) => {
29-
if (actualRoute.meta.authRequired === true && !isUserAuthorized()) {
33+
router.beforeEach(async (actualRoute, _, next) => {
34+
const isAuthorized = await isUserAuthorized();
35+
36+
if (actualRoute.meta.authRequired === true && !isAuthorized) {
3037
/**
3138
* If auth is required and user is not autorized
3239
* Then show google auth popup and redirect user to auth page

src/infrastructure/auth.repository.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type AuthRepositoryInterface from '@/domain/auth.repository.interface';
22
import type NotesApiTransport from './transport/notes-api';
33
import type AuthStorage from './storage/auth';
44
import type AuthSession from '@/domain/entities/AuthSession';
5+
import { notEmpty } from './utils/empty';
56

67
/**
78
* Facade for the auth data
@@ -28,7 +29,9 @@ export default class AuthRepository implements AuthRepositoryInterface {
2829
* Specify whether we have auth session (refresh token)
2930
*/
3031
public hasSession(): boolean {
31-
return this.authStorage.getRefreshToken() !== null;
32+
const refreshToken = this.authStorage.getRefreshToken();
33+
34+
return notEmpty(refreshToken);
3235
}
3336

3437
/**

src/infrastructure/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,10 @@ export function init(noteApiUrl: string, eventBus: EventBus): Repositories {
7878
* Init storages
7979
*/
8080
const noteStore = new NoteStore();
81-
const authStore = new AuthStore();
8281
const userStore = new UserStore();
8382
const editorToolsStore = new EditorToolsStore();
8483
const openedPagesStore = new OpenedPagesStore();
84+
const authStore = new AuthStore();
8585

8686
/**
8787
* Init transport
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/* eslint-disable-next-line boundaries/element-types */
2+
import { SubscribableStore } from './subscribable';
3+
4+
export class PersistantStore<StoreData extends Record<string, unknown>> extends SubscribableStore<StoreData> {
5+
/**
6+
* Keys of the StoreData type
7+
* Used to separate the needed local storage information
8+
*/
9+
protected readonly keysStored: string[];
10+
11+
/**
12+
* Proxy for data stored
13+
*/
14+
protected data: StoreData;
15+
16+
/**
17+
* Storage that would retain information when proxy is cleared
18+
*/
19+
private storage = window.localStorage;
20+
21+
constructor(keysStored: string[]) {
22+
super();
23+
this.keysStored = keysStored;
24+
this.data = new Proxy<StoreData>({} as StoreData, this._data);
25+
26+
/**
27+
* Load data from local store to proxy
28+
*/
29+
this.loadInitialData();
30+
};
31+
32+
protected get _data(): ProxyHandler<StoreData> {
33+
return {
34+
get: (target, prop) => {
35+
const item = this.storage.getItem(prop as string);
36+
37+
return item !== null ? JSON.parse(item) : undefined;
38+
},
39+
40+
set: (target, prop, value, receiver) => {
41+
/**
42+
* Set new property value for proxy usage
43+
*/
44+
Reflect.set(target, prop, value, receiver);
45+
46+
/**
47+
* Set new property value for storage
48+
*/
49+
this.storage.setItem(prop as string, JSON.stringify(value));
50+
51+
this.onDataChange([{ prop: prop as keyof StoreData,
52+
newValue: value }]);
53+
54+
return true;
55+
},
56+
57+
deleteProperty: (target, prop) => {
58+
this.storage.removeItem(prop as string);
59+
60+
return Reflect.deleteProperty(target, prop);
61+
},
62+
};
63+
}
64+
65+
/**
66+
* Funciton for loading initial data from window.localStorage to proxy object
67+
* Data stored in localStorage would be normalized for proxy
68+
*/
69+
private loadInitialData(): void {
70+
for (let i = 0; i < this.storage.length; i++) {
71+
const key = this.storage.key(i);
72+
73+
if (key !== null && this.keysStored.includes(key)) {
74+
const storedValue = this.storage.getItem(key);
75+
76+
if (storedValue !== null) {
77+
this.data[key as keyof StoreData] = JSON.parse(storedValue);
78+
}
79+
}
80+
}
81+
}
82+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/* eslint-disable-next-line boundaries/element-types */
2+
import { SubscribableStore } from './subscribable';
3+
4+
export class SessionStore<StoreData extends Record<string, unknown>> extends SubscribableStore<StoreData> {
5+
/**
6+
* Proxy for data stored in store.
7+
* Used to watch data changes
8+
*/
9+
protected data = new Proxy<StoreData>({} as StoreData, this._data);
10+
11+
/**
12+
* Data proxy handler
13+
*/
14+
protected get _data(): ProxyHandler<StoreData> {
15+
return {
16+
set: (target, prop, value, receiver) => {
17+
Reflect.set(target, prop, value, receiver);
18+
19+
this.onDataChange([{ prop: prop as keyof StoreData,
20+
newValue: value }]);
21+
22+
return true;
23+
},
24+
get(target, prop, receiver) {
25+
return Reflect.get(target, prop, receiver);
26+
},
27+
28+
deleteProperty: (target, prop) => {
29+
return Reflect.deleteProperty(target, prop);
30+
},
31+
};
32+
}
33+
}

src/infrastructure/storage/abstract/subscribable.ts

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -24,40 +24,15 @@ type Change<StoreData> = {
2424
* Allows to subscribe to store data changes
2525
*/
2626
export abstract class SubscribableStore<StoreData extends Record<string, unknown>> {
27-
/**
28-
* Proxy for data stored in store.
29-
* Used to watch data changes
30-
*/
31-
protected data = new Proxy<StoreData>({} as StoreData, this._data);
32-
3327
/**
3428
* List of subscribers on data change
3529
*/
36-
private subscribers: PropChangeCallback<StoreData>[] = [];
30+
protected subscribers: PropChangeCallback<StoreData>[] = [];
3731

3832
/**
3933
* Using for accumulation of changes in store until subscriber appearse
4034
*/
41-
private stashedChanges: Change<StoreData>[] = [];
42-
43-
/**
44-
* Data proxy handler
45-
*/
46-
protected get _data(): ProxyHandler<StoreData> {
47-
return {
48-
set: (target, prop, value, receiver) => {
49-
Reflect.set(target, prop, value, receiver);
50-
51-
this.onDataChange([{ prop: prop as keyof StoreData,
52-
newValue: value }]);
53-
54-
return true;
55-
},
56-
get(target, prop, receiver) {
57-
return Reflect.get(target, prop, receiver);
58-
},
59-
};
60-
}
35+
protected stashedChanges: Change<StoreData>[] = [];
6136

6237
/**
6338
* Subscribe to store changes
@@ -86,7 +61,7 @@ export abstract class SubscribableStore<StoreData extends Record<string, unknown
8661
* Notifies subscribers about data change.
8762
* @param changes - array of changes
8863
*/
89-
private onDataChange(changes: Change<StoreData>[]): void {
64+
protected onDataChange(changes: Change<StoreData>[]): void {
9065
/**
9166
* If there are no subscribers stash current change
9267
*/

src/infrastructure/storage/auth.ts

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,39 @@
1-
/**
2-
* Stores auth data in local storage
3-
*/
4-
export default class AuthStorage {
5-
/**
6-
* Storage implementation
7-
*/
8-
private readonly storage: Storage;
1+
import { PersistantStore } from './abstract/persistant';
92

3+
export type AuthStoreData = {
104
/**
11-
* Creates storage instance
5+
* If user is authorized refresh token will be stored
126
*/
7+
refreshToken?: string;
8+
};
9+
10+
/**
11+
* Stores auth data in local storage
12+
*/
13+
export default class AuthStorage extends PersistantStore<AuthStoreData> {
1314
constructor() {
14-
this.storage = window.localStorage;
15+
super(['refreshToken']);
1516
}
1617

1718
/**
1819
* Returns refresh token
1920
*/
2021
public getRefreshToken(): string | null {
21-
return this.storage.getItem('refreshToken');
22+
return this.data.refreshToken === undefined ? null : this.data.refreshToken;
2223
}
2324

2425
/**
2526
* Save refresh token
2627
* @param refreshToken - refresh token to save
2728
*/
2829
public setRefreshToken(refreshToken: string): void {
29-
this.storage.setItem('refreshToken', refreshToken);
30+
this.data.refreshToken = refreshToken;
3031
}
3132

3233
/**
3334
* Removes refresh token
3435
*/
3536
public removeRefreshToken(): void {
36-
this.storage.removeItem('refreshToken');
37+
delete this.data.refreshToken;
3738
}
3839
}

src/infrastructure/storage/openedPage.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { OpenedPage } from '@/domain/entities/OpenedPage';
2-
import { SubscribableStore } from './abstract/subscribable';
2+
import { PersistantStore } from './abstract/persistant';
33

44
export type OpenedPagesStoreData = {
55
/**
@@ -11,10 +11,9 @@ export type OpenedPagesStoreData = {
1111
/**
1212
* Class to store all pages that are currently opened in workspace
1313
*/
14-
export class OpenedPagesStore extends SubscribableStore<OpenedPagesStoreData> {
14+
export class OpenedPagesStore extends PersistantStore<OpenedPagesStoreData> {
1515
constructor() {
16-
super();
17-
this.data.openedPages = [];
16+
super(['openedPages']);
1817
}
1918

2019
/**

src/infrastructure/storage/user.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { User } from '@/domain/entities/User';
2-
import { SubscribableStore } from './abstract/subscribable';
2+
import { SessionStore } from './abstract/session';
33
import type EditorTool from '@/domain/entities/EditorTool';
44

55
/**
@@ -20,7 +20,7 @@ export type UserStoreData = {
2020
/**
2121
* Store for the user data
2222
*/
23-
export class UserStore extends SubscribableStore<UserStoreData> {
23+
export class UserStore extends SessionStore<UserStoreData> {
2424
/**
2525
* Returns user data
2626
*/

0 commit comments

Comments
 (0)